【问题标题】:Problems with Spring Java Config and @EnableTransactionManagementSpring Java Config 和 @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})

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

【问题讨论】:

    标签: java spring


    【解决方案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。谢谢
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-09-29
    • 2017-04-05
    • 1970-01-01
    • 1970-01-01
    • 2020-09-05
    • 1970-01-01
    • 2019-01-02
    相关资源
    最近更新 更多