Spring的@Autowired依赖注入原来这么多坑!(中)

本文涉及的产品
云数据库 RDS SQL Server,基础系列 2核4GB
RDS SQL Server Serverless,2-4RCU 50GB 3个月
推荐场景:
简介: 像第一个案例,同种类型的实现,可能不是同时出现在自己的项目代码中,而是有部分实现出现在依赖的类库。看来研究源码的确能让我们少写几个 bug!

修正

打破上述两个条件中的任何一个即可,即让候选项具有优先级或根本不选择。

但并非每种条件的打破都满足实际需求:

如可以通过使用**@Primary**让被标记的候选者有更高优先级,但并不一定符合业务需求,好比我们本身需要两种DB都能使用,而非不可兼得。

@Repository
@Primary
@Slf4j
public class OracleDataService implements DataService{
    //省略非关键代码
}

要同时支持多种DataService,不同情景精确匹配不同的DataService,可这样修改:

@Autowired
DataService oracleDataService;

将属性名和Bean名精确匹配,就能实现完美的注入选择:

  • 需要Oracle时指定属性名为oracleDataService
  • 需要Cassandra时则指定属性名为cassandraDataService

显式引用Bean时首字母忽略大小写

还有另外一种解决办法,即采用@Qualifier显式指定引用服务,例如采用下面的方式:

@Autowired()
@Qualifier("cassandraDataService")
DataService dataService;

这样能让寻找出的Bean只有一个(即精确匹配),无需后续的决策过程:

DefaultListableBeanFactory#doResolveDependency

@Nullable
public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
      @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
      //省略其他非关键代码
      //寻找bean过程
      Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
      if (matchingBeans.isEmpty()) {
         if (isRequired(descriptor)) {
            raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
         }
         return null;
      }
      //省略其他非关键代码
      if (matchingBeans.size() > 1) {
         //省略多个bean的决策过程,即案例1重点介绍内容
      } 
     //省略其他非关键代码
}

使用 @Qualifier 指定名称匹配,最终只找到唯一一个。但使用时,可能会忽略Bean名称首字母大小写。

如:

@Autowired
@Qualifier("CassandraDataService")
DataService dataService;

运行报错:

Exception encountered during context initialization - cancelling refresh
attempt: org.springframework.beans.factory.UnsatisfiedDependencyException:
 Error creating bean with name 'studentController': Unsatisfied dependency
  expressed through field 'dataService'; nested exception is
   org.springframework.beans.factory.NoSuchBeanDefinitionException: No
    qualifying bean of type 'com.spring.puzzle.class2.example2.DataService'
     available: expected at least 1 bean which qualifies as autowire
      candidate. Dependency annotations:
       {@org.springframework.beans.factory.annotation.Autowired(required=true),
        @org.springframework.beans.factory.annotation.Qualifier(value=CassandraDataService)}

若未显式指定 bean 名称,默认就是类名,不过首字母小写!

假设要支持SQLServer,定义了一个名为SQLServerDataService的实现:

@Autowired
@Qualifier("sQLServerDataService")
DataService dataService;

依然出现之前错误,而若改成SQLServerDataService,则运行通过。

这真是疯了呀!

显式引用Bean时,首字母到底是大写还是小写?

答疑

raiseNoMatchingBeanFound(type, descriptor.getResolvableType(),
  descriptor);

当因名称问题(例如引用Bean首字母搞错了)找不到Bean,会抛NoSuchBeanDefinitionException。


不显式设置名字的Bean,其默认名称首字母到底是大写还是小写呢?

Spring Boot应用会自动扫包,找出直接或间接标记了 @Component 的BeanDefinition。例如CassandraDataService、SQLServerDataService都被标记了@Repository,而Repository本身被@Component标记,所以都间接标记了@Component。


一旦找出这些Bean信息,就可生成Bean名,然后组合成一个个BeanDefinitionHolder返回给上层:

ClassPathBeanDefinitionScanner#doScan

image.png

BeanNameGenerator#generateBeanName产生Bean名,有两种实现方式:

image.png

因为DataService实现都是使用注解,所以Bean名称的生成逻辑最终调用的其实是

AnnotationBeanNameGenerator#generateBeanName

image.png

看Bean有无显式指明名称,若:


  • 用显式名称
  • 没有
    生成默认名称

案例没有给Bean指名,所以生成默认名称,通过方法:

buildDefaultBeanName

image.png

首先,获取一个简短的ClassName,然后调用Introspector#decapitalize方法,设置首字母大写或小写,具体参考下面的代码实现:

image.png

  • 一个类名是以两个大写字母开头,则首字母不变
  • 其它情况下默认首字母变成小写

SQLServerDataService的Bean,其名称应该就是类名本身,而CassandraDataService的Bean名称则变成了首字母小写(cassandraDataService)。

相关实践学习
使用SQL语句管理索引
本次实验主要介绍如何在RDS-SQLServer数据库中,使用SQL语句管理索引。
SQL Server on Linux入门教程
SQL Server数据库一直只提供Windows下的版本。2016年微软宣布推出可运行在Linux系统下的SQL Server数据库,该版本目前还是早期预览版本。本课程主要介绍SQLServer On Linux的基本知识。 相关的阿里云产品:云数据库RDS&nbsp;SQL Server版 RDS SQL Server不仅拥有高可用架构和任意时间点的数据恢复功能,强力支撑各种企业应用,同时也包含了微软的License费用,减少额外支出。 了解产品详情:&nbsp;https://www.aliyun.com/product/rds/sqlserver
目录
相关文章
|
1月前
|
Java 测试技术 程序员
为什么Spring不推荐@Autowired用于字段注入?
作为Java程序员,Spring框架在日常开发中使用频繁,其依赖注入机制带来了极大的便利。然而,尽管@Autowired注解简化了依赖注入,Spring官方却不推荐在字段上使用它。本文将探讨字段注入的现状及其存在的问题,如难以进行单元测试、违反单一职责原则及易引发NPE等,并介绍为何Spring推荐构造器注入,包括增强代码可读性和维护性、方便单元测试以及避免NPE等问题。通过示例代码展示如何将字段注入重构为构造器注入,提高代码质量。
|
4月前
|
XML Java 测试技术
Spring Boot中的依赖注入和控制反转
Spring Boot中的依赖注入和控制反转
|
3月前
|
Java Spring 容器
彻底改变你的编程人生!揭秘 Spring 框架依赖注入的神奇魔力,让你的代码瞬间焕然一新!
【8月更文挑战第31天】本文介绍 Spring 框架中的依赖注入(DI),一种降低代码耦合度的设计模式。通过 Spring 的 DI 容器,开发者可专注业务逻辑而非依赖管理。文中详细解释了 DI 的基本概念及其实现方式,如构造器注入、字段注入与 setter 方法注入,并提供示例说明如何在实际项目中应用这些技术。通过 Spring 的 @Configuration 和 @Bean 注解,可轻松定义与管理应用中的组件及其依赖关系,实现更简洁、易维护的代码结构。
51 0
|
3月前
|
设计模式 自然语言处理 Java
简单了解下Spring中的各种Aware接口实现依赖注入
在Spring框架中,Aware接口是一组用于提供特定资源或环境信息的回调接口。这些接口被设计用来允许Bean获取对Spring容器或其他相关资源的引用,并在需要时进行适当的处理。
36 2
|
3月前
|
自然语言处理 Java 开发者
简单了解下Spring中的各种Aware接口实现依赖注入
【8月更文挑战第21天】在Spring框架中,Aware接口系列是一种特殊的机制,它允许Bean在初始化过程中获取到Spring容器或容器中的特定资源,从而实现了更加灵活和强大的依赖注入方式。本文将围绕Spring中的各种Aware接口,详细探讨它们如何帮助开发者在工作和学习中更好地实现依赖注入。
107 0
|
4月前
|
缓存 Java Spring
Spring循环依赖问题之Spring不支持构造器内的强依赖注入如何解决
Spring循环依赖问题之Spring不支持构造器内的强依赖注入如何解决
|
5月前
|
设计模式 Java 测试技术
Spring Boot中的依赖注入详解
Spring Boot中的依赖注入详解
|
5月前
|
Java Spring 容器
spring如何进行依赖注入,通过set方法把Dao注入到serves
spring如何进行依赖注入,通过set方法把Dao注入到serves
|
4月前
|
XML Java 数据格式
深入理解Spring中的依赖注入原理
深入理解Spring中的依赖注入原理
|
4月前
|
Java Spring 容器
深入理解Spring Boot中的容器与依赖注入
深入理解Spring Boot中的容器与依赖注入