Spring依赖注入@Autowired深层原理、源码级分析,感受DI带来的编程之美【享学Spring】(中)

本文涉及的产品
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
简介: Spring依赖注入@Autowired深层原理、源码级分析,感受DI带来的编程之美【享学Spring】(中)

AutowiredFieldElement实现属性依赖注入


这个类继承自静态抽象内部类InjectionMetadata.InjectedElement,并且它还是AutowiredAnnotationBeanPostProcessor的private内部类,体现出非常高的内聚性:


  // 它的宿主类是AutowiredAnnotationBeanPostProcessor 高内聚低耦合
  private class AutowiredFieldElement extends InjectionMetadata.InjectedElement {
    private final boolean required;
    private volatile boolean cached = false;
    @Nullable
    private volatile Object cachedFieldValue;
    //唯一构造方法
    public AutowiredFieldElement(Field field, boolean required) {
      super(field, null); // 此处显示调用父类的构造函数
      this.required = required;
    }
    // 核心方法:字段的依赖注入
    @Override
    protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
      // 显然此处父类的member就指的是filed
      Field field = (Field) this.member;
      Object value;
      // 走缓存,关于cachedFieldValue的值  且听下文分解
      if (this.cached) {
        value = resolvedCachedArgument(beanName, this.cachedFieldValue);
      } else {
        // 每个Field都包装成一个DependencyDescriptor
        // 如果是Method包装成DependencyDescriptor,毕竟一个方法可以有多个入参
        // 此处包装成它后,显然和元数据都无关了,只和Field有关了  完全隔离
        DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
        desc.setContainingClass(bean.getClass());
        Set<String> autowiredBeanNames = new LinkedHashSet<>(1);
        Assert.state(beanFactory != null, "No BeanFactory available");
        // 转换器使用的bean工厂的转换器~~~
        TypeConverter typeConverter = beanFactory.getTypeConverter();
        try {
          // 获取依赖的value值的工作  最终还是委托给beanFactory.resolveDependency()去完成的~~~~
          // 这个接口方法由AutowireCapableBeanFactory提供,它提供了从bean工厂里获取依赖值的能力~
          value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
        } catch (BeansException ex) {
          throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);
        }
        // 下面代码是把缓存值缓存起来  让同一个Field注入多次能提高效率
        synchronized (this) {
          if (!this.cached) {
            // 可以看到value!=null并且required=true才会进行缓存的处理
            // 按照上例 此处value值为A@2277实例
            if (value != null || this.required) {
              this.cachedFieldValue = desc; // 可以看到缓存的值是上面的DependencyDescriptor对象~~~~
              // 这个方法是宿主类AutowiredAnnotationBeanPostProcessor的方法
              // 简单的说就是注册到bean工厂去,比如此处b是依赖a的  所以就注册这个依赖关系进去了
              // 参考this.beanFactory.registerDependentBean(autowiredBeanName, beanName);
              registerDependentBeans(beanName, autowiredBeanNames);
              // autowiredBeanNames里可能会有别名的名称~~~所以size可能大于1
              if (autowiredBeanNames.size() == 1) {
                // beanFactory.isTypeMatch挺重要的~~~~因为@Autowired是按照类型注入的
                String autowiredBeanName = autowiredBeanNames.iterator().next();
                if (beanFactory.containsBean(autowiredBeanName) && beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {
                  this.cachedFieldValue = new ShortcutDependencyDescriptor(desc, autowiredBeanName, field.getType());
                }
              }
            } else {
              this.cachedFieldValue = null;
            }
            this.cached = true;
          }
        }
      }
      // 不为null,就完成最终的set值  利用反射给filed属性赋值~~~~
      if (value != null) {
        ReflectionUtils.makeAccessible(field);
        field.set(bean, value);
      }
    }
  }


前面准备好的注解的、需要注入的元数据信息,这一步使用执行注入。

从源码中也能发现一个特点,Spring大量的使用到了转换、适配、委托等机制。因此最终最最最重要的一行代码无非是这一句:


value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);


委托AutowireCapableBeanFactory来获取到依赖值value,从而最终完成注入(反射执行注入~)。

so,其实最核心还是在Bean工厂里,也就是它的唯一内建实现类DefaultListableBeanFactory

DefaultListableBeanFactory解析依赖值


分离关注,本处只看它解析依赖值的方法resolveDependency():

public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
  ...
  @Override
  @Nullable
  public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
      @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
    //此处使用的是DefaultParameterNameDiscoverer 来获取参数名
    // 这就是为什么我们使用@Autowired注入,即使有多个同类型的Bean,也可以通过field属性名进行区分的根本原因(可以不需要使用@Qualifier注解)
    descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());
    // 支持Optional  原来还是doResolveDependency  暂略
    if (Optional.class == descriptor.getDependencyType()) {
      return createOptionalDependency(descriptor, requestingBeanName);
    } 
    // 原理也是doResolveDependency  
    else if (ObjectFactory.class == descriptor.getDependencyType() ||
        ObjectProvider.class == descriptor.getDependencyType()) {
      return new DependencyObjectProvider(descriptor, requestingBeanName);
    } else if (javaxInjectProviderClass == descriptor.getDependencyType()) {
      return new Jsr330Factory().createDependencyProvider(descriptor, requestingBeanName);
    } else { // 绝大部分情况下  肯定走到这里~~~~
      // 此处特别特别的需要重视:这是@Lazy支持的根本原因~~
      // 关于AutowireCandidateResolver我建议小伙伴先看下一个章节~~~
      // 此处若通过AutowireCandidateResolver解析到了值就直接返回了(若标注了@Lazy,此处的result将不会为null了~~~)
      Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(descriptor, requestingBeanName);
      // doResolveDependency是绝大多数情况下  最终会去执行的代码(若result仍旧为null的话)
      if (result == null) {
        result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
      }
      return result;
    }
  }
  ...
}


截止到这,整个流程中有两句非常重要的代码不容忽视:

第一句是:


/

Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(descriptor, requestingBeanName)

它会解析@Lazy这个注解,来创建代理对象。而如果它已经创建了,就不会执行第二句了。

关于这句代码的详细解释、分析,请参阅(强烈建议阅读一下本文):

【小家Spring】Spring依赖注入(DI)核心接口AutowireCandidateResolver深度分析,解析@Lazy、@Qualifier注解的原理


第二句:(重中之重)


绝大多数情况下,肯定会经由我们的doResolveDependency()方法来处理,因此这个方法的实现才是重中之重。


看过上文你就能知道:其实getLazyResolutionProxyIfNecessary()它的底层依赖还是doResolveDependency()这个方法


看看DefaultListableBeanFactory对这个"重中之重"的实现:

// 此处autowiredBeanNames是在inject的一个空的Set
// autowired表示最终可以注入进去的bean名称们(因为可能是会有多个符合条件的)
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
  ...
  @Nullable
  public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
      @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
    // 记录注入点,其实使用的ThreadLocal~
    InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);
    try {
      // 只有ShortcutDependencyDescriptor实现了resolveShortcut方法,其实就是根据
      // 实现也就一句话:beanFactory.getBean(this.shortcut, this.requiredType)
      // ShortcutDependencyDescriptor实在inject完成后创建的,就是有缓存的效果~~~
      Object shortcut = descriptor.resolveShortcut(this);
      if (shortcut != null) {
        return shortcut;
      }
      // interface com.fsx.dependency.AInterface
      Class<?> type = descriptor.getDependencyType();
      // 拿到@Value注解的value值(是个字符串)  若没有标注@Value  显然就不用那啥了
      // 从此处其实也可以看出,@Value注解的优先级对于找到bean来说还是蛮高的
      Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
      // ============ 这部分主要是解析@Value注解
      // 解析它的占位符,解析它的SpEL表达式
      // 相关处理类曹靠BeanExpressionResolver和StandardBeanExpressionResolver  StandardEvaluationContext等
      // 因为关于@Value的文章里详细解过,此处一笔带过~~~
      if (value != null) {
        if (value instanceof String) {
          String strVal = resolveEmbeddedValue((String) value);
          BeanDefinition bd = (beanName != null && containsBean(beanName) ?
              getMergedBeanDefinition(beanName) : null);
          value = evaluateBeanDefinitionString(strVal, bd);
        }
        TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
        try {
          return converter.convertIfNecessary(value, type, descriptor.getTypeDescriptor());
        } catch (UnsupportedOperationException ex) {
          // A custom TypeConverter which does not support TypeDescriptor resolution...
          return (descriptor.getField() != null ?
              converter.convertIfNecessary(value, type, descriptor.getField()) :
              converter.convertIfNecessary(value, type, descriptor.getMethodParameter()));
        }
      }
      // ============
      // 此处处理的是多值注入的情况,比如注入Stream<Object>、Map、Array、Collection等等  Spring都是支持的
      // 需要稍微注意一点,Stream这种类型的注入在Spring4以上才得到支持的
      Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
      if (multipleBeans != null) {
        return multipleBeans;
      }
      // 显然绝大部分情况下,都会走到这里(因为大部分我们都是单值注入~)
      // findAutowireCandidates可以说又是整个依赖注入的灵魂之一~~~~ 它的查找步骤简述如下:
      //1、BeanFactoryUtils.beanNamesForTypeIncludingAncestors() 找到这种类型的所有的beanNames(candidateNames)(可能有多个哟,但大多数情况下只有一个)
      //2、处理resolvableDependencies比如ApplicationContext的依赖,让他们也能够正常注入进去(他们可不作为bean存在容器里~)
      //3、遍历candidateNames:检查它是否可以被依赖、容器内是否存在bean定义(或者Singleton) 若符合,getBean()出来放在map里
      //4、若返回的Map不为Empty()了,直接return  表示找到了(当然还可能是多个~~)
      // 若返回的还是Empty,那就继续检查requiredType是否为Map、Collection等类型,从它里面去找。。。(这种注入case使用太少,此处暂略~)
      Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
      // 所以matchingBeans证明没有找到匹配的,但requied=true,所以此处就抛出异常了~
      if (matchingBeans.isEmpty()) {
        if (isRequired(descriptor)) {
          raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
        }
        return null;
      }
      String autowiredBeanName;
      Object instanceCandidate;
      // 若有多个匹配
      // 需要注意的是:@Qualifier注解在上面就已经生效了~~~~因为AutowireCandidateResolver.isAutowireCandidate是在上面生效的
      if (matchingBeans.size() > 1) {
        // 由它进行判别  从弱水三千中  取出一瓢
        // 1、是否标注有@Primary  有这种bean就直接返回(@Primary只允许标注在一个同类型Bean上)
        // 2、看是否有标注有`javax.annotation.Priority`这个注解的
        // 3、根据字段field名,去和beanName匹配  匹配上了也行(这就是为何我们有时候不用@Qulifier也没事的原因之一)
        // 此处注意:descriptor.getDependencyName()这个属性表示字段名,靠的是`DefaultParameterNameDiscoverer`去把字段名取出来的~
        autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
        if (autowiredBeanName == null) {
          if (isRequired(descriptor) || !indicatesMultipleBeans(type)) {
            // 此处抛出异常:NoUniqueBeanDefinitionException
            return descriptor.resolveNotUnique(descriptor.getResolvableType(), matchingBeans);
          } else {
            // In case of an optional Collection/Map, silently ignore a non-unique case:
            // possibly it was meant to be an empty collection of multiple regular beans
            // (before 4.3 in particular when we didn't even look for collection beans).
            return null;
          }
        }
        instanceCandidate = matchingBeans.get(autowiredBeanName);
      } else { // 只有一个匹配就啥都说了  干就完了
        // We have exactly one match.
        Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();
        autowiredBeanName = entry.getKey();
        instanceCandidate = entry.getValue();
      }
      if (autowiredBeanNames != null) {
        autowiredBeanNames.add(autowiredBeanName);
      }
      if (instanceCandidate instanceof Class) {
        // 此处getBean() 拿到该类型的实例对象了~~~
        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);
    }
  }
  ...
}
相关文章
|
17天前
|
安全 Java 数据库
一天十道Java面试题----第四天(线程池复用的原理------>spring事务的实现方式原理以及隔离级别)
这篇文章是关于Java面试题的笔记,涵盖了线程池复用原理、Spring框架基础、AOP和IOC概念、Bean生命周期和作用域、单例Bean的线程安全性、Spring中使用的设计模式、以及Spring事务的实现方式和隔离级别等知识点。
|
15天前
|
Java
Spring5入门到实战------9、AOP基本概念、底层原理、JDK动态代理实现
这篇文章是Spring5框架的实战教程,深入讲解了AOP的基本概念、如何利用动态代理实现AOP,特别是通过JDK动态代理机制在不修改源代码的情况下为业务逻辑添加新功能,降低代码耦合度,并通过具体代码示例演示了JDK动态代理的实现过程。
Spring5入门到实战------9、AOP基本概念、底层原理、JDK动态代理实现
|
2月前
|
Java 应用服务中间件 开发者
Java面试题:解释Spring Boot的优势及其自动配置原理
Java面试题:解释Spring Boot的优势及其自动配置原理
93 0
|
3天前
|
人工智能 前端开发 Java
【实操】Spring Cloud Alibaba AI,阿里AI这不得玩一下(含前后端源码)
本文介绍了如何使用 **Spring Cloud Alibaba AI** 构建基于 Spring Boot 和 uni-app 的聊天机器人应用。主要内容包括:Spring Cloud Alibaba AI 的概念与功能,使用前的准备工作(如 JDK 17+、Spring Boot 3.0+ 及通义 API-KEY),详细实操步骤(涵盖前后端开发工具、组件选择、功能分析及关键代码示例)。最终展示了如何成功实现具备基本聊天功能的 AI 应用,帮助读者快速搭建智能聊天系统并探索更多高级功能。
29 2
【实操】Spring Cloud Alibaba AI,阿里AI这不得玩一下(含前后端源码)
|
15天前
|
XML Java 数据格式
Spring5入门到实战------2、IOC容器底层原理
这篇文章深入探讨了Spring5框架中的IOC容器,包括IOC的概念、底层原理、以及BeanFactory接口和ApplicationContext接口的介绍。文章通过图解和实例代码,解释了IOC如何通过工厂模式和反射机制实现对象的创建和管理,以及如何降低代码耦合度,提高开发效率。
Spring5入门到实战------2、IOC容器底层原理
|
18天前
|
Java 程序员 数据库连接
女朋友不懂Spring事务原理,今天给她讲清楚了!
该文章讲述了如何解释Spring事务管理的基本原理,特别是针对女朋友在面试中遇到的问题。文章首先通过一个简单的例子引入了传统事务处理的方式,然后详细讨论了Spring事务管理的实现机制。
女朋友不懂Spring事务原理,今天给她讲清楚了!
|
2月前
|
Java Spring 容器
Spring Boot 启动源码解析结合Spring Bean生命周期分析
Spring Boot 启动源码解析结合Spring Bean生命周期分析
67 11
|
9天前
|
设计模式 Java 程序员
学习 Spring 源码的意义是什么呢?
研究Spring源码能深化框架理解,提升代码分析与设计能力,助您掌握设计模式及最佳实践,增强解决问题的效率,促进职业生涯发展,并激发技术热情。选择稳定版本,从核心模块开始,结合实际项目并参与社区,让学习之旅既充实又具乐趣。
|
2月前
|
缓存 Java 程序员
spring IoC 源码
spring IoC 源码
44 3
|
2月前
|
Java Spring
Spring初始化加速的思路和方案问题之DAG分析在Spring并行初始化中面临哪些困难
Spring初始化加速的思路和方案问题之DAG分析在Spring并行初始化中面临哪些困难
下一篇
云函数