spring exception--No unique bean of type

本文探讨了Spring框架中Bean的初始化顺序及名称冲突处理机制,分析了@Service注解与XML配置下Bean的加载顺序和冲突解决策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

今天碰到一个问题,就是我现有项目需要加一个定时器任务,我的代码如下:<!-- 每日数据同步 总数监测任务******************begin --> <bean id="dataMonitorServiceImpl" class="com.netqin.function.dataMonitor.service.impl.DataMonitorServiceImpl"> </bean> <bean id="scheduledDataMonitorDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean"> <property name="targetObject"> <ref bean="dataMonitorServiceImpl" /> </property> <property name="targetMethod"> <value>autoMonitorSyncStatus</value> </property> </bean> <bean id="dataMonitorCronReportTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean"> <property name="jobDetail"> <ref bean="scheduledDataMonitorDetail" /> </property> <property name="cronExpression"> <value>0 41 10 * * ?</value> </property> </bean> <!-- 每日数据同步 总数监测任务******************end -->

这里我初始化了一个叫:dataMonitorServiceImpl的bean,这个bean是一个现有Service的实现类。

@Service public class DataMonitorServiceImpl implements IDataMonitorService { Logger logger = Logger.getLogger(DataMonitorServiceImpl.class); @Autowired private IBaseDao dao; @Autowired private IDasBaseDao dasDao;如上所示,我这里是用了注解@Service,spring会自动加载这个类,

因为上面我对这个bean有定义了一次,理论上来说会报错,因为同一个bean加载了两次,那么当向IDataMonitorService注入的时候就会发现有两个DataMonitorServiceImpl的bean,

但是我想想中的报错场景并没有发生,tomcat正常启动,我就很想知道为什么。

我把xml里面定义bean的名字改了一下,第一个字母改成大写,重启tomcat,ok,我看见了我想看见的错误:

No unique bean of type [com.netqin.function.dataMonitor.service.IDataMonitorService] is defined: expected single matching bean but found 2: [dataMonitorServiceImpl, DataMonitorServiceImpl]很明显,后边那个bean是我在xml里面声明的,那么第一个就是我用annotation的@Service声明的,这就是说明

1. @Service在声明bean的时候如果不指定名称的话,会默认以类名的一个字母小写命名。

2. 当spring初始化bean时,会监测要初始化的bean是否已经存在相同名称的bean。

做出上面的两个结论之后,我又有了俩个疑问,

1. bean在初始化的时候,xml声明和annotation的声明初始化bean的先后顺序是怎么样的。

2. bean在监测是否有相同名称的bean的时候,是只检测名称一致性,还是说是在每个bean的类别下的名称一致性?

第一个问题的答案是这样的,他们的初始化的顺序是有你指定的xml所决定的,举个例子:

你在applicationcontext.xml(这里只是举例)里显示声明了annotation需要扫描的文件目录包括那些,然后接在在下面又声明了很多bean,这种情况就是先初始化annotation,在初始化xml生命的bean。

spring是依据你声明的顺序来初始化一切,spring会从最原始的spring的xml文件开始扫描,扫描一条处理一条,也可能你的spring又引用了很多别的xml配置,那也是一样的,看引入的先后顺序。

第二个问题我做了如下的实验,:

<bean id="dataMonitorServiceImpl" class="com.netqin.function.channel.service.impl.ChannelManagementServiceImpl"> </bean> <!-- <bean id="dataMonitorServiceImpl" class="com.netqin.function.dataMonitor.service.impl.DataMonitorServiceImpl"> </bean> -->原先的bean我注释掉了 我换了一个,但是bean名字没变,

public DataMonitorServiceImpl(){ System.out.println("DataMonitorServiceImpl is loaded!"); }我给DataMonitorServiceImpl写了一个空的构造方法,已确定他是否已经被spring装载。

ok,看看tomcat说什么:

No unique bean of type [com.netqin.function.dataMonitor.service.IDataMonitorService] is defined: Unsatisfied dependency of type [interface com.netqin.function.dataMonitor.service.IDataMonitorService]: expected at least 1 matching beanspring说没有这个类型的bean被定义,这个类型的bean被期望的是最少有一个匹配的bean。

第二个问题的答案就是spring在初始化bean的时候是只保持名称的一致性,这个其实和xml里面声明bean的时候bean不能重复是一致的,多了annotation也不影响。

总结一下今天的结论:

1. @Service在声明bean的时候如果不指定名称的话,会默认以类名的一个字母小写命名。

2. 当spring初始化bean时,会监测要初始化的bean是否已经存在相同名称的bean。

3. spring初始化bean的顺序依赖于你在xml里面配置的顺序。

4.spring在初始化bean的时候是只保持名称的一致性。

### 关于 Spring Boot 3.x 中 `spring-boot-starter-jta-atomikos` 的配置与使用 #### 依赖引入 在 Spring Boot 3.x 版本中,为了支持 JTA(Java Transaction API),可以继续通过 Maven 或 Gradle 添加 Atomikos Starter 作为依赖项。以下是基于 Maven 的示例: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jta-atomikos</artifactId> </dependency> ``` 此依赖会自动拉取必要的库来启用分布式事务功能[^2]。 --- #### 数据源配置 对于多数据源场景下的事务管理,需定义多个 XA 数据源并将其注册到应用上下文中。以下是一个典型的 MySQL 配置示例: ```properties # Primary Data Source Configuration spring.jta.atomikos.datasource.primary.unique-resource-name=primaryDS spring.jta.atomikos.datasource.primary.xa-data-source-class-name=com.mysql.cj.jdbc.MysqlXADataSource spring.jta.atomikos.datasource.primary.url=jdbc:mysql://localhost:3306/primary_db?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC spring.jta.atomikos.datasource.primary.user=root spring.jta.atomikos.datasource.primary.password=password # Secondary Data Source Configuration spring.jta.atomikos.datasource.secondary.unique-resource-name=secondaryDS spring.jta.atomikos.datasource.secondary.xa-data-source-class-name=com.mysql.cj.jdbc.MysqlXADataSource spring.jta.atomikos.datasource.secondary.url=jdbc:mysql://localhost:3306/secondary_db?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC spring.jta.atomikos.datasource.secondary.user=root spring.jta.atomikos.datasource.secondary.password=password ``` 以上属性指定了两个不同的数据库连接池及其对应的资源名称[^3]。 --- #### 启用全局事务管理器 默认情况下,Spring Boot 将自动检测是否存在 JTA 支持,并初始化相应的事务管理器实例 (`UserTransactionManager`) 和同步工厂 (`SynchronizationFactory`)。如果需要自定义行为,则可以通过 Java Config 实现更精细控制: ```java import com.atomikos.icatch.config.UserTransactionServiceImp; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class JtaConfig { @Bean(initMethod = "init", destroyMethod = "close") public UserTransactionServiceImp userTransactionService() { return new UserTransactionServiceImp(); } } ``` 上述代码片段展示了如何手动创建和注入 `UserTransactionServiceImp` 豆类以增强灵活性[^2]。 --- #### 测试案例 下面提供了一个简单的测试服务用于验证跨多个数据源的操作是否被纳入同一事务范围之内: ```java @Service @.Transactional public class MultiDatasourceService { private final PrimaryRepository primaryRepo; private final SecondaryRepository secondaryRepo; public MultiDatasourceService(PrimaryRepository primaryRepo, SecondaryRepository secondaryRepo) { this.primaryRepo = primaryRepo; this.secondaryRepo = secondaryRepo; } public void performTransactionalOperation(String dataForPrimary, String dataForSecondary) { primaryRepo.save(new PrimaryEntity(dataForPrimary)); if (true) { // Simulate an exception to rollback the transaction. throw new RuntimeException("Simulated error"); } secondaryRepo.save(new SecondaryEntity(dataForSecondary)); } } ``` 在此例子中,当异常发生时整个操作会被回滚至初始状态[^3]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值