使用@Transactional时,注入自动连接的依赖项失败

时间:2012-03-22 02:49:21

标签: spring hibernate java-ee orm annotations

我测试了我的DAO,但它没有用。发生以下错误:

Tests in error: 
  testAccountOperations(com.tsekhan.rssreader.dao.HibernateControllerTest): Error creating bean with name 'com.tsekhan.rssreader.dao.HibernateControllerTest': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: com.tsekhan.rssreader.dao.HibernateController com.tsekhan.rssreader.dao.HibernateControllerTest.hibernateController; nested exception is java.lang.IllegalArgumentException: Can not set com.tsekhan.rssreader.dao.HibernateController field com.tsekhan.rssreader.dao.HibernateControllerTest.hibernateController to $Proxy25

我的道:

@Service
@Scope("singleton")
public class HibernateController extends HibernateDaoSupport {

    @Autowired
    public SessionFactory sessionFactory;

    @Transactional
    public void addAcount(Account account) {
        sessionFactory.getCurrentSession().saveOrUpdate(account);
    }
}

我对这个DAO的测试:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:/applicationContext.xml")
public class HibernateControllerTest {

    @Autowired
    HibernateController hibernateController;

    private Set<Channel> getTestChannelList(String channelLink) {
        Channel testChannel = new Channel();
        testChannel.setSourceLink(channelLink);
        Set<Channel> testChannelList = new HashSet<Channel>();
        testChannelList.add(testChannel);
        return testChannelList;
    }

    private Account getTestAccount(String accountLogin, String channelLink) {
        Account testAccount = new Account();
        testAccount.setAccountLogin(accountLogin);
        testAccount.setChannelList(getTestChannelList(channelLink));
        return testAccount;
    }

    @Test
    public void testAccountOperations() {
        hibernateController
                .addAcount(getTestAccount("test_login", "test_link"));
    }
}

我的 applicationContext.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:context="http://www.springframework.org/schema/context"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-3.1.xsd"
        default-autowire="byName">

    <!-- Enabling spring-transaction annotations -->
    <tx:annotation-driven transaction-manager="transactionManager"/>

    <!-- Enabling annotation-driven configurating -->
    <context:annotation-config />

    <!-- Creation of transaction manager -->

    <bean id="transactionManager" scope="singleton" 
        class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory"/>
    </bean>

    <bean id="sessionFactory" scope="singleton"
        class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
        <property name="configLocation" value="classpath:/hibernate.cfg.xml"/>
        <property name="configurationClass">
            <value>org.hibernate.cfg.AnnotationConfiguration</value>
        </property>
    </bean>
    <!--
    A Spring interceptor that takes care of Hibernate session lifecycle.
    -->
    <bean id="hibernateInterceptor" 
            class="org.springframework.orm.hibernate3.HibernateInterceptor">
        <property name="sessionFactory">
            <ref bean="sessionFactory"/>
        </property>
    </bean>

    <bean name="employeeDAO" scope="prototype" 
        class="com.tsekhan.rssreader.dao.HibernateController" />

    <!-- Searching for hibernate POJO files in package com.tsekhan.rssreader.web -->
    <context:component-scan base-package="com.tsekhan.rssreader.web" />
    <bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping" />
    <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter" />

</beans>

我注意到,如果您在DAO中评论@Transactional,则会正确创建bean。会发生什么?

2 个答案:

答案 0 :(得分:22)

首先,将控制器中的名称命名为DAO非常令人困惑,控制器和DAO具有不同的用途。

当你将@Transactional添加到服务或dao类时,为了使它在事务中工作需要创建该类的代理,它是一种包装器,在执行代理类之前(考虑到代理的类)方法spring启动事务,并且在执行之后如果没有异常完成事务,这可以在Spring中通过AOP和Annotations完成。用代码描述。

public class OriginalDaoImpl implements OriginalDao extends DaoSupport {

  public void save(Object o){
      manager.save(o);
  }
}

public class ProxyDaoImpl implements OriginalDao {

    private OriginalDao originalDaoImpl; //instance of OriginalDaoImpl
    public void save(Object o){
       try{
            transaction.start();
            originalDaoImpl.save(o);
            transaction.commit(); 
       }catch(Exception e){
            transaction.rollback();
       }finally{
            //clean up code
       }
    }
}

如您所见,这不是一个确切的实现,而是基础代码,交易如何神奇地为您服务。关键点在于接口OriginalDao使得这种注入变得容易,因为OriginalDaoImpl和ProxyDaoImpl都实现了相同的接口。因此,它们可以被交换,即代理取代原件。可以通过Java动态代理在Java中创建此动态代理。现在,问题是如果你的类没有实现一个接口,更换就会变得更难。 据我所知,其中一个库CGLIB在这种情况下有所帮助,它为所考虑的类生成一个动态子类,并且在override方法中通过调用super.save(o)委托原始语法执行魔术。代码。

现在注射的问题。

  1. 创建界面并使你的dao实现,并且spring将默认为JDK代理,因为它现在正在运行。
  2. proxy-target-class="true"属性添加到<tx:annotation-driven transaction-manager="transactionManager"/>
  3. 就异常而言,它正在抛出,因为它期望注入的bean是'HibernateController'类型但不是。

    如需参考,请参阅以下链接。

    1. 10.5.6 Using @Transactional
    2. Spring AOP Doc
    3. 希望这有助于!!!!!。

答案 1 :(得分:0)

如果您正在使用Spring MVC,请确保仅在servlet context file中扫描特定的控制器类。否则,它将扫描2次,并且在应用程序上下文中无法进行事务处理。