【问题标题】:Spring: Can't move @Transactioanl annotation from DAO to Service layerSpring:无法将 @Transactionanl 注释从 DAO 移动到服务层
【发布时间】:2013-11-03 16:26:25
【问题描述】:

Transactional 注释在它工作的 DAO 层中,如果我将它移动到服务层,我会得到异常:

Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [com.adam.czibere.RestAPIController]: Illegal arguments for constructor; nested exception is java.lang.IllegalArgumentException: argument type mismatch

这是我的代码:

目录DAO接口:

public interface CatalogDAOInterface {

    public List<Product> getAllProduct();

    public List<Category> getAllCategories() ;

    public List<Media> getAllMedias();

}

目录DAO:

@Repository
@SuppressWarnings({"unchecked", "rawtypes"})
public class CatalogDAO implements CatalogDAOInterface {

     @Autowired private SessionFactory sessionFactory;


     @Override
     public List<Product> getAllProduct() {
             Session session = sessionFactory.getCurrentSession();
             List products = session.createQuery("from Product").list();
             return products;
     }

     @Override
     public List<Category> getAllCategories() {
             Session session = sessionFactory.getCurrentSession();
             List products = session.createQuery("from Category").list();
             return products;
     }

     @Override
     public List<Media> getAllMedias() {
             Session session = sessionFactory.getCurrentSession();
             List medias = session.createQuery("from Media").list();
             return medias;
     }   

}

目录服务接口:

public interface CatalogServiceInterface {

    public List<Category> getAllCategories();
    public List<Product> getAllProducts();
    public List<Media> getAllMedias();

}

目录服务:

@Service
public class CatalogService implements CatalogServiceInterface{

    @Autowired
    private CatalogDAO catalogDAO;

        @Transactinal
    @Override
    public List<Product> getAllProducts() {
        return catalogDAO.getAllProduct();
    }

        @Transactinal
    @Override
    public List<Category> getAllCategories() {
        return catalogDAO.getAllCategories();
    }

        @Transactinal
    @Override
    public List<Media> getAllMedias() {
        return catalogDAO.getAllMedias();
    }

}

servlet-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:beans="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd">

    <!-- Enable @Controller annotation support -->
    <mvc:annotation-driven />

    <!-- Handles HTTP GET requests for /resources/** by efficiently serving 
        up static resources in the ${webappRoot}/resources directory -->
    <mvc:resources mapping="/resources/**" location="/resources/" />

    <!-- Resolves views selected for rendering by @Controllers to .jsp resources 
        in the /WEB-INF/views directory -->
    <bean
        class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/views/" />
        <property name="suffix" value=".jsp" />
    </bean>

    <context:component-scan base-package="com.adam.czibere" />

<!--    <bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" -->
<!--        destroy-method="close"> -->
<!--        <property name="driverClassName" value="com.mysql.jdbc.Driver" /> -->
<!--        <property name="url" value="jdbc:mysql://localhost:3306/pizzashop" /> -->
<!--        <property name="username" value="root" /> -->
<!--        <property name="password" value="czadam" /> -->
<!--        <property name="validationQuery" value="SELECT 1" /> -->
<!--    </bean> -->

    <bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource"
        destroy-method="close">
        <property name="driverClassName" value="net.sourceforge.jtds.jdbc.Driver" />
        <property name="url" value="jdbc:jtds:sqlserver://something" />
        <property name="username" value="something" />
        <property name="password" value="something" />
        <property name="validationQuery" value="SELECT 1" />
    </bean>

    <!-- Hibernate Session Factory -->
    <bean id="mySessionFactory"
        class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
        <property name="dataSource" ref="myDataSource" />
        <property name="packagesToScan">
            <array>
                <value>com.adam.czibere</value>
            </array>
        </property>
        <property name="hibernateProperties">
            <value>
                hibernate.dialect=org.hibernate.dialect.MySQLDialect
            </value>
        </property>
    </bean>

    <!-- Hibernate Transaction Manager -->
    <bean id="transactionManager"
        class="org.springframework.orm.hibernate4.HibernateTransactionManager">
        <property name="sessionFactory" ref="mySessionFactory" />
    </bean>

    <!-- Activates annotation based transaction management -->
    <tx:annotation-driven transaction-manager="transactionManager" />

</beans>

堆栈跟踪:

exception 

javax.servlet.ServletException: Servlet.init() for servlet appServlet threw exception
    org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
    org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:99)
    org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:936)
    org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:407)
    org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1004)
    org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:589)
    org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:312)
    java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    java.lang.Thread.run(Thread.java:724)


root cause 

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'catalogAPIController' defined in file [C:\Users\czadam\Documents\workspace 2.0\.metadata\.plugins\org.eclipse.wst.server.core\tmp1\wtpwebapps\SalesWizard\WEB-INF\classes\com\adam\czibere\CatalogAPIController.class]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [com.adam.czibere.CatalogAPIController]: Illegal arguments for constructor; nested exception is java.lang.IllegalArgumentException: argument type mismatch
    org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:288)
    org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1035)
    org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:939)
    org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:485)
    org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:456)
    org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:294)
    org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:225)
    org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:291)
    org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:193)
    org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:585)
    org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:913)
    org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:464)
    org.springframework.web.servlet.FrameworkServlet.configureAndRefreshWebApplicationContext(FrameworkServlet.java:631)
    org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:588)
    org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:645)
    org.springframework.web.servlet.FrameworkServlet.initWebApplicationContext(FrameworkServlet.java:508)
    org.springframework.web.servlet.FrameworkServlet.initServletBean(FrameworkServlet.java:449)
    org.springframework.web.servlet.HttpServletBean.init(HttpServletBean.java:133)
    javax.servlet.GenericServlet.init(GenericServlet.java:160)
    org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
    org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:99)
    org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:936)
    org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:407)
    org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1004)
    org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:589)
    org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:312)
    java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    java.lang.Thread.run(Thread.java:724)


root cause 

org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [com.adam.czibere.CatalogAPIController]: Illegal arguments for constructor; nested exception is java.lang.IllegalArgumentException: argument type mismatch
    org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:158)
    org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:110)
    org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:280)
    org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1035)
    org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:939)
    org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:485)
    org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:456)
    org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:294)
    org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:225)
    org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:291)
    org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:193)
    org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:585)
    org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:913)
    org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:464)
    org.springframework.web.servlet.FrameworkServlet.configureAndRefreshWebApplicationContext(FrameworkServlet.java:631)
    org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:588)
    org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:645)
    org.springframework.web.servlet.FrameworkServlet.initWebApplicationContext(FrameworkServlet.java:508)
    org.springframework.web.servlet.FrameworkServlet.initServletBean(FrameworkServlet.java:449)
    org.springframework.web.servlet.HttpServletBean.init(HttpServletBean.java:133)
    javax.servlet.GenericServlet.init(GenericServlet.java:160)
    org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
    org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:99)
    org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:936)
    org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:407)
    org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1004)
    org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:589)
    org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:312)
    java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    java.lang.Thread.run(Thread.java:724)


root cause 

java.lang.IllegalArgumentException: argument type mismatch
    sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
    sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    java.lang.reflect.Constructor.newInstance(Constructor.java:526)
    org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:147)
    org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:110)
    org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:280)
    org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1035)
    org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:939)
    org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:485)
    org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:456)
    org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:294)
    org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:225)
    org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:291)
    org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:193)
    org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:585)
    org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:913)
    org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:464)
    org.springframework.web.servlet.FrameworkServlet.configureAndRefreshWebApplicationContext(FrameworkServlet.java:631)
    org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:588)
    org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:645)
    org.springframework.web.servlet.FrameworkServlet.initWebApplicationContext(FrameworkServlet.java:508)
    org.springframework.web.servlet.FrameworkServlet.initServletBean(FrameworkServlet.java:449)
    org.springframework.web.servlet.HttpServletBean.init(HttpServletBean.java:133)
    javax.servlet.GenericServlet.init(GenericServlet.java:160)
    org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
    org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:99)
    org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:936)
    org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:407)
    org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1004)
    org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:589)
    org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:312)
    java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    java.lang.Thread.run(Thread.java:724)

我的控制器的一部分:

@Controller
@RequestMapping(value = "api", produces = "application/json")
public class CatalogAPIController {

    CatalogService catalogService;

    private static final int BUFFER_SIZE = 4096;

    @Autowired
    public CatalogAPIController(CatalogService catalogService) {
        this.catalogService = catalogService;
    }

    // get all categories
    @RequestMapping(value = "category/all", produces = "application/json;charset=UTF-8")
    @ResponseBody
    public String getAllCategories() {
        JSONArray categoryArray = new JSONArray();
        for (Category cat : catalogService.getAllCategories()) {
            JSONObject categoryJSON = new JSONObject();
            try {
                categoryJSON.put("id", cat.getId());
                categoryJSON.put("name", cat.getName());

                categoryJSON.put("imageMediaID", cat.getImageMediaID());
                categoryJSON.put("parentID", cat.getParent().getId());

                // Media
                JSONArray mediaArray = new JSONArray();
                for (Media item : cat.getMedias()) {
                    if (item != null) {

                        mediaArray.put(item.getId());
                    }
                }
                categoryJSON.put("mediaIDs", mediaArray);

                categoryJSON.put("modifiedDate", cat.getModifiedDate());

                categoryArray.put(categoryJSON);
            } catch (JSONException e) {

                e.printStackTrace();
                return "Error: " + e.getMessage();
            }

        }
        return categoryArray.toString();
    }

}

【问题讨论】:

  • 请发布完整的异常堆栈跟踪和您的RestAPIController 课程。
  • 当您将@Transactional 注解添加到您的服务类时,Spring 将向您的RestAPIController 注入一个代理,而不是一个真实服务的实例。这可能导致IllegalArgumentException,但正如 Sotirios 所说,发布堆栈跟踪和控制器代码,以便我们可以获得更多信息。
  • 我发布了堆栈跟踪和 teh 控制器。

标签: spring hibernate spring-mvc transactional


【解决方案1】:

当 Spring 在内部应用 @Transactional 注释时,它会创建一个代理类来包装您的服务类。

因此,当您在应用程序上下文中创建服务 bean 时,您将获得一个类型不是 CatalogService 而是一些 Proxy$1 类的对象。

这个Proxy$1 类不会扩展CatalogService,但它将实现CatalogServiceInterface。因此,当您的 CatalogAPIController bean 被创建时,它会尝试使用您的 Proxy$1 对象调用其构造函数,这是错误的类型,因为您的构造函数需要一个类。

因此,如果您将控制器更改为使用接口而不是实现,那么我相信您的问题将会消失。这是因为Proxy$1 确实实现了CatalogServiceInterface

故事的寓意:如果您引用您的实现,请始终使用该接口(因为如果您决定提供不同的实现(例如,CatalogService2 也实现了CatalogServiceInterface,您只能将其插入您的@987654333 @ 如果它的构造函数使用的是接口而不是类(本质上这就是你指定 @Transactional 时发生的事情。

希望这是有道理的。

【讨论】:

    【解决方案2】:

    有两件事引起了我的注意

    1.

    @Transactinal
    

    是拼写错误还是要导入另一个注解,应该是@Transactional

    2.

    如果你已经定义了接口,为什么不使用它们?

    而不是

    @Autowired
    public CatalogAPIController(CatalogService catalogService) {
        this.catalogService = catalogService;
    }
    

    改成

    @Autowired
    public CatalogAPIController(CatalogServiceInterface catalogService) {
        this.catalogService = catalogService;
    }
    

    同样适用于 dao 接口。

    我不明白将事务从一个地方转移到另一个地方之间的关系。看看有没有帮助

    【讨论】:

      【解决方案3】:

      根据您提供的详细信息,这是我能想到的。我相信您的类路径中没有 CGLIB 库。因此,为了让 Spring 应用 @Transactional 行为,它使用 JDK 代理代理您的注释类。 JDK 代理使用接口,它们不能代理基类。因此,代理的直接超类是java.lang.reflect.Proxy

      在这种情况下,您的CatalogService bean 将被包装在一个代理中,其类类似于Proxy$1。当 Spring 尝试使用构造函数通过反射来实例化您的 @Controller 类时,CatalogAPIController

      @Autowired
      public CatalogAPIController(CatalogService catalogService) {
          this.catalogService = catalogService;
      }
      

      它将失败,因为传递给Constructor#newInstance(Object...) 方法的参数将不匹配参数类型CatalogService,因为Proxy$1 不是CatalogService 的子类型。

      当您注释 DAO 时没有发生这种情况的原因,我相信您的 DAO 类不在应用 @Transactional 行为的 component-scan 包上。所以看起来好像@Transactional 正在工作,但事实并非如此。因为@Transactional 不起作用,所以没有创建代理,因此 Spring 没有抱怨注入这个字段

      @Autowired
      private CatalogDAO catalogDAO;
      

      在您的服务类中。

      一种可能的解决方案是按照 Luis 的建议,将参数类型更改为接口作为 JDK 代理来实现接口,即。这些接口是为包装您的 bean 而生成的 Proxy$1 类实例的超类型。

      另一种解决方案是在您的类路径中提供CGLIB jar。 Spring 会检测到它们并使用它们。然后它将能够通过基类而不是接口来代理您的类。 You can find the libraries here.

      别忘了告诉 Spring 代理目标类

      <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true" />
      

      【讨论】:

        猜你喜欢
        • 2011-05-16
        • 2013-05-16
        • 2013-10-17
        • 2012-01-25
        • 1970-01-01
        • 1970-01-01
        • 2012-03-22
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多