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

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

前言


关于Spring IOC的依赖注入(DI机制),之前虽有过分析,但总感觉一直落了一块:对@Autowired注解元数据的解析部分。


本篇文章重在拾遗,并且从依赖注入整体的流程上进行把握。因为个人觉得依赖注入对于Spring框架来说太重要了,所以用多少笔墨,强调多少遍都不为过。so希望本篇文章能继续为大家服务,帮助到大家~


在继续这篇文章之前,强烈建议还赌自己是"小白"的同学先观看博文:

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


关于Spring的依赖注入(DI)


注入:为某个对象的外部资源赋值,注入某个对象所需要的外部资源(包括对象、资源、常量数据等)。


注意对这里我所说的外部资源的理解,理解了就好,不用刻意的咬文嚼字~


依赖注入:Dependency Injection,简称DI,说白了就是利用反射机制为类的属性赋值的操作。


关于@Autowired这个注解,我们再熟悉不过了,经常跟@Resource来做对比,这篇文章我们不讨论两者有何异同,仅分析@Autowired的深层原理


AutowiredAnnotationBeanPostProcessor

准备

解析@Resource注解的不是这个类,而是CommonAnnotationBeanPostProcessor,但本文只会以AutowiredAnnotationBeanPostProcessor为例做深入分析~~~(解析@Autowired)


为了更好的说明问题,以下面这个具体实例进行讲解:


@Service
public class A implements AInterface {
    @Async
    @Override
    public void funA() {
        System.out.println("线程名称:" + Thread.currentThread().getName());
    }
}
@Service
public class B implements BInterface {
    @Autowired
    private AInterface a;
    @Override
    public void funTemp() {
        System.out.println("线程名称:" + Thread.currentThread().getName());
    }
    @Override
    public void funB() {
        System.out.println("线程名称:" + Thread.currentThread().getName());
    }
}


源码分析


对于此类,其实@Autowired的javadoc上就有提到:它是靠AutowiredAnnotationBeanPostProcessor去完成解析结果的


Please consult the javadoc for the {@link AutowiredAnnotationBeanPostProcessor} class (which, by default, checks for the presence of this annotation).


先看一下AutowiredAnnotationBeanPostProcessor它的继承图表:


image.png

由此继承图标我们能得出如下结论:


  1. 实现了InstantiationAwareBeanPostProcessor接口,所以可以介入到Bean的实例化前后
  2. 实现了BeanPostProcessor,所以介入到Bean的初始化前后
  3. 实现了MergedBeanDefinitionPostProcessor,说明它可以合并bean的定义信息
  4. 实现了BeanFactoryAware,实现了PriorityOrdered


解析注解元信息阶段

public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter implements MergedBeanDefinitionPostProcessor, PriorityOrdered, BeanFactoryAware {
  // 该处理器支持解析的注解们~~~(这里长度设置为4)  默认支持的是3个(当然你可以自己添加自定义的依赖注入的注解   这点非常强大)
  private final Set<Class<? extends Annotation>> autowiredAnnotationTypes = new LinkedHashSet<>(4);
  // @Autowired(required = false)这个注解的属性值名称
  // A 'required' dependency means that autowiring should fail when no beans are found.
  private String requiredParameterName = "required";
  // 这个值一般请不要改变(若改成false,效果required = false的作用是相反的了)
  private boolean requiredParameterValue = true;
  private int order = Ordered.LOWEST_PRECEDENCE - 2;
  // 对@Lookup方法的支持  本文不讨论
  private final Set<String> lookupMethodsChecked = Collections.newSetFromMap(new ConcurrentHashMap<>(256));
  // 构造函数注入,本文也不讨论
  private final Map<Class<?>, Constructor<?>[]> candidateConstructorsCache = new ConcurrentHashMap<>(256);
  // 方法注入、字段filed注入  本文的重中之重
  // 此处InjectionMetadata这个类非常重要,到了此处@Autowired注解含义已经没有了,完全被准备成这个元数据了  所以方便我们自定义注解的支持~~~优秀
  // InjectionMetadata持有targetClass、Collection<InjectedElement> injectedElements等两个重要属性
  // 其中InjectedElement这个抽象类最重要的两个实现为:AutowiredFieldElement和AutowiredMethodElement
  private final Map<String, InjectionMetadata> injectionMetadataCache = new ConcurrentHashMap<>(256);
  // 这是它唯一构造函数  默认支持下面三种租借(当然@Inject需要额外导包)
  // 请注意:此处@Value注解也是被依赖注入解析的~~~~~~~~
  // 当然如果你需要支持到你的自定义注解,你还可以调用下面的set方法添加。。。
  public AutowiredAnnotationBeanPostProcessor() {
    this.autowiredAnnotationTypes.add(Autowired.class);
    this.autowiredAnnotationTypes.add(Value.class);
    try {
      this.autowiredAnnotationTypes.add((Class<? extends Annotation>)ClassUtils.forName("javax.inject.Inject", AutowiredAnnotationBeanPostProcessor.class.getClassLoader()));
      logger.trace("JSR-330 'javax.inject.Inject' annotation found and supported for autowiring");
    } catch (ClassNotFoundException ex) {
      // JSR-330 API not available - simply skip.
    }
  }
  // 下面两个方法可以自定义支持的依赖注入注解类型
  public void setAutowiredAnnotationType(Class<? extends Annotation> autowiredAnnotationType) {
    Assert.notNull(autowiredAnnotationType, "'autowiredAnnotationType' must not be null");
    this.autowiredAnnotationTypes.clear();
    this.autowiredAnnotationTypes.add(autowiredAnnotationType);
  }
  public void setAutowiredAnnotationTypes(Set<Class<? extends Annotation>> autowiredAnnotationTypes) {
    Assert.notEmpty(autowiredAnnotationTypes, "'autowiredAnnotationTypes' must not be empty");
    this.autowiredAnnotationTypes.clear();
    this.autowiredAnnotationTypes.addAll(autowiredAnnotationTypes);
  }
  ... // 省略其余get/set方法
  // bean工厂必须是ConfigurableListableBeanFactory的(此处放心使用,唯独只有SimpleJndiBeanFactory不是它的子类而已~)
  @Override
  public void setBeanFactory(BeanFactory beanFactory) {
    if (!(beanFactory instanceof ConfigurableListableBeanFactory)) {
      throw new IllegalArgumentException("AutowiredAnnotationBeanPostProcessor requires a ConfigurableListableBeanFactory: " + beanFactory);
    }
    this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
  }
  // 第一个非常重要的核心方法~~~  
  //它负责1、解析@Autowired等注解然后转换
  // 2、把注解信息转换为InjectionMetadata然后缓存到上面的injectionMetadataCache里面
  // postProcessMergedBeanDefinition的执行时机非常早,在doCreateBean()前部分完成bean定义信息的合并
  @Override
  public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
    // findAutowiringMetadata方法重要,完成了解析注解、缓存下来的操作
    InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null);
    // 检查~~~(不太重要,可忽略)
    metadata.checkConfigMembers(beanDefinition);
  }
  // 方法名为查找到该bean的依赖注入元信息,内部只要查找到了就会加入到缓存内,下次没必要再重复查找了~
  // 它是一个模版方法,真正做事的方法是:buildAutowiringMetadata  它复杂把标注有@Autowired注解的属性转换为Metadata元数据信息  从而消除注解的定义
  // 此处查找包括了字段依赖注入和方法依赖注入~~~
  private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {
    String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
    InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
    if (InjectionMetadata.needsRefresh(metadata, clazz)) {
      synchronized (this.injectionMetadataCache) {
        metadata = this.injectionMetadataCache.get(cacheKey);
        if (InjectionMetadata.needsRefresh(metadata, clazz)) {
          if (metadata != null) {
            metadata.clear(pvs);
          }
          metadata = buildAutowiringMetadata(clazz);
          this.injectionMetadataCache.put(cacheKey, metadata);
        }
      }
    }
    return metadata;
  }
  // 这里我认为是整个依赖注入前期工作的精髓所在,简单粗暴的可以理解为:它把以依赖注入都转换为InjectionMetadata元信息,待后续使用
  // 这里会处理字段注入、方法注入~~~
  // 注意:Autowired使用在static字段/方法上、0个入参的方法上(不会报错 只是无效)
  // 显然方法的访问级别、是否final都是可以正常被注入进来的~~~
  private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
    List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
    Class<?> targetClass = clazz;
    // 小细节:这里是个do while循环,所以即使在父类,父父类上依赖注入依旧是好使的(直到Object后停止)
    do {
      final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();
      ReflectionUtils.doWithLocalFields(targetClass, field -> {
        AnnotationAttributes ann = findAutowiredAnnotation(field);
        if (ann != null) {
          // static属性不好使
          if (Modifier.isStatic(field.getModifiers())) {
            if (logger.isInfoEnabled()) {
              logger.info("Autowired annotation is not supported on static fields: " + field);
            }
            return;
          }
          //解析required属性(若存在)  最终封装成AutowiredFieldElement
          boolean required = determineRequiredStatus(ann);
          currElements.add(new AutowiredFieldElement(field, required));
        }
      });
      ReflectionUtils.doWithLocalMethods(targetClass, method -> {
        Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
        if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
          return;
        }
        AnnotationAttributes ann = findAutowiredAnnotation(bridgedMethod);
        if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
          // static方法不好使
          if (Modifier.isStatic(method.getModifiers())) {
            if (logger.isInfoEnabled()) {
              logger.info("Autowired annotation is not supported on static methods: " + method);
            }
            return;
          }
          // 方法没有入参不好使
          if (method.getParameterCount() == 0) {
            if (logger.isInfoEnabled()) {
              logger.info("Autowired annotation should only be used on methods with parameters: " + method);
            }
          }
          boolean required = determineRequiredStatus(ann);
          PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
          // AutowiredMethodElement里封装了一个PropertyDescriptor(比字段多了一个参数)
          currElements.add(new AutowiredMethodElement(method, required, pd));
        }
      });
      // 小细节:父类的都放在第一位,所以父类是最先完成依赖注入的
      elements.addAll(0, currElements);
      targetClass = targetClass.getSuperclass();
    } while (targetClass != null && targetClass != Object.class);
    // 可见InjectionMetadata就是对clazz和elements的一个包装而已
    return new InjectionMetadata(clazz, elements);
  }
  // 只要方法/属性上但凡标注有一个注解,就立马返回了~~(so你标注多个最终生效的只会有一个哦)
  @Nullable
  private AnnotationAttributes findAutowiredAnnotation(AccessibleObject ao) {
    if (ao.getAnnotations().length > 0) {  // autowiring annotations have to be local
      for (Class<? extends Annotation> type : this.autowiredAnnotationTypes) {
        AnnotationAttributes attributes = AnnotatedElementUtils.getMergedAnnotationAttributes(ao, type);
        if (attributes != null) {
          return attributes;
        }
      }
    }
    return null;
  }
  // 小细节这个方法是protected的
  protected boolean determineRequiredStatus(AnnotationAttributes ann) {
    return (!ann.containsKey(this.requiredParameterName) || this.requiredParameterValue == ann.getBoolean(this.requiredParameterName));
  }
  ... // 关于内部类inject的最终可以放在下面的重点
}

这一步,借助postProcessMergedBeanDefinition()方法完成了对该bean中所有的依赖注入的属性、方法完成了原始元信息的转换,已经把依赖注入的相关注解全都转换成了InjectionMetadata,这样后面的使用过程中将不再需要再和具体注解打交道,而是做着一些和业务无关的动作即可。

注入阶段


这是核心阶段,也是最为复杂的阶段,当然前面的解析已经为本步骤做好了元数据的铺垫。


我们知道在Bean的创建过程中,完成Bean的实例化后,会调用方法AbstractAutowireCapableBeanFactory#populateBean()完成对Bean的属性赋值,从而就会触发InstantiationAwareBeanPostProcessor.postProcessPropertyValues()方法给属性进行赋值,这处也就是本步骤的入口~


注意:Spring5.1之后把此方法@Deprecated了,用postProcessProperties方法代替(这个方法名语义更清晰,单纯的方法名的改动而已,内容无任何变动~~)

// @since 2.5
public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter implements MergedBeanDefinitionPostProcessor, PriorityOrdered, BeanFactoryAware {
  ...
  @Deprecated // 标注为过期,直接调用postProcessProperties方法
  @Override
  public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) {
    return postProcessProperties(pvs, bean, beanName);
  } 
  @Override
  public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
    // 从缓存中取出这个bean对应的依赖注入的元信息~
    InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
    //(生路异常处理部分代码)所以事情都委托给了InjectionMetadata 的inject方法
    // 此处注意InjectionMetadata是会包含多个那啥的~~~(当然可能啥都没有  没有依赖注入的东东)
    //InjectionMetadata.inject内部查看也十分简单:最终都还是委托给了InjectedElement.inject实例去处理的
    metadata.inject(bean, beanName, pvs);
    return pvs;
  }
}


综上能够发现,最终依赖注入实际做事的是InjectedElement,这个抽象类有好几个实现,此处我们以使用最为广泛的AutowiredFieldElement进行说明

相关文章
|
9天前
|
设计模式 Java Spring
spring源码设计模式分析(五)-策略模式
spring源码设计模式分析(五)-策略模式
|
7天前
|
负载均衡 Java 网络架构
实现微服务网关:Zuul与Spring Cloud Gateway的比较分析
实现微服务网关:Zuul与Spring Cloud Gateway的比较分析
20 5
|
9天前
|
消息中间件 设计模式 缓存
spring源码设计模式分析(四)-观察者模式
spring源码设计模式分析(四)-观察者模式
|
9天前
|
设计模式 Java Spring
spring源码设计模式分析(六)-模板方法模式
spring源码设计模式分析(六)-模板方法模式
|
9天前
|
设计模式 Java Spring
spring源码设计模式分析(七)-委派模式
spring源码设计模式分析(七)-委派模式
|
9天前
|
XML 存储 Java
Spring-源码深入分析(二)
Spring-源码深入分析(二)
|
9天前
|
XML 设计模式 Java
Spring-源码深入分析(一)
Spring-源码深入分析(一)
|
7天前
|
SQL 监控 druid
springboot-druid数据源的配置方式及配置后台监控-自定义和导入stater(推荐-简单方便使用)两种方式配置druid数据源
这篇文章介绍了如何在Spring Boot项目中配置和监控Druid数据源,包括自定义配置和使用Spring Boot Starter两种方法。
|
XML 数据格式 Java
Spring_DI_XML_01
欢迎移步博客查看-http://futaosmile.oschina.io/coder957 基于XMl的DI 1.设值注入 2.构造注入 3.p命名空间设值注入 4.
816 0
|
XML 数据格式 Python
Spring_DI_XML_02
欢迎移步博客查看-http://futaosmile.oschina.io/coder957 基于XMl的DI 1.集合属性注入 2.array数组属性注入 3.
880 0
下一篇
无影云桌面