spring使用@Primary注解

  1. 在某些情况下,需要注册多个相同类型的bean

    public class TestPrimary {
        private String name;
    }
    
    @Configuration
    public class Config {
    
        @Bean
        public TestPrimary testPrimary(){
            return new TestPrimary();
        }
    
        @Bean
        public TestPrimary testPrimary1(){
            return new TestPrimary();
        }
    
    }
    

    可以看到我们往spring容器中注册了两个相同类型的Bean:TestPrimary ,但是他们的beanName不一样,因此我们如果通过beanName去获取bean是不会报错的。

    public class Test {
        public static void main(String[] args) throws IOException {
            AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringAnnotationTest.class);
            TestPrimary testPrimary = (TestPrimary)context.getBean("testPrimary");
            System.out.println(testPrimary);
            TestPrimary testPrimary1 = (TestPrimary)context.getBean("testPrimary1");
            System.out.println(testPrimary1);
        }
    }
    

    打印输出:
    在这里插入图片描述
    但是,如果我们通过 TestPrimary.class 去获取bean,则会报错

    在这里插入图片描述
    此时,因为我们由于业务场景需要,确实需要注册多个相同类型的bean,那么只能通过@Primary注解解决该问题
    在这里插入图片描述
    该注解的作用也就是:当注册了多个相同类型的bean时,如果按照.class去获取bean,则默认使用被注解修饰的去获取bean

<think>我们之前已经尝试在自定义的SqlSessionFactory上添加了@Primary注解,但问题依旧。这通常意味着: 1. 可能有多个配置类都定义了SqlSessionFactory,导致@Primary被覆盖 2. 配置加载顺序问题,可能自定义配置先加载,而动态数据源配置后加载,导致动态数据源自动配置的SqlSessionFactory覆盖了自定义的 3. 可能动态数据源配置中自己创建了一个SqlSessionFactory,而我们自定义的另一个,两者冲突 根据引用[3]中的经验,有时在SqlSessionTemplate上添加@Primary也能解决问题,但我们的错误是关于SqlSessionFactory的。 现在,我们有两个SqlSessionFactory Bean:一个名称是"sqlsessionfactory"(可能是默认生成的),另一个是"sqlapisessionfactory"(自定义的)。我们尝试在自定义的SqlSessionFactory上添加了@Primary,但未生效。 可能的原因及解决方案: 1. 检查自定义的SqlSessionFactory的配置类是否被正确扫描到? 确保配置类上有@Configuration注解,并且位于Spring Boot的主应用扫描包下,或者通过@ComponentScan显式扫描。 2. 检查是否还有其他地方定义了SqlSessionFactory? 搜索整个项目(包括XML配置)中是否有其他定义SqlSessionFactory的地方。 3. 动态数据源配置可能会自动配置SqlSessionFactory,我们需要排除自动配置? 在启动类上排除MybatisAutoConfiguration: @SpringBootApplication(exclude = {MybatisAutoConfiguration.class}) 这样可以让我们的自定义配置完全生效。 4. 尝试修改Bean的名称,确保动态数据源使用的是我们自定义的那个SqlSessionFactory。 动态数据源框架(如dynamic-datasource-spring-boot-starter)通常要求SqlSessionFactory的名称为"sqlSessionFactory",所以我们自定义的Bean最好使用这个名字。 5. 如果以上方法都不行,可以尝试在自定义的SqlSessionFactory上同时使用@Bean("sqlSessionFactory")和@Primary,确保名称也是默认的。 步骤: 方案一:修改自定义SqlSessionFactory的Bean名称为默认的"sqlSessionFactory",并加上@Primary 将自定义的SqlSessionFactory的Bean名称改为"sqlSessionFactory"(即与动态数据源预期的默认名称一致): ```java @Bean(name = "sqlSessionFactory") // 使用默认名称 @Primary public SqlSessionFactory sqlApiSessionFactory(DataSource dataSource) throws Exception { // ... 原有逻辑 } ``` 方案二:排除MybatisAutoConfiguration(如果方案一无效) 在启动类上排除MybatisAutoConfiguration,这样就不会自动创建SqlSessionFactory: ```java @SpringBootApplication(exclude = {MybatisAutoConfiguration.class}) public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ``` 方案三:检查动态数据源配置中是否指定了SqlSessionFactory的Bean名称? 在application.yml中,动态数据源配置可能有指定SqlSessionFactory的名称,例如: ```yaml spring: datasource: dynamic: primary: main datasource: main: # ... 主数据源配置 slave: # ... 从数据源配置 # 注意是否有如下配置: sqlSessionFactory: sqlApiSessionFactory # 这里指定动态数据源应该使用哪个SqlSessionFactory ``` 如果没有,可以尝试指定sqlSessionFactory为我们自定义的Bean名称(比如sqlApiSessionFactory),这样动态数据源就会明确使用这个Bean,而不会再去寻找多个。 但是,请注意,动态数据源starter在创建SqlSessionFactory时,默认会使用名称为"sqlSessionFactory"的Bean。如果我们自定义的Bean名称不是这个,它可能还会自动创建一个,从而导致冲突。 所以,更推荐方案一:统一使用默认名称"sqlSessionFactory",并加上@Primary。 如果问题仍然存在,请检查是否有多个数据源配置类在创建SqlSessionFactory,确保只有一个SqlSessionFactory被创建。 验证:启动时观察日志,搜索关键字"sqlSessionFactory",看看到底有几个Bean被创建,以及它们的名称。 最后,如果以上方法都无效,可以尝试在自定义的SqlSessionFactory上使用@ConditionalOnMissingBean注解,确保在没有其他SqlSessionFactory的情况下才创建这个Bean(但这样可能会导致另一个SqlSessionFactory被创建,从而不满足条件,所以需要排除自动配置)。 总结步骤: 1. 修改自定义SqlSessionFactory的Bean名称为"sqlSessionFactory",并加上@Primary。 2. 排除MybatisAutoConfiguration(如果步骤1无效)。 3. 在配置文件中指定动态数据源使用的SqlSessionFactory为我们自定义的Bean名称(如果步骤1和2无效)。 根据引用[3]的经验,还有一种可能是需要在SqlSessionTemplate上加上@Primary,但是错误是关于SqlSessionFactory的,所以先不采用。 如果错误变化为关于SqlSessionTemplate,那么我们可以考虑在SqlSessionTemplate上添加@Primary。 现在,我们先按照方案一进行修改。 如果修改后启动仍然报错有两个Bean,那么可能是动态数据源的自动配置又创建了一个。此时,我们需要排除MybatisAutoConfiguration(方案二)或者使用@ConditionalOnMissingBean。 具体操作: 方案一代码修改示例: ```java @Configuration public class MyBatisConfig { // 将Bean名称改为默认的sqlSessionFactory,并加上@Primary @Bean(name = "sqlSessionFactory") @Primary public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean(); sessionFactory.setDataSource(dataSource); // 其他配置... return sessionFactory.getObject(); } } ``` 注意:如果之前有其他地方通过@Autowired按类型注入SqlSessionFactory,那么现在应该可以正常工作了,因为@Primary指定了主Bean。同时,因为名称是"sqlSessionFactory",动态数据源框架也会使用这个Bean。 方案二:排除自动配置类(在启动类上) ```java @SpringBootApplication(exclude = {MybatisAutoConfiguration.class}) public class MyApplication { // ... } ``` 方案三:在配置文件中指定(如果动态数据源支持): ```yaml spring: datasource: dynamic: sqlSessionFactory: sqlApiSessionFactory # 指定使用自定义的Bean名称(如果保留自定义名称) ``` 但注意,如果使用方案三,那么自定义的SqlSessionFactory的Bean名称必须是sqlApiSessionFactory,并且不需要改为sqlSessionFactory。但是,这样就需要动态数据源框架支持这个配置项(查看文档是否有该配置)。 通常情况下,动态数据源框架(如baomidou的动态数据源)默认使用名称为"sqlSessionFactory"的Bean,所以方案一更通用。 请尝试方案一,如果无效再尝试方案二。 另外,确保在配置类中只定义了一个SqlSessionFactory,如果有两个@Bean方法,请删除一个。 最后,如果问题解决,记得验证动态数据源切换是否正常。 如果问题仍未解决,请提供更多的配置信息,例如: - 自定义SqlSessionFactory的完整配置类 - 动态数据源的配置 - 项目依赖(pom.xml或build.gradle)中关于mybatis和动态数据源的版本 </think>### 问题分析与解决方案 您已配置了 `spring.datasource.dynamic.primary=main` 并在自定义的 `SqlSessionFactory` 上添加了 `@Primary` 注解,但问题仍未解决。这种情况通常由以下原因导致: #### 核心原因 1. **配置加载顺序冲突** `@Primary` 注解可能被后续配置覆盖,动态数据源框架(如 dynamic-datasource-spring-boot-starter)会在初始化阶段自动创建 `SqlSessionFactory`,与您的自定义配置冲突[^3]。 2. **Bean 名称不一致** 动态数据源框架默认查找名为 `sqlSessionFactory` 的 Bean,而您的自定义 Bean 名称是 `sqlApiSessionFactory`,导致识别失败。 3. **依赖版本兼容性问题** MyBatis 与动态数据源组件版本不匹配,可能导致自动配置逻辑异常。 --- ### 解决方案(逐步操作) #### ✅ 方案一:统一 Bean 名称并排除自动配置(推荐) ```java @Configuration public class MyBatisConfig { // 关键点 1:使用默认名称 sqlSessionFactory // 关键点 2:排除自动配置避免冲突 @Bean(name = "sqlSessionFactory") @Primary public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean(); factoryBean.setDataSource(dataSource); // 添加其他配置(类型别名、插件等) return factoryBean.getObject(); } } ``` 在启动类排除自动配置: ```java @SpringBootApplication(exclude = { MybatisAutoConfiguration.class // 关键点 3:禁止自动创建SqlSessionFactory }) public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ``` #### ✅ 方案二:强制指定动态数据源使用的 Bean 在 `application.yml` 中显式声明 Bean 名称: ```yaml spring: datasource: dynamic: primary: main sqlSessionFactory: sqlApiSessionFactory # 明确指定Bean名称 ``` #### ✅ 方案三:升级依赖解决兼容性问题 检查并更新 `pom.xml` 依赖版本: ```xml <dependency> <groupId>com.baomidou</groupId> <artifactId>dynamic-datasource-spring-boot-starter</artifactId> <version>4.2.0</version> <!-- 使用最新稳定版 --> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>3.0.3</version> <!-- 版本必须兼容 --> </dependency> ``` --- ### 验证与调试步骤 1. **检查Bean注册日志** 启动时添加参数查看Bean注册详情: ``` --debug 或 logging.level.org.springframework.beans=DEBUG ``` 搜索日志中 `Creating shared instance of singleton bean 'sqlSessionFactory'` 确认数量。 2. **优先级验证** 在自定义配置类添加验证注解: ```java @Bean @ConditionalOnMissingBean // 确保无其他冲突Bean时才生效 public SqlSessionFactory sqlSessionFactory(...) { ... } ``` 3. **依赖树检查** 运行命令确认无冲突依赖: ``` mvn dependency:tree | findstr mybatis ``` --- ### 典型错误场景复盘 ```log // 错误示例 Found 2 SqlSessionFactory beans: - sqlSessionFactory (自动创建,DataSource指向默认数据源) - sqlApiSessionFactory (自定义创建,DataSource指向动态数据源) ``` 原因:动态数据源初始化 **晚于** `MybatisAutoConfiguration`,导致自动配置优先创建了基于默认数据源的 `SqlSessionFactory`[^3]。 --- ### 预防措施 1. **配置一致性原则** - 统一使用 `sqlSessionFactory` 作为Bean名称 - 禁止混用XML/Java配置方式 2. **启动顺序控制** 在 `application.yml` 中显式控制配置顺序: ```yaml spring: autoconfigure: exclude: org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration ``` 3. **使用条件注解** 在自定义配置中添加条件判断: ```java @ConditionalOnProperty(name = "spring.datasource.dynamic.enabled") public class DynamicDataSourceConfig { ... } ``` ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值