Spring Java Config 和 @EnableTransactionManagement 的问题

Posted

技术标签:

【中文标题】Spring Java Config 和 @EnableTransactionManagement 的问题【英文标题】:Problems with Spring Java Config and @EnableTransactionManagement 【发布时间】:2014-07-05 20:32:39 【问题描述】:

我正在从 XML 配置迁移到 Spring 上下文配置。相反,当我尝试在 Spring 4.0.3.RELEASE Java 配置上使用功能等效的 @EnableTransactionManagement 时,我的 Spring 上下文无法实例化并出现以下异常:

java.lang.IllegalStateException:无法加载 ApplicationContext 在 org.springframework.test.context.CacheAwareContextLoaderDelegate.loadContext(CacheAwareContextLoaderDelegate.java:99) 在 org.springframework.test.context.DefaultTestContext.getApplicationContext(DefaultTestContext.java:101) 在 org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:109) 在 org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:75) 在 org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:319) 在 org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:212) 在 org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:289) 在 org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) 在 org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:291) 在 org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:232) 在 org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:89) 在 org.junit.runners.ParentRunner$3.run(ParentRunner.java:238) 在 org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63) 在 org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236) 在 org.junit.runners.ParentRunner.access$000(ParentRunner.java:53) 在 org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229) 在 org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61) 在 org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:71) 在 org.junit.runners.ParentRunner.run(ParentRunner.java:309) 在 org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:175) 在 org.apache.maven.surefire.junit4.JUnit4Provider.execute(JUnit4Provider.java:236) 在 org.apache.maven.surefire.junit4.JUnit4Provider.executeTestSet(JUnit4Provider.java:134) 在 org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:113) 在 sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 在 sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) 在 sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) 在 java.lang.reflect.Method.invoke(Method.java:597) 在 org.apache.maven.surefire.util.ReflectionUtils.invokeMethodWithArray(ReflectionUtils.java:189) 在 org.apache.maven.surefire.booter.ProviderFactory$ProviderProxy.invoke(ProviderFactory.java:165) 在 org.apache.maven.surefire.booter.ProviderFactory.invokeProvider(ProviderFactory.java:85) 在 org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:103) 在 org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:74) 原因:org.springframework.beans.factory.BeanCreationException:创建名为“basemysqlTest.TestConfig”的bean时出错:bean初始化失败;嵌套异常是 org.springframework.beans.factory.BeanCreationException:在类路径资源 [org/springframework/transaction/annotation/ProxyTransactionManagementConfiguration.class] 中定义名称为 'org.springframework.transaction.config.internalTransactionAdvisor' 的创建 bean 时出错:实例化豆失败;嵌套异常是 org.springframework.beans.factory.BeanDefinitionStoreException: Factory method [public org.springframework.transaction.intercep...skipping... 在 org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1558) 在 org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:539) ... 45 更多 引起:org.springframework.beans.factory.BeanDefinitionStoreException:工厂方法[public org.springframework.transaction.interceptor.BeanFactoryTransactionAttributeSourceAdvisor org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration.transactionAdvisor()]抛出异常;嵌套异常是 org.springframework.beans.factory.BeanCreationException:在类路径资源 [org/springframework/transaction/annotation/ProxyTransactionManagementConfiguration.class] 中定义名称为“transactionInterceptor”的 bean 创建错误:调用 init 方法失败;嵌套异常是 java.lang.IllegalArgumentException:需要属性“transactionManager” 在 org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:188) 在 org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:586) ... 62 更多 原因:org.springframework.beans.factory.BeanCreationException:在类路径资源[org/springframework/transaction/annotation/ProxyTransactionManagementConfiguration.class]中定义名称为“transactionInterceptor”的bean创建错误:调用init方法失败;嵌套异常是 java.lang.IllegalArgumentException:需要属性“transactionManager” 在 org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1553) 在 org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:539) 在 org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:475) 在 org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:304) 在 org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228) 在 org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:300) 在 org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:195) 在 org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:324) 在 org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration$$EnhancerBySpringCGLIB$$83a12634.transactionInterceptor() 在 org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration.transactionAdvisor(ProxyTransactionManagementConfiguration.java:45) 在 org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration$$EnhancerBySpringCGLIB$$83a12634.CGLIB$transactionAdvisor$0() 在 org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration$$EnhancerBySpringCGLIB$$83a12634$$FastClassBySpringCGLIB$$$cc829ae.invoke() 在 org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:228) 在 org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:312) 在 org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration$$EnhancerBySpringCGLIB$$83a12634.transactionAdvisor() 在 sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 在 sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) 在 sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) 在 java.lang.reflect.Method.invoke(Method.java:597) 在 org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:166) ... 63 更多 原因:java.lang.IllegalArgumentException:需要属性“transactionManager” 在 org.springframework.transaction.interceptor.TransactionAspectSupport.afterPropertiesSet(TransactionAspectSupport.java:195) 在 org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1612) 在 org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1549) ... 82 更多

这恰好在单元测试中获得,但是当它在这里工作时,我可以在生产代码中使用它。

这是我发生 Spring 接线的单元测试基类:

@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = PropertyPlaceholderConfigurer.class, BaseMySQLTest.TestConfig.class) 公共类 BaseMySQLTest 扩展 AbstractTransactionalJUnit4SpringContextTests @配置 @Import(DaoConfig.class) @PropertySource("类路径:/jdbc.properties") @EnableTransactionManagement 公共静态类 TestConfig @豆角,扁豆 公共 PlatformTransactionManager 提供TransactionManager(ListableBeanFactory beanFactory) 返回新的 DataSourceTransactionManager(beanFactory.getBean(DataSource.class));

这是一个使用这个基类和配置的子类:

公共类 UserDaoImplTest 扩展 BaseMySQLTest @自动连线 私人用户道用户道; @测试 公共无效 testById() 抛出异常 jdbcTemplate.execute("插入用户(电子邮件)值 ('bob@example.com')"); ...

可以看到,我的 Spring TestConfig 定义了一个事务管理器 bean,它根据 Javadoc

http://docs.spring.io/spring/docs/3.1.x/javadoc-api/org/springframework/transaction/annotation/EnableTransactionManagement.html

意味着我不必为 bean 命名(尽管我已经命名它是为了让它工作)。事实上,面对没有明确命名为“transactionManager”的 bean,Spring 上下文的行为就像使用 XML 配置进行配置一样。

我的 Java Config 缺少什么以使 Spring 上下文无法使用此事务管理器 bean 在实例化时满足其要求?

感谢您提供任何有用的意见。

编辑:

(我不确定这个编辑应该去哪里,所以我在这里尝试。ae6rt)

这是新的测试类,结果和原作一样的错误:

@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = BaseMySQLTest.TestConfig.class) 公共类 BaseMySQLTest 扩展 AbstractTransactionalJUnit4SpringContextTests 受保护的 int lastInsertId() return jdbcTemplate.queryForInt("select LAST_INSERT_ID()"); @自动连线 私人用户道用户道; @测试 公共无效 testById() 抛出异常 jdbcTemplate.execute("插入 mgdb.users (email) 值 ('bob@example.com')"); int userId = lastInsertId(); 可选 xoomUserOptional = userDao.byId(userId); assertThat(xoomUserOptional.isPresent(), equalTo(true)); XoomUser 用户 = xoomUserOptional.get(); assertThat(user.getEmailAddress(), equalTo("bob@example.com")); @配置 @Import(DaoConfig.class) @PropertySource("类路径:/jdbc.properties") @EnableTransactionManagement 公共静态类 TestConfig @豆角,扁豆 公共 PlatformTransactionManager 提供TransactionManager(ListableBeanFactory beanFactory) 返回新的 DataSourceTransactionManager(beanFactory.getBean(DataSource.class));

我实际上并不需要这里的属性配置

@ContextConfiguration(classes = BaseMySQLTest.TestConfig.class)

所以我删除了它。希望这符合这一轮的精神。

【问题讨论】:

【参考方案1】:

看起来唯一的变化应该是将您的事务管理器 bean 名称命名为“transactionManager”:

public static class TestConfig 
    @Autowired
    private DataSource datasource;

    @Bean
    public PlatformTransactionManager transactionManager() 
        return new DataSourceTransactionManager(datasource);
    

编辑

你能试试这些额外的东西吗:

.1。从这里删除PropertyPlaceHolderConfigurer..@ContextConfiguration(classes = PropertyPlaceholderConfigurer.class, BaseMySQLTest.TestConfig.class),这不是PropertyPlaceholderConfigurer的使用方式,你必须这样做:

@Bean
public static PropertySourcesPlaceholderConfigurer placeholderConfigurer() 
    PropertySourcesPlaceholderConfigurer placeholderConfigurer = new PropertySourcesPlaceholderConfigurer();
    ClassPathResource resource = new ClassPathResource("/META-INF/spring/database.properties");
    placeholderConfigurer.setLocation(resource);
    return placeholderConfigurer;

另外,为了测试,您能否将您的实际测试也移动到基类中,看看其中一种配置是否适合您。我在自己的机器上进行了测试,它似乎与 Spring 4.0.2+ 配合得很好。

【讨论】:

感谢您的建议。我试过了,不幸的是,这并没有改变异常。我什至为 DataSource 采用了您的 Autowired 表单,认为 Spring 可能看不到我的单参数 ListableBeanFactory 表单用于事务管理器方法。使用 Autowired 表单也无济于事。 对不起,是的。您可以尝试一些我要添加的东西作为我的答案的编辑。 我根据您的建议对原始帖子进行了编辑。非常感谢。 我在 GitHub 上创建了一个测试项目来重现我原来的问题。无论我做错了什么,我都会一直这样做,因为更简单的测试项目显示相同的异常。见github.com/ae6rt/spring-tx-java-config。谢谢

以上是关于Spring Java Config 和 @EnableTransactionManagement 的问题的主要内容,如果未能解决你的问题,请参考以下文章

具有 Spring Security 和 Java Config 的自定义身份验证提供程序

Spring Security Java Config - 自定义 AuthenticationProvider 和 UserDetailsS​​ervice

Spring Security Java Config Preview--官方

我在从 Spring XML 迁移到 Java-config 时不知所措

如何使用 Spring-Security 3 和 Hibernate 4 将 spring security xml 配置 hibernate 转换为 java config

spring mvc java config怎么配置session-timeout