稀土掘金技术社区 2024年12月29日
深入剖析Spring自动注入的实现原理
index_new5.html
../../../zaker_core/zaker_tpl_static/wap/tpl_guoji1.html

 

@Autowired注解是Spring框架中实现依赖注入的关键。本文从源码角度深入剖析了@Autowired注解的实现原理,揭示了Spring如何通过后置处理器AutowiredAnnotationBeanPostProcessor来自动装配Bean。文章详细介绍了@Autowired的注入时机,以及如何通过反射和BeanFactory的resolveDependency方法来实现依赖注入。同时,也解释了在多个Bean匹配时,Spring如何通过@Primary和@Priority注解以及属性名来确定最终的注入对象,帮助读者理解Spring依赖注入的底层逻辑。

💡 @Autowired 注解是Spring框架中用于自动装配Bean的关键工具,它简化了依赖注入的过程,可以应用于构造器、字段和方法上。

⚙️ AutowiredAnnotationBeanPostProcessor 是Spring框架中内置的后置处理器,负责处理标注了 @Autowired 注解的属性注入。它在Bean的初始化过程中介入,通过扫描并注入标注了@Autowired的属性实现依赖注入。

🔍 @Autowired 注解的解析时机发生在Spring Bean的初始化阶段,具体在AbstractAutowireCapableBeanFactory的populateBean方法中,通过调用后置处理器的postProcessProperties方法来完成属性的填充。其中,findAutowiringMetadata方法解析@Autowired注解,metadata.inject方法完成注入。

🎯 在依赖注入过程中,Spring会先根据类型查找匹配的Bean,如果找到多个相同类型的Bean,则会根据@Primary和@Priority注解来确定首选Bean。如果这些注解都没有,则会尝试根据属性名进行匹配,从而完成依赖注入。

原创 毅航 2024-12-29 09:01 重庆

点击关注公众号,“技术干货”即使达!

点击关注公众号,“技术干货” 及时达!

摘要:从源码角度剖析@Autowire注解实现自动注入的原理,透彻理解Spring中依赖注入实现的底层逻辑。

前言

在日常开发中,@Autowire注解可以说是我们日常开发中使用最频繁的一个注解。通过@Autowire注解我们可以顺利实现相关Bean的自动注入,而无需我们那在手动通过new关键字来构建一个对象。

但最熟悉的往往是最陌生的,不妨思考一下,虽然@Autowire是我们开发中使用最频繁的一个,但你是否明白其内部的原理呢?换言之,你是否有真正了解过@Autowired注解背后的实现原理?

不了解也没关系,本文会重点对@Autowired原理进行深入剖析。相信读完本文能加深你对于   @Autowired注解底层原理有深刻认识。

@Autowire简介

@Autowired 注解是Spring 框架中的一种依赖注入机制,用于自动装配 Bean。它可以应用于构造函数、字段和方法上。当 Spring容器启动时,它会自动寻找与被注入类型匹配的 Bean,并将其注入到标注了 @Autowired 的位置。

具体来看,其可用于:「构造器注入,字段注入,方法注入」等。其使用方式如下:

总之,@Autowired 注解是 Spring框架中用于自动装配 Bean的关键工具。它可以应用于构造器、字段和方法,简化了依赖注入的过程,增强了代码的灵活性与可维护性。

@Autowire解析原理

众所周知,在容器启动阶段Spring首先会实例化 Bean,然后进行初始化操作。在初始化阶段,而通过调用Bean 后置处理器可以完成对Bean属性的赋值等操作。进一步,对于 @Autowired而言,其在功能的实现上也依赖于——AutowiredAnnotationBeanPostProcessor这一后置处理器。

具体来看,AutowiredAnnotationBeanPostProcessorSpring框架中内置的一个后置处理器,主要负责处理标注了 @Autowired 注解标注属性的注入。「其主要在Bean的初始化过程中介入,以实现依赖注入。」

更进一步,在 Spring 容器启动时,AutowiredAnnotationBeanPostProcessor 会被注册为一个后置处理器。每当一个Bean被创建并初始化时,它会在 postProcessProperties 方法中进行扫描,识别并注入标注了 @Autowired 的属性。通过这一机制,AutowiredAnnotationBeanPostProcessor 有效地实现了 Spring 的依赖注入,提升了代码的灵活性和可维护性。

接下来,我们便来AutowiredAnnotationBeanPostProcessor看看这个类的源码究竟是如何来完成对@Autowire注解进行解析的。

@Autowire解析的时机

下图展示了Spring中一个Bean的加载全过程,具体来看,Spring在创建bean的过程中,岂会调用AbstractAutowireCapableBeanFactory中的doCreateBean方法,进而在doCreateBea中进一步调用populateBean()方法完成Bean中属性的填充,而Bean中所依赖bean信息自动装配也全部在populateBean()完成。

其中,populateBean()方法的部分源码如下。

protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
    // ... 省略无关代码
   PropertyDescriptor[] filteredPds = null;
   if (hasInstAwareBpps) {
      if (pvs == null) {
         pvs = mbd.getPropertyValues();
      }
      // 遍历容器中全部的后置处理器,执行其中的postProcessProperties方法
      for (BeanPostProcessor bp : getBeanPostProcessors()) {
         if (bp instanceof InstantiationAwareBeanPostProcessor) {
            InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
            PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
            if (pvsToUse == null) {
               if (filteredPds == null) {
                  filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
               }
               pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
               if (pvsToUse == null) {
                  return;
               }
            }
            pvs = pvsToUse;
         }
      }
   }
   // ... 省略无关代码
}

可以看看到,在populateBean其内部会循环遍历容器中的后置并判断是否需要属性填充,如果不需要进行属性填充,那么就会直接进行return,反之就会调用容器中的后置处理的postProcessPropertyValues()方法来实现Bean的依赖注入。

进一步,AutowiredAnnotationBeanPostProcessorpostProcessProperties的内部逻辑如下:

AutowiredAnnotationBeanPostProcessor # postProcessProperties

public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
    // <1> 解析出bean中带有@Autowired注解、@Inject和@Value注解的属性和方法
   InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
    // <2> 遍历标有 @Autowire注解的字段,完成反射的注入
    metadata.inject(bean, beanName, pvs);
   return pvs;
}

其中<1> findAutowiringMetadata()方法主要对Bean对象中的@Autowired注解进行解析。具体来看, 「对于属性上加了Autowired注解的,经过findAutowiringMetadata的处理后,会将字段解析为AutowiredFieldElement类型」;而如果是方法上加了@Autowired注解,则会解析为AutowiredMethodElement类型。

事实上,无论其扩展于InjectedElement<2> metadata.inject()方法则最终会调用InjectedElement类的inject()方法。

具体来看,以@Autowire标注在字段上为例来进行分析。正如之前所说,对于@Autowire标注在字段上的情况,其会将标有@Autowire字段的属性解析为AutowiredFieldElement进而调用其内部的inject方法来完成属性的注入。

其中,AutowiredFieldElement内部inject()的实现逻辑如下:

protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
      Field field = (Field) this.member;
      Object value;
          // 判断当前被注入类是否第一次注入,如果不是则从缓存中进行获取
      if (this.cached) {
         value = resolvedCachedArgument(beanName, this.cachedFieldValue);
      } else {
         DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
         desc.setContainingClass(bean.getClass());
         Set<String> autowiredBeanNames = new LinkedHashSet<>(1);
         Assert.state(beanFactory != null"No BeanFactory available");
         TypeConverter typeConverter = beanFactory.getTypeConverter();
    
         // <1> 通过beanFactory.resolveDependency()方法,来从容器中找到对应的bean信息
         value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
         
         catch (BeansException ex) {
            throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);
         }
       //<2> 借助反射来完成属性注入
      if (value != null) {
         ReflectionUtils.makeAccessible(field);
         field.set(bean, value);
      }
   }
}

概览inject方法中,不难发现其核心逻辑在于如下两点:

    通过BeanFactory中的resolveDependency找出待注入的value对象

    利用反射机制,将获取value对象通过反射注入到bean成员中

进一步,对于其中的resolveDependency的逻辑如下。

public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
      @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter)
 throws BeansException 
{
   // ..... 省略其他无关逻辑
         result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
      
      return result;
   }
}

不难发现在resolveDependency内部,其主要依赖doResolveDependency方法来完成bean对象的筛选,其具体逻辑如下:

public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
      @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter)
 throws BeansException 
{
   InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);
   try {
      ... 省略无关代码
        // <1> 解析属性,这里筛选出的属性可能是多个。即对于数组、集合、Map类型
      Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
      if (multipleBeans != null) {
         return multipleBeans;
      }
     // <2> 根据需要注入的类型type,从容器中找到有哪些匹配的Bean
      Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
      if (matchingBeans.isEmpty()) {
         if (isRequired(descriptor)) {
            raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
         }
         return null;
      }
      String autowiredBeanName;
      Object instanceCandidate;
     
      if (matchingBeans.size() > 1) {
       //<3> 判断应该使用哪一个bean
         autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
         if (autowiredBeanName == null) {
            if (isRequired(descriptor) || !indicatesMultipleBeans(type)) {
               return descriptor.resolveNotUnique(descriptor.getResolvableType(), matchingBeans);
            }
         }
         instanceCandidate = matchingBeans.get(autowiredBeanName);
      }
       // ... 省略无关代码
      if (autowiredBeanNames != null) {
         autowiredBeanNames.add(autowiredBeanName);
      }
      if (instanceCandidate instanceof Class) {
         instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);
      }
      Object result = instanceCandidate;
      if (result instanceof NullBean) {
         if (isRequired(descriptor)) {
            raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
         }
         result = null;
      }
      if (!ClassUtils.isAssignableValue(type, result)) {
         throw new BeanNotOfRequiredTypeException(autowiredBeanName, type, instanceCandidate.getClass());
      }
      return result;
   }
   finally {
      ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint);
   }
}

    对于<1>处逻辑,如果需要注入的属性是普通类型(「即非数组、集合、Map」),那么方法会继续向下执行,会调用下面一行代码,根据属性的类型来查找bean。进而走到<2>

    此处findAutowireCandidates()方法,会从容器中找到对应类型的bean。如果此时无法找到,且Autowired注解的required属性为true那么就会抛出异常。也就是我们常见的:NoSuchBeanDefinitionException

    如果能找到,则会继续执行。首先,matchingBeans这个map的大小可能大于1,因为在spring容器中是有可能找到多个满足类型的Bean信息的。针对这种情况,就需要进行额外的判断了。其逻辑即可能找到多个UserService类型的bean,那么这个时候就需要判断这多个bean中,究竟应该注入哪一个。

因此,其会调用determineAutowireCandidate()方法,判断应该使用哪一个Bean。具体来看, determineAutowireCandidate()其会首先查找标有 **@Primary注解的Bean 「作为候选Bean信息。」 而如果都没有加@Primary注解,那么就会找标有@Priority注解的Bean,进而将优先级高的Bean选中。反之,如果都没有加@Priority@Primary。那么此时则会先查找类型相匹配的类,例如,如果期待注入一个UserService类型的属性,那么其在注入时则会首先寻找UserService类型的类信息,如果找不到则会根据属性名进行查找。

这也就是我们平常所说的@Autowired注解在注入时的核心逻辑,即其会「先根据类型注入,当碰到多个相同类型时,就会根据属性名注入」

总结

本文首先介绍了@Autowired 注解的作用及使用方式,接着对@Autowired实现原理进行深入剖析。具体来看,@Autowired 自动注入的通过AutowiredAnnotationBeanPostProcessor这一后置处理器来完成的。随后,我们对其调用时机及实现逻辑进行深入剖析。简单来看,@Autowired 自动注入的本质就是反射实现Bean内部实行的赋值。而在筛选候选Bean时所采取的策略则是先根据类型查找,在根据你属性名查找候选Bean

点击关注公众号,“技术干货” 及时达!

阅读原文

跳转微信打开

Fish AI Reader

Fish AI Reader

AI辅助创作,多种专业模板,深度分析,高质量内容生成。从观点提取到深度思考,FishAI为您提供全方位的创作支持。新版本引入自定义参数,让您的创作更加个性化和精准。

FishAI

FishAI

鱼阅,AI 时代的下一个智能信息助手,助你摆脱信息焦虑

联系邮箱 441953276@qq.com

相关标签

@Autowired Spring 依赖注入 后置处理器 Bean
相关文章