当前位置: 首页 > news >正文

Spring源码分析の依赖注入(byNamebyType模式)

文章目录

  • 前言
  • 一、使用案例
  • 二、populateBean
    • 2.1、autowireByName
    • 2.2、autowireByType
      • 2.2.1、unsatisfiedNonSimpleProperties
      • 2.2.2、resolveDependency
      • 2.2.3、registerDependentBean
  • 总结


前言

  在Spring中,依赖注入(DI)可以通过xml和注解的方式实现。而注解模式也分为@AutoWired@Resource等以及@Bean(autowire = Autowire.BY_NAME)@Bean(autowire = Autowire.BY_TYPE)。本篇重点分析注解模式中的byName&byType。


一、使用案例

  假设我们有OrderService和UserService 两个类,而OrderService 是UserService 的属性,需要进行依赖注入。

@Component
public class OrderService {}
public class UserService {private OrderService orderService;/*** 根据名称注入 取的是set后的xxx** set方法是一种规范 如果不写set方法* org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#unsatisfiedNonSimpleProperties(org.springframework.beans.factory.support.AbstractBeanDefinition, org.springframework.beans.BeanWrapper)* 的PropertyDescriptor[] pds = bw.getPropertyDescriptors();* 是拿不到值的* @param orderService*/public void setOrderService(OrderService orderService) {this.orderService = orderService;}
}

  如果需要使用BY_NAME或BY_TYPE,需要手动在配置类中将UserService 注册成bean,并且指定注解模式:

@Configuration
@ComponentScan("org.ragdollcat.di")
public class AppConfig {@Bean(autowire = Autowire.BY_TYPE)public UserService userService() {return new UserService();}
}

  最终发现UserService 的OrderService 属性被成功注入:
在这里插入图片描述  而在Spring的源码中,上述的过程重点体现org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean方法的下述代码中:

  其中applyMergedBeanDefinitionPostProcessors是去找@AutoWired@Resource的注入点,所以本篇重点关注populateBean方法中关于BY_NAME和BY_TYPE的相关实现:

二、populateBean

  populateBean是用于bean实例化后属性填充的方法,关于BY_NAME和BY_TYPE的相关实现,重点是:

		//当前bean定义的属性值PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);//获取注入的模式int resolvedAutowireMode = mbd.getResolvedAutowireMode();//1:按照名称注入 2:按照类型注入if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {MutablePropertyValues newPvs = new MutablePropertyValues(pvs);// Add property values based on autowire by name if applicable.if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {//2.1、autowireByType 按照名称注入autowireByName(beanName, mbd, bw, newPvs);}// Add property values based on autowire by type if applicable.if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {//2.2、autowireByType 按照类型注入autowireByType(beanName, mbd, bw, newPvs);}pvs = newPvs;}

2.1、autowireByName

  autowireByName的源码和autowireByType类似。if (containsBean(propertyName))的判断,是根据名称注入的核心体现。

  • beanName:当前bean的名称。
  • mbd:当前bean的定义。
  • bw:当前bean实例化后被包装成BeanWrapper的对象。
  • pvs:存储了当前bean定义的属性值的集合。
protected void autowireByName(String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {//和autowireByType的unsatisfiedNonSimpleProperties 一样,获取当前bean中需要被注入的属性名String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);for (String propertyName : propertyNames) {//检查容器中是否存在名称与属性名匹配的beanif (containsBean(propertyName)) {//通过 getBean(propertyName) 获取与当前属性名匹配的bean实例。Object bean = getBean(propertyName);//将找到的bean添加到当前bean的属性值集合(pvs)中,表示该属性已经被注入。pvs.add(propertyName, bean);//和autowireByType的 registerDependentBean 一样 注册依赖关系registerDependentBean(propertyName, beanName);if (logger.isTraceEnabled()) {logger.trace("Added autowiring by name from bean name '" + beanName +"' via property '" + propertyName + "' to bean named '" + propertyName + "'");}}else {if (logger.isTraceEnabled()) {logger.trace("Not autowiring property '" + propertyName + "' of bean '" + beanName +"' by name: no matching bean found");}}}
}

2.2、autowireByType

  autowireByType是根据类型注入的核心方法:

  • beanName:当前bean的名称。
  • mbd:当前bean的定义。
  • bw:当前bean实例化后包装成的BeanWrapper 对象。
  • pvs:存储了当前bean定义的属性值的集合。
protected void autowireByType(String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {//查看是否有自定义的类型转换器TypeConverter converter = getCustomTypeConverter();if (converter == null) {converter = bw;}//初始化存放待注入bean名称的集合Set<String> autowiredBeanNames = new LinkedHashSet<>(4);//2.2.1、unsatisfiedNonSimpleProperties 检查Spring中的bean的属性依赖关系String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);//遍历上一步得到的属性名for (String propertyName : propertyNames) {try {//得到该属性的描述PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName);//Object 类型的属性通常不需要进行自动注入,因为其类型过于宽泛,无法通过类型匹配正确注入。if (Object.class != pd.getPropertyType()) {//得到set方法的参数,Spring需要通过写方法的参数类型来确定需要注入什么样的依赖。MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd);//某些bean可能需要延迟初始化,优先级较高的bean可能不希望在自动注入时过于急切地被实例化。boolean eager = !(bw.getWrappedInstance() instanceof PriorityOrdered);//创建 DependencyDescriptor 进行依赖解析,DependencyDescriptor 用于描述依赖的详细信息DependencyDescriptor desc = new AutowireByTypeDependencyDescriptor(methodParam, eager);//2.2.2、resolveDependency 解析依赖并获取自动注入的参数Object autowiredArgument = resolveDependency(desc, beanName, autowiredBeanNames, converter);if (autowiredArgument != null) {//将解析出的依赖加入到 pvs 中pvs.add(propertyName, autowiredArgument);}for (String autowiredBeanName : autowiredBeanNames) {//2.2.3、registerDependentBean 进行依赖注入registerDependentBean(autowiredBeanName, beanName);if (logger.isTraceEnabled()) {logger.trace("Autowiring by type from bean name '" + beanName + "' via property '" +propertyName + "' to bean named '" + autowiredBeanName + "'");}}autowiredBeanNames.clear();}}catch (BeansException ex) {throw new UnsatisfiedDependencyException(mbd.getResourceDescription(), beanName, propertyName, ex);}}
}

2.2.1、unsatisfiedNonSimpleProperties

  unsatisfiedNonSimpleProperties检查Spring中的bean的属性依赖关系:

protected String[] unsatisfiedNonSimpleProperties(AbstractBeanDefinition mbd, BeanWrapper bw) {Set<String> result = new TreeSet<>();//通过 mbd 获取bean的属性值PropertyValues pvs = mbd.getPropertyValues();//通过 bw 获取bean的所有属性描述符PropertyDescriptor[] pds = bw.getPropertyDescriptors();//遍历上一步拿到的所有属性描述符for (PropertyDescriptor pd : pds) {//进入该分支的条件,必须同时满足://1、属性的set方法必须要有//2、当前属性没有被排除//3、属性的名称不在 PropertyValues 中//4、属性的类型不是简单类型,如String、int、booleanif (pd.getWriteMethod() != null && !isExcludedFromDependencyCheck(pd) && !pvs.contains(pd.getName()) &&!BeanUtils.isSimpleProperty(pd.getPropertyType())) {result.add(pd.getName());}}//将符合要求的属性名称返回return StringUtils.toStringArray(result);
}

  符合要求的属性名称的第三个条件:属性的名称不在 PropertyValues 中,具体来说,就是在对象的实例化之后,会执行自定义实现了MergedBeanDefinitionPostProcessor接口的postProcessMergedBeanDefinition方法,在该方法中,可以对bean的定义再次进行修改,比如:

@Component
public class MyMergedBeanDefinitionPostProcessor implements MergedBeanDefinitionPostProcessor {/*** 手动给orderService的userService属性赋值* @param beanDefinition the merged bean definition for the bean* @param beanType the actual type of the managed bean instance* @param beanName the name of the bean*/public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {if (beanName.equals("orderService")) {UserService userService = new UserService();beanDefinition.getPropertyValues().add("userService", userService);}}
}

  手动给bean定义中的某个属性赋值,这样在自动注入时就会跳过该字段,因为最终会被覆盖。
  PropertyDescriptor[] pds = bw.getPropertyDescriptors();这个方法拿到的描述符,必须是类中含有对应set方法的属性:
在这里插入图片描述在这里插入图片描述拿不到没有set方法的属性

  并且拿到的属性名,是setOrderService中的orderService

2.2.2、resolveDependency

  resolveDependency是autowireByType根据类型注入的体现。

  • descriptor:表示当前需要解析的依赖描述符**
  • requestingBeanName:当前bean的名称
  • autowiredBeanNames:已经注入过的bean名称集合
  • typeConverter:类型转换器
@Override
@Nullable
public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {// 初始化参数名发现器descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());//处理前依赖类型是 Optional的情况if (Optional.class == descriptor.getDependencyType()) {return createOptionalDependency(descriptor, requestingBeanName);}//如果当前依赖类型是 ObjectFactory 或 ObjectProvider,则返回一个 DependencyObjectProvider 实例。//DependencyObjectProvider 是用于提供懒加载功能的代理,它会在需要时创建实际的依赖实例。else if (ObjectFactory.class == descriptor.getDependencyType() ||ObjectProvider.class == descriptor.getDependencyType()) {return new DependencyObjectProvider(descriptor, requestingBeanName);}//javax.inject.Provider 是 Java EE 和 JSR-330 标准中用于延迟获取依赖的接口。//Spring 在实现中支持这种依赖注入方式,允许在需要时按需提供依赖。else if (javaxInjectProviderClass == descriptor.getDependencyType()) {return new Jsr330Factory().createDependencyProvider(descriptor, requestingBeanName);}else {//一般情况下的依赖注入//如果一个依赖需要懒加载,getLazyResolutionProxyIfNecessary 会返回一个代理对象Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(descriptor, requestingBeanName);if (result == null) {//如果懒解析代理为 null,则调用 doResolveDependency 方法进行实际的依赖解析。result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);}return result;}
}

  假设我的set方法这样定义:
在这里插入图片描述  那么unsatisfiedNonSimpleProperties中拿到的名称是
在这里插入图片描述  MethodParameter中的类型,是setOrderService111(OrderService orderService)中的OrderService
在这里插入图片描述  在resolveDependency中,也是按照类型去进行比较的。
在这里插入图片描述  当容器中没有OrderService 类型的bean时,则无法注入。和set方法的方法名称无关。

2.2.3、registerDependentBean

  registerDependentBean主要用于处理依赖关系:dependentBeanMap,当前bean被哪些bean依赖;dependenciesForBeanMap,当前bean依赖了哪些bean。

  • beanName:当前bean中需要依赖注入的bean的名称
  • dependentBeanName:当前bean的名称。
public void registerDependentBean(String beanName, String dependentBeanName) {//这一步主要是处理别名的情况String canonicalName = canonicalName(beanName);synchronized (this.dependentBeanMap) {Set<String> dependentBeans =this.dependentBeanMap.computeIfAbsent(canonicalName, k -> new LinkedHashSet<>(8));if (!dependentBeans.add(dependentBeanName)) {return;}}synchronized (this.dependenciesForBeanMap) {Set<String> dependenciesForBean =this.dependenciesForBeanMap.computeIfAbsent(dependentBeanName, k -> new LinkedHashSet<>(8));dependenciesForBean.add(canonicalName);}
}

总结

  如果需要使用 @Bean(autowire = Autowire.BY_NAME) 或 @Bean(autowire = Autowire.BY_TYPE) 的模式,则需要确保对应的 bean 属性有 setter 方法。

  • 按类型匹配:Spring 会根据 setter 方法的入参类型匹配容器中的 bean 类型。
  • 按名称匹配:Spring 会根据 setter 方法名称(去掉 set 前缀后)来匹配容器中 bean 的名称。例如,setOrderService 方法会匹配名为 orderService 的 bean。
      需要注意的是,Spring 依赖于 setter 方法来实现 Autowire.BY_NAME 和 Autowire.BY_TYPE,如果没有 setter 方法,Spring 将无法进行自动装配。

http://www.mrgr.cn/news/91945.html

相关文章:

  • 【面试系列】Java开发--AI常见面试题
  • 设计心得——解耦的实现技术
  • Python采用DeepSeekR1本地部署+本地API接口实现简单对话
  • FTP 实验(ENSP模拟器实现)
  • C++ DAY3
  • 个人环境配置--安装记录
  • 虚拟机从零实现机器人控制
  • 深入理解设计模式之解释器模式
  • Java Web开发实战与项目——开发一个在线论坛系统
  • 一个解析cyber record文件的python示例脚本
  • 分布式事务-本地消息表学习与落地方案
  • python使用httpx_sse调用sse流式接口对响应格式为application/json的错误信息的处理
  • Swiper插件的运用和学习
  • 蓝桥与力扣刷题(蓝桥 交换瓶子)
  • C++17中std::chrono::duration和std::chrono::time_point的舍入函数
  • DPVS-1:编译安装DPVS (ubuntu22.04)
  • 23. AI-大语言模型-DeepSeek简介
  • 安全运维,等保测试常见解决问题。
  • DeepSeek与ChatGPT:会取代搜索引擎和人工客服的人工智能革命
  • 二级公共基础之数据结构与算法篇(七)排序技术