一尘不染

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

hibernate

我测试了我的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

我的DAO:

@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。怎么了?


阅读 350

收藏
2020-06-20

共1个答案

一尘不染

首先,将以Controller结尾的名称给DAO确实很令人困惑,Controller和DAO共同具有不同的目的。

当您添加@Transactional到服务或dao类时,为了使Spring在事务中起作用,需要创建该类的代理,它是一种包装,在执行代理类(考虑类的方法)之前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库之一在这种情况下会有所帮助,在这种情况下,它会为所考虑的类生成一个动态子类,并且在覆盖方法中,它通过调用super.save(o)
委托给原始代码来执行上述操作。

现在开始注射问题。

  1. 创建接口并让您的dao实现该功能,并且spring将默认使用JDK代理,因为它现在正在运行。
  2. proxy-target-class="true"属性添加到<tx:annotation-driven transaction-manager="transactionManager"/>

就异常而言,它正在抛出,因为它期望注入的bean是’HibernateController’类型的,但不是。

供您参考,您可以参考下面的链接。

  1. 10.5.6使用@Transactional
  2. springAOP文件

希望这可以帮助 !!!!!。

2020-06-20