Spring:无法将@Transactioanl注释从DAO移动到Service层

时间:2013-10-24 19:25:45

标签: spring hibernate spring-mvc 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

这是我的代码:

CatalogDAOInterface:

public interface CatalogDAOInterface {

    public List<Product> getAllProduct();

    public List<Category> getAllCategories() ;

    public List<Media> getAllMedias();

}

的CatalogDAO:

@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;
     }   

}

CatalogServiceInterface:

public interface CatalogServiceInterface {

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

}

CatalogService的:

@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();
    }

}

3 个答案:

答案 0 :(得分:6)

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

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

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

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

故事的寓意:如果你引用你的实现,总是使用界面(如果你决定提供不同的实现(例如CatalogService2也实现CatalogServiceInterface,你只能插入进入你的CatalogAPIController如果它的构造函数使用的是接口而不是类(基本上这就是指定@Transactional时发生的情况。

希望这是有道理的。

答案 1 :(得分:0)

两件事引起了我的注意

1

@Transactinal

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

2

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

而不是

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

将其更改为

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

同样适用于dao接口。

我不明白将交易从一个地方转移到另一个地方之间的关系。看看是否有帮助

答案 2 :(得分:0)

这是我能提到的细节。我相信你的类路径中没有CGLIB个库。因此,对于Spring应用@Transactional行为,它使用JDK代理代理带注释的类。 JDK代理使用接口,它们不能代理基类。因此,代理的直接超类是java.lang.reflect.Proxy

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

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

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

当您注释DAO时没有发生这种情况的原因,我相信您的DAO类不在应用component-scan行为的@Transactional包中。所以它可能看起来好像@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" />