• more than one ‘primary‘ bean found among candidates: xxxTransactionManager


    背景

    详细内容可看我的上一篇文章 springboot 使用多数据源 + 多事务管理器
    当我配置多个事务管理器,并且在某个serviceImpl类的方法上使用 @Transactional() 注解的时候,出现了文章标题的报错。


    报警内容

    org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 
    'org.springframework.transaction.TransactionManager' available: 
    more than one 'primary' bean found among candidates: [xxxxTransactionManager, xxxxTransactionManager, transactionManager]
    
    • 1
    • 2
    • 3

    思考过程

    首先,报错意思很明显,spring 发现有多个 primary bean, 这些bean都是事务管理器的bean。 但是我自己其实只注册了两个 事务管理器,
    且 我已经标注了其中一个作为我的 @primary bean, 那么为何报错最后多出了一个 transactionManager
    多半是基础框架里面设置了基础的事务管理器,那我就得从日志去看下


    日志验证

    上面找到了大概的方向,这次我再用启动日志去观察下 是否真的如此、
    在idea启动类配置上加上 debug=true;logging.level.root=debug
    然后启动服务,观察日志输出

     DataSourceTransactionManagerAutoConfiguration.DataSourceTransactionManagerConfiguration#transactionManager:
          Did not match:
             - @ConditionalOnMissingBean (types: org.springframework.transaction.PlatformTransactionManager; SearchStrategy: all) found beans of type 'org.springframework.transaction.PlatformTransactionManager' 
             - bigdataTransactionManager, jxTransactionManager, transactionManager (OnBeanCondition)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5

    这里可以看到在 spring的事务管理器的自动装配的时候,已经发现了三个事务管理器,我自己注册的两个,还有一个是不知道哪儿来的。
    这里我想到一个办法,既然知道了你的beanName,那么我就在我把我自己注册的其中一个事务管理器改成你这个名字, 日志总会打印出我覆盖你的信息,这样的话就能知道大概是哪里装配的那个 transactionManager事务管理器了。

        @Bean(name = "transactionManager")
        @Primary
        public DataSourceTransactionManager bigdataTransactionManager() {
            return new DataSourceTransactionManager(bigdataDataSource());
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    修改以后,我重新启动服务,再次观察日志, 找到一段 关键信息

    Overriding bean definition for bean 'transactionManager' with a different definition: 
    replacing [Root bean: class [null]; scope=; abstract=false; lazyInit=null; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=true; factoryBeanName=bigdataDataSourceConfig; 
    factoryMethodName=bigdataTransactionManager; initMethodName=null; destroyMethodName=(inferred); 
    defined in class path resource [xxxx/xxx/xxxx/jdbc/BigdataDataSourceConfig.class]] 
    with [Root bean: class [null]; scope=; abstract=false; lazyInit=null; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=true; 
    factoryBeanName=xxxx.xxx.xxx.jdbc.MybatisPlusConfiguration; 
    factoryMethodName=transactionManager; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [xx/xxx/xxx/jdbc/MybatisPlusConfiguration.class]]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    这里我是把他的bean 覆盖了,原始的bean是从 MybatisPlusConfiguration这个类来的。 全局搜索了一下 找到这个类之后,发现了问题所在

    @Bean(name = "transactionManager")
        @Primary
        public DataSourceTransactionManager transactionManager(DataSource dataSource) {
            return new DataSourceTransactionManager(dataSource);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    这里他生成了这个 事务管理器,并且把这个管理器也置为 Primary。 所以springboot才会报错说,有多个primary bean存在。


    解决办法

    找到问题所在就简单了,因为没有使用到 mybatisplus 所以只需要找到这个依赖在哪儿引入的, 去除掉之后就好了。
    idea里面可以装 Maven Helper 插件,这个插件可以很好的帮助定位 依赖从哪儿来。

    或者不想改 pom.xml 也可以, 直接像我那样用自己配置的 事务管理器把他的覆盖掉 哈哈哈


    探究SpringBoot自动装配事务管理器

    @SpringBootApplication 注解开始 往上找

    1. @SpringBootApplication --> @EnableAutoConfiguration --> @AutoConfigurationPackage
      找到 @AutoConfigurationPackage 所在的包路径,点击跳转过去。
      在这里插入图片描述

    这个包目录下,我们可以很容易地找到一个子路径 : transaction
    里面可以很容易找到一个类 TransactionAutoConfiguration, 很明显,这个类就是spring用来自动装载事务管理器的类。

    @Configuration(proxyBeanMethods = false)
    @ConditionalOnClass(PlatformTransactionManager.class)
    @AutoConfigureAfter({ JtaAutoConfiguration.class, HibernateJpaAutoConfiguration.class,
    		DataSourceTransactionManagerAutoConfiguration.class, Neo4jDataAutoConfiguration.class })
    @EnableConfigurationProperties(TransactionProperties.class)
    public class TransactionAutoConfiguration {
    
    	@Bean
    	@ConditionalOnMissingBean
    	public TransactionManagerCustomizers platformTransactionManagerCustomizers(
    			ObjectProvider<PlatformTransactionManagerCustomizer<?>> customizers) {
    		return new TransactionManagerCustomizers(customizers.orderedStream().collect(Collectors.toList()));
    	}
    	..........
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    进去之后看这个类的注解, 重点在 @AutoConfigureAfter , 这个注解的意思就是 应在其他指定的自动配置类之后应用自动配置。
    所以根据这个注解里面的类 我们又找到了 DataSourceTransactionManagerAutoConfiguration这个类

    @Configuration(proxyBeanMethods = false)
    @ConditionalOnClass({ JdbcTemplate.class, PlatformTransactionManager.class })
    @AutoConfigureOrder(Ordered.LOWEST_PRECEDENCE)
    @EnableConfigurationProperties(DataSourceProperties.class)
    public class DataSourceTransactionManagerAutoConfiguration {
    
    	@Configuration(proxyBeanMethods = false)
    	@ConditionalOnSingleCandidate(DataSource.class)
    	static class DataSourceTransactionManagerConfiguration {
    
    		@Bean
    		@ConditionalOnMissingBean(PlatformTransactionManager.class)
    		DataSourceTransactionManager transactionManager(DataSource dataSource,
    				ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers) {
    			DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(dataSource);
    			transactionManagerCustomizers.ifAvailable((customizers) -> customizers.customize(transactionManager));
    			return transactionManager;
    		}
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    这里面我们可以看到,生成了一个Bean就是 transactionManager。 所以文章开头提到的那个多余的 bean 就是这里出现的。

    题外话:
    刚才我们在 TransactionAutoConfiguration 类 里面的 @AutoConfigureAfter 注解里面还看到一个类,Neo4jDataAutoConfiguration,看他的代码可以发现,里面也会注册一个事务管理器,但是自动装配的时候只会有一个事务管理器。这是因为:

    1. 这两个Bean上都有一个注解, @ConditionalOnMissingBean(PlatformTransactionManager.class)
      2.DataSourceTransactionManager 和 Neo4jTransactionManager 都是 PlatformTransactionManager 的一个子类,所以beanFactory里面只要加载了其中一个bean 就不会加载另一个了。

    如何装配自己的多个事务管理器

    在springboot的类加载流程中,它首先会加载应用程序配置的类(也就是你自己的config类),然后再去加载那些自动配置类。所以当我们配置了事务配置的类,就会优先加载我们自己设置的bean。

        @Bean(name = "transactionManager")
        @Primary
        public DataSourceTransactionManager bigdataTransactionManager() {
            return new DataSourceTransactionManager(bigdataDataSource());
        }
        
        @Bean(name = "anTransactionManager")
        public DataSourceTransactionManager anTransactionManager() {
            return new DataSourceTransactionManager(anDataSource());
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    并且这个bean的类是 接口PlatformTransactionManager 的实现类。那么spring在之后解析spring.factories文件中的自动事务配置类时,会因为@ConditionalOnMissingBean(PlatformTransactionManager.class)的限制,就会跳过解析。也就完成了自己的事务管理器的注入

  • 相关阅读:
    首届中国虚拟艺术巡展 NFT Showcase 圆满落幕!
    【网络】详解HTTPS及探究加密过程
    MotionLayout--在可视化编辑器中实现动画
    pygame2 画点线
    Blob对象实现文件下载和图片预览 FileReader
    Netty编码和解码
    [附源码]Python计算机毕业设计Django小太阳幼儿园学生管理系统
    windows修改键位F11变insert(改键盘映射)
    shell中 << EOF 和 EOF 使用
    Mybatis知识【Mapper代理开发&核心配置】第三章
  • 原文地址:https://blog.csdn.net/qq_40309183/article/details/126866163