聊透Spring依赖注入

聊透Spring依赖注入依赖注入是我们使用Spring时最最常用的功能,甚至都不是之一。

大家好,欢迎来到IT知识分享网。

依赖注入是我们使用Spring时最最常用的功能,甚至都不是之一。然而即使面对如此常用的功能,有时间难免也会力不从心,被它小小欺负一下,比如NoSuchBeanDefinitionException、NoUniqueBeanDefinitionException、UnsatisfiedDependencyException等等小问题总是时不时叨扰力一下,给原来快乐划水的日子平添一丝烦恼。

究其原因,还是对Spring依赖注入的原理和实现不清楚所致,小伙伴们可以先自查一下这几个问题:

  1. @AutoWire注入时,是根据类型查找的,有多个实现的子类,是如何选择注入对象的,属性名称对注入对象的选择有影响吗?怎么控制注入特定子类呢?
  2. @AutoWire注入列表属性时,想控制列表顺序该如何实现呢?
  3. @Resource注入时,是根据名称查找的,如果名称查找不到,会根据类型查找吗?name属性的配置,对查找逻辑有影响吗?
  4. Spring依赖注入的查找逻辑是怎么样的,是遍历属性和方法,然后通过反射注入的吗?

如果小伙伴对Spring比较了解,对上述问题都了然指掌的话,请先受小弟一拜,大神非你莫属。如若不然,那就跟随我的脚步,让我们一起慢慢解开Spring依赖注入的神秘面纱,寻找上述问题的答案吧。

1. 依赖自动注入

本章节我们先讨论一下Spring的依赖自动注入的方式,虽然自动注入平时我们使用的并不多,但是在Spring内部和一些特定场景下,使用起来还是很方便的,我们先简单了解一下。

1.1 依赖注入模型

首先我们来看一下属性的自动注入类型有哪些,Spring默认的注入模型是AUTOWIRE_NO,这点我们通过 AbstractBeanDefinition的属性 private int autowireMode = AUTOWIRE_NO可以知道,也就是不自动注入。除了AUTOWIRE_NO,Spring还提供了AUTOWIRE_BY_NAME(通过名称注入)、AUTOWIRE_BY_TYPE(通过类型注入)、AUTOWIRE_CONSTRUCTOR(通过构造器注入)等多种自动注入模型。

不过我们使用最多的@AutoWired注入,却不属于自动注入模型的范围,因为它需要我们手动增加@AutoWired注解来驱动属性的注入,所以它本质上是手动注入,或者称为半自动注入更合适,因为毕竟我们只加了注解,属性查找、注入的逻辑等核心逻辑还是Spring自动完成的。下面我们对注入模型进行一下总结:

聊透Spring依赖注入

  1. 依赖注入可划分为自动注入和非自动注入两种。自动注入基于构造器、setter方法注入,非自动注入基于属性注入。
  2. 自动注入需要有对应的构造器或者setter方法,作为回调注入的入口,不存在则注入失败。默认的注入模型为AUTOWIRE_NO,需要在bean实例化前修改注入模型,才能使自动注入生效。
  3. @AutoWired、@Resource等注解驱动的属性注入,本质上属于非自动注入。由于注入时使用反射注入,无法保证属性注入的顺序。

1.2 依赖自动注入

现在我们对注入模型有了初步的了解,知道了Spring是支持自动注入的,只不过需要修改注入模型才能生效。那什么时机修改,怎么修改才能生效呢?首先,必须在bean实例化之前就要修改,如果bean已经实例化了,属性已经注入了,再修改是无法生效的。修改方式则是通过BeanDefinition提供的setAutowireMode()直接设置。基于这两点我们猜测一下,在BeanFactoryPostProcessor#postProcessBeanFactory()的后置处理器中获取BeanDefinition修改,应该是可以生效的,我们试一下:

@Component
public class A {
    private B b;

    public A(){
      System.out.println("无参数的构造方法");
    }
   
    public A(B b){
        System.out.println("只有B的构造方法");
    }

    public void setB(B b){
      System.out.println("set方法注入b");
    }

    public B getB() {
       return b;
    }
}
// 默认的注入模型是不注入,所以在构造方法推断的时候,直接采用无参数的构造方法
@Test
public void defaultModel(){
    AnnotationConfigApplicationContext
          context = new AnnotationConfigApplicationContext("com.autoModel.defaults");
    A a = context.getBean(A.class);
    System.out.println(a.getB());
}
// 修改前输出信息
// 无参数的构造方法
// null

首先我们看一下,未修改采用默认的注入模型AUTOWIRE_NO时,是无法自动注入属性b的,此时输出内容为null,即默认不注入属性。

// 通过BeanFactoryPostProcessor修改bean的注入模型
public class AutowiredByTypeBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
   @Override
   public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
      AbstractBeanDefinition beanDefinition = (AbstractBeanDefinition)beanFactory.getBeanDefinition("a");
      beanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
   }
}

@Test
public void autowiredByType(){
   AnnotationConfigApplicationContext
         context = new AnnotationConfigApplicationContext();
   context.register(A.class, B.class);
   context.register(AutowiredByTypeBeanFactoryPostProcessor.class);
   context.refresh();
   A a = context.getBean(A.class);
   System.out.println(a);
}
// 输出信息
//无参数的构造方法
// set方法注入b
// com.autoModel.defaults.A@3cc1435c

现在我们通过AutowiredByTypeBeanFactoryPostProcessor,先获取a的BeanDefinition,然后修改为AUTOWIRE_BY_TYPE,并提供setB()方法。通过输出发现是可以自动注入属性b的,原理是通过类型B在容器中进行查找,将符合条件的bean注入给该属性,具体逻辑在后续章节详细展开。

对于AUTOWIRE_BY_NAME,是基于bean名称去Spring容器中查找、匹配、注入的。而AUTOWIRE_CONSTRUCTOR则相对复杂一些,因为可能存在多个构造方法,Spring会根据参数个数、类型等多个方面进行综合评分,最后推断构造方法进行实例化和依赖注入。这里我们不详细展开,感兴趣的小伙伴可以自行查阅资料。

2 @AutoWired依赖注入

@AutoWired可以说是我们日常使用最多的Spring注解之一了,本章节我们一起通过源码解读的方式探索一下他的实现机制和注入逻辑。Spring对@AutoWired的处理大致分为解析 -> 查找 -> 注入 三个核心过程,下面我们顺着这个思路,一起看一下@AutoWired是怎样完成依赖注入的。

2.1 @AutoWired注入属性的解析

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args){
    try {
       // 通过后置处理器,提供一个:修改合并后的BeanDefinition的机会
       applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
    }
    // ...省略部分代码
}

protected void applyMergedBeanDefinitionPostProcessors(RootBeanDefinition mbd, Class<?> beanType, String beanName) {
   for (BeanPostProcessor bp : getBeanPostProcessors()) {
      if (bp instanceof MergedBeanDefinitionPostProcessor) {
         MergedBeanDefinitionPostProcessor bdp = (MergedBeanDefinitionPostProcessor) bp;
         // AutowiredAnnotationBeanPostProcessor完成@Autowired注解的查找
         // CommonAnnotationBeanPostProcessor完成@Resource注解的查找
         bdp.postProcessMergedBeanDefinition(mbd, beanType, beanName);
      }
   }
}

//AutowiredAnnotationBeanPostProcessor.java
private InjectionMetadata buildAutowiringMetadata(Class<?> clazz) {
   // do while是为了查找父类
   do {
      // 存储当前类中加了@Autowired 的属性或者方法
      final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();

      // 1: 查找所有加了@Autowire的属性,封装成 AutowiredFieldElement
      ReflectionUtils.doWithLocalFields(targetClass, field -> {
         MergedAnnotation<?> 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字段的值,默认为true
            boolean required = determineRequiredStatus(ann);
            currElements.add(new AutowiredFieldElement(field, required));
         }
      });

      // 2: 查找所有加了@Autowire的方法,封装成 AutowiredMethodElement
      ReflectionUtils.doWithLocalMethods(targetClass, method -> {
         Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
         if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
            return;
         }
         MergedAnnotation<?> ann = findAutowiredAnnotation(bridgedMethod);
         if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
            if (Modifier.isStatic(method.getModifiers())) {
               if (logger.isInfoEnabled()) {
                  logger.info("Autowired annotation is not supported on static methods: " + method);
               }
               return;
            }
            // 如果方法的参数个数为0,没办法注入响应的属性信息,会有错误日志的打印
            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);
            currElements.add(new AutowiredMethodElement(method, required, pd));
         }
      });

      elements.addAll(0, currElements);
      targetClass = targetClass.getSuperclass();
   }
   // 获取父类,while循环,解析父类上的注解信息
   while (targetClass != null && targetClass != Object.class);

   // 3: 封装成一个注入器
   return InjectionMetadata.forElements(elements, clazz);
}

通过源码分析,我们发现:在Spring创建bean的时候,通过applyMergedBeanDefinitionPostProcessors()完成了后置处理器MergedBeanDefinitionPostProcessor#postProcessMergedBeanDefinition()的调用,该类型的后置处理器,提供了修改合并BeanDefinition的机会。其中AutowiredAnnotationBeanPostProcessor在该拓展点完成@Autowired注解的查找 ,CommonAnnotationBeanPostProcessor在该拓展点完成@Resource注解的查找,并进行了缓存。便于后续属性填充时直接使用。

查找的逻辑在源码中也直观的体现了出来,主要借助反射机制,遍历属性和方法,判断是否存在@Autowire的,具体流程如下:

  1. 先通过反射查找所有加了@Autowire的属性,封装成 AutowiredFieldElement对象,放入currElements集合中;
  2. 再通过反射查找所有加了@Autowire的方法,封装成AutowiredMethodElement,也放入currElements集合中;
  3. 将目标类置为父类,查找父类中加了@Autowire的属性和方法,一并放入currElements集合中;
  4. 将查找到的集合封装为InjectionMetadata,方便后续进行属性查找使用。
聊透Spring依赖注入

2.2 @AutoWired注入对象的查找

注入对象的查找是@AutoWired的核心功能,@AutoWired的功能也很强大,不仅支持单值类型属性的注入,还支持集合属性注入排序、自动推断注入对象等逻辑,下面我们一起探究一下如此强大的功能是如何实现的。  

首先我们@AutoWired注入的核心逻辑,在源码的注释中我们对照源码标注了超找流程,小伙伴们认真阅读哦。

public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
      @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
   InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);
   try {
      // 获取依赖的类型,根据类型查找,这个就是要查找的类型
      Class<?> type = descriptor.getDependencyType();
       // ...省略部分代码

      //1:是否支持多个bean注入,也就是集合类型属性(List<Filter> filters)。如果支持,直接返回注入类型的Bean集合
      Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
      if (multipleBeans != null) {
         return multipleBeans;
      }

      // 2: 根据类型查找所有的候选bean信息,key为beanName,value为bean对应的class类型
      Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
      // 处理类型查找到候选bean为空的情况,通常直接返回null
      if (matchingBeans.isEmpty()) {
         if (isRequired(descriptor)) {
            raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
         }
         return null;
      }

      String autowiredBeanName;
      Object instanceCandidate;

      // 3. 根据类型找出多个候选bean,但是字段只支持单个bean注入,需要推断选择一个
      if (matchingBeans.size() > 1) {
          // 3.1 从多个bean中选择一个注入
         autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
         instanceCandidate = matchingBeans.get(autowiredBeanName);
      }
      else {
         //4:根据类型找出一个候选者,字段也是单个bean注入,直接赋值
         Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();
         autowiredBeanName = entry.getKey();
         instanceCandidate = entry.getValue();
      }

      // 5:将选择注入的bean实例化,返回进行注入
      if (instanceCandidate instanceof Class) {
         instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);
      }
      Object result = instanceCandidate;
      return result;
   }
   finally {
      ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint);
   }
}
聊透Spring依赖注入

2.2.1 注入列表属性时,如何控制列表顺序

private Object resolveMultipleBeans(DependencyDescriptor descriptor, @Nullable String beanName,      @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) {    // ...省略部分代码    else if (Collection.class.isAssignableFrom(type) && type.isInterface()) {       Class<?> elementType = descriptor.getResolvableType().asCollection().resolveGeneric();       if (elementType == null) {          return null;       }       // 1: 根据类型进行查找bean,key是bean名称,value是bean实例       Map<String, Object> matchingBeans = findAutowireCandidates(beanName, elementType, new MultiElementDescriptor(descriptor));       // ...省略部分代码       Object result = converter.convertIfNecessary(matchingBeans.values(), type);       // 对结果数据进行排序,排序规则:       // 1: 优先按照PriorityOrdered定义的顺序进行排序       // 2: 其次按照Ordered定义的顺序进行排序       // 3: 最后按照@Order定义的顺序进行排序       if (result instanceof List) {          if (((List<?>) result).size() > 1) {             // Comparator是AnnotationAwareOrderComparator             Comparator<Object> comparator = adaptDependencyComparator(matchingBeans);             if (comparator != null) {                ((List<?>) result).sort(comparator);             }          }       }       return result;    }}

通过源码跟踪我们发现:返回的列表会通过PriorityOrdered、Ordered、@Order定义的顺序进行排序,并且优先级PriorityOrdered > Ordered > @Order,所以我们是完全可以通过在注入bean实现接口或者增加注解控制注入的顺序。

聊透Spring依赖注入

2.2.2 有多个实现的子类,是如何选择注入对象的

protected String determineAutowireCandidate(Map<String, Object> candidates, DependencyDescriptor descriptor) {   Class<?> requiredType = descriptor.getDependencyType();   // 1: 如果已经指定Primary Bean,直接返回指定的(在类上加@Primary注解)   String primaryCandidate = determinePrimaryCandidate(candidates, requiredType);   if (primaryCandidate != null) {      return primaryCandidate;   }   // 2: 根据优先级返回优先级最高的(javax.annotation.Priority注解)   String priorityCandidate = determineHighestPriorityCandidate(candidates, requiredType);   if (priorityCandidate != null) {      return priorityCandidate;   }   // 3: bean的名称和字段名字匹配,就返回这个   for (Map.Entry<String, Object> entry : candidates.entrySet()) {      String candidateName = entry.getKey();      Object beanInstance = entry.getValue();      if ((beanInstance != null && this.resolvableDependencies.containsValue(beanInstance)) ||            matchesBeanName(candidateName, descriptor.getDependencyName())) {         return candidateName;      }   }   return null;

通过源码跟踪我们发现:当根据类型查找到多个候选bean,但是属性值要求注入单一bean的时候,会通过下面逻辑选择注入对象:

  1. 会先在候选者查找添加了@Primary的注入;
  2. 如果不存在标注了@Primary的候选者,会再次查找标注了@Priority的候选者,挑出优先级最高的进行注入;
  3. 如果前两者都没有满足条件的,会通过属性名和候选者的beanName匹配,如果有bean的beanName和属性名相同,就注入该bean;
  4. 都不匹配,返回null。

看到这里小伙伴们是不是惊奇的发现,以类型注入为第一优先级的@AutoWired,原来也会考虑属性名称的影响,在@Primary和@Priority都不存在的情况下,会优先注入beanName和属性名相等bean,所以,我们是可以直接通过属性名,控制注入的bean实现的。

聊透Spring依赖注入

  • 判断是否加了@Primary注解
// ClassPathBeanDefinitionScanner#doScan() -> AnnotationConfigUtils.processCommonDefinitionAnnotations() static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd, AnnotatedTypeMetadata metadata) {   // ... 解析@Lazy注解   // 解析@Primary注解   if (metadata.isAnnotated(Primary.class.getName())) {      abd.setPrimary(true);   }}

2.3 @AutoWired属性值的注入

 通过上一章节的分析,我们已经知道属性是如何查找的了,Spring采用的是懒加载的实现,在注入的时候,才会去驱动注入对象的查找。本章节我们看一下Spring是何时进行属性注入的。

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args){
    try {
       // 通过后置处理器,提供一个:修改合并后的BeanDefinition的机会
       applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
    }
    // ...省略部分代码
    try {
        // 属性填充
        populateBean(beanName, mbd, instanceWrapper);
    }
    // ...省略部分代码
}

protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
    // ...省略部分代码
    for (BeanPostProcessor bp : getBeanPostProcessors()) {
       if (bp instanceof InstantiationAwareBeanPostProcessor) {
          InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
          //AutowiredAnnotationBeanPostProcessor完成@Autowired注入
          //CommonAnnotationBeanPostProcessor完成@Resource注入
          PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
          // ...省略部分代码
       }
    }
}
//AutowiredAnnotationBeanPostProcessor.java
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
    // ...省略部分代码
    try {
        // 执行注入元素自己的inject方法
        metadata.inject(bean, beanName, pvs);
    }
    // ...省略部分代码
}

// InjectionMetadata.java
public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
    // ...省略部分代码
    // 解析阶段保存的数据,,这里就是AutowiredFieldElement和AutowiredMethodElement
    for (InjectedElement element : elementsToIterate) {
     // 执行注入逻辑
     element.inject(target, beanName, pvs);
    }
}

//AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement.java 内部类
protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
   Field field = (Field) this.member;
   // ...省略部分代码
   else {
      // 1: 查找属性值
      value = resolveFieldValue(field, bean, beanName);
   }
   if (value != null) {
      ReflectionUtils.makeAccessible(field);
      // 2: 反射将属性值注入
      field.set(bean, value);
   }
}

通过源码跟踪我们发现:在Spring实例化bean的时候,有个单独的阶段populateBean(),该阶段会直接对解析阶段解析好的属性和方法进行注入。注入前先去Spring容器查找合适的bean,然后通过反射进行注入。

聊透Spring依赖注入

2.4 配合@Qualifier实现名称查找

protected Map<String, Object> findAutowireCandidates(      @Nullable String beanName, Class<?> requiredType, DependencyDescriptor descriptor) {   // 通过类型在BeanFactory中找出符合条件的bean的名称   String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(         this, requiredType, true, descriptor.isEager());   // 遍历判断是否符合条件,包括对@Qualifier注解的判断   for (String candidate : candidateNames) {      if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, descriptor)) {         addCandidateEntry(result, candidate, descriptor, requiredType);      }   }}//QualifierAnnotationAutowireCandidateResolver.java// bdHolder 查找出来的候选bean的定义信息;annotationsToSearch 字段上注解信息protected boolean checkQualifiers(BeanDefinitionHolder bdHolder, Annotation[] annotationsToSearch) {    for (Annotation annotation : annotationsToSearch) {       Class<? extends Annotation> type = annotation.annotationType();       // 判断字段是否标注了@Qualifier注解       if (isQualifier(type)) {          // 判断当前bean,是否与注解value匹配          if (!checkQualifier(bdHolder, annotation, typeConverter)) {             fallbackToMeta = true;          }       }    }}protected boolean checkQualifier(      BeanDefinitionHolder bdHolder, Annotation annotation, TypeConverter typeConverter) {    // 获取@Qualifier注解的属性信息    Map<String, Object> attributes = AnnotationUtils.getAnnotationAttributes(annotation);for (Map.Entry<String, Object> entry : attributes.entrySet()) {   // 注解的属性名,@Qualifier获取到的为value   String attributeName = entry.getKey();   // 注解的属性值,@Qualifier获取到的为需要注入的beanName   Object expectedValue = entry.getValue();   Object actualValue = null;   // ...省略部分代码   // 当前actualValue为null,且属性名为value,属性值(userService)是String类型,所以重点需要判断候选者的beanName是否和@Qualifier的value字段的属性值相等,相等则条件成立,直接返回true   if (actualValue == null && attributeName.equals(AutowireCandidateQualifier.VALUE_KEY) &&         expectedValue instanceof String && bdHolder.matchesName((String) expectedValue)) {      // Fall back on bean name (or alias) match      continue;   }   return true;}

通过源码跟踪我们发现:在通过类型获取可注入的候选bean时,会判断注入字段是否存在@Qualifier注解,如果存在,会根据候选bean的beanName和@Qualifier的value属性匹配,匹配成功才会加入候选者列表。

聊透Spring依赖注入

在注入对象查找这一章节,我们分析过注入属性的选择逻辑,如果将属性名命名为需要注入的bean的beanName,也可以达到相同的效果,只是优先级较低,在Spring源码注释中,被定义为降级逻辑,有可能执行不到这个选择逻辑。另外我们从可拓展性的角度出发,通常采用面向接口编程,建议使用@Qualifier指定通过名称查找。

3 @Resource依赖注入

3.1 @Resourced注入属性的解析

@Resource的触发时机和执行逻辑和@AutoWired一致,只是执行解析逻辑的BeanPostProcessor不同,解析@Resource注解的是:CommonAnnotationBeanPostProcessor,而解析@AutoWired注解的是:AutowiredAnnotationBeanPostProcessor。虽然解析类不同,但是解析逻辑是一致的,通过反射查找bean中标注了@Resource的属性和方法,封装成ResourceElement,方便后续查找注入。我们一起来看一下:

private InjectionMetadata buildResourceMetadata(Class<?> clazz) {

   // do...while为了解析父类
   do {
      // 存储当前类中加了@Resource 的属性或者方法
      final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();

      // 1: 解析所有加了@Resource的属性,构成了ResourceElement对象
      ReflectionUtils.doWithLocalFields(targetClass, field -> {
         // ...省略部分代码
         else if (field.isAnnotationPresent(Resource.class)) {
            if (Modifier.isStatic(field.getModifiers())) {
               throw new IllegalStateException("@Resource annotation is not supported on static fields");
            }
            if (!this.ignoredResourceTypes.contains(field.getType().getName())) {
               currElements.add(new ResourceElement(field, field, null));
            }
         }
      });

      // 2: 解析所有加了@Resource的方法,构成了ResourceElement对象
      ReflectionUtils.doWithLocalMethods(targetClass, method -> {
         Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
         if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
            return;
         }
         if (method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
             // ...省略部分代码
            else if (bridgedMethod.isAnnotationPresent(Resource.class)) {
               if (Modifier.isStatic(method.getModifiers())) {
                  throw new IllegalStateException("@Resource annotation is not supported on static methods");
               }
               Class<?>[] paramTypes = method.getParameterTypes();
               if (paramTypes.length != 1) {
                  throw new IllegalStateException("@Resource annotation requires a single-arg method: " + method);
               }
               if (!this.ignoredResourceTypes.contains(paramTypes[0].getName())) {
                  PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
                  currElements.add(new ResourceElement(method, bridgedMethod, pd));
               }
            }
         }
      });

      elements.addAll(0, currElements);
      targetClass = targetClass.getSuperclass();
   }
   while (targetClass != null && targetClass != Object.class);
   // 3: 封装成一个注入器
   return InjectionMetadata.forElements(elements, clazz);
}
聊透Spring依赖注入

3.2 @Resourced注入对象的查找

protected Object autowireResource(BeanFactory factory, LookupElement element, @Nullable String requestingBeanName)      throws NoSuchBeanDefinitionException {   Object resource;   Set<String> autowiredBeanNames;   String name = element.name;   if (factory instanceof AutowireCapableBeanFactory) {      AutowireCapableBeanFactory beanFactory = (AutowireCapableBeanFactory) factory;      DependencyDescriptor descriptor = element.getDependencyDescriptor();      // 1: 如果条件成立,使用@Autowired查找属性值那一套逻辑      if (this.fallbackToDefaultTypeMatch && element.isDefaultName && !factory.containsBean(name)) {         autowiredBeanNames = new LinkedHashSet<>();         resource = beanFactory.resolveDependency(descriptor, requestingBeanName, autowiredBeanNames, null);         if (resource == null) {            throw new NoSuchBeanDefinitionException(element.getLookupType(), "No resolvable resource object");         }      }      else {         // 2:通过名字去spring容器中,获取一个对象进行注入         resource = beanFactory.resolveBeanByName(name, descriptor);         autowiredBeanNames = Collections.singleton(name);      }   }   // ...省略部分代码   return resource;}

通过源码我们发现:if (this.fallbackToDefaultTypeMatch && element.isDefaultName && !factory.containsBean(name)) 这个条件是决定查找方式的关键,如果判断条件成立:会使用和@Autowired完全一致的逻辑进行属性值得查找;如果条件不成立:只会通过名字去容器中查找。既然它如此关键,我们先来分解一下这个判断的条件:

fallbackToDefaultTypeMatch: 默认为true;
element.isDefaultName: @Resource没有指定name的话,就位true;
!factory.containsBean(name): 判断spring容器中,是否有这个名字的bean

其实这里的判断的核心逻辑是: 有没有在@Resource中提供名称。我们一起梳理一下这个逻辑:

  1. 如果提供名称了,直接拿着指定的名称,去spring容器看该名称是否存在;
  2. 如果没有提供名称,就把属性名当做指定的名称,去spring容器看该名称是否存在;
  3. spring容器中如果存在指定的名称,就根据名称把bean取出来,作为反射注入的属性值。
  4. spring容器中根据名称查找不到,就使用属性的类型去找,查找逻辑和@Autowired相同。
聊透Spring依赖注入

通过分析我们可以得出结论:@Resource以名称为查找第一优先级,如果提供了名称,则只会根据名称查找,无论能不能找到。这也好理解,因为你已经指定了嘛,找不找的到都要按照你指定的来找才对啊。

但是如果你没有指定,他也会遵循以名称为查找第一优先级的原则,将属性名作为名称尝试进行查找,只是这里还有一个必要条件,就是根据属性名在Spring容器中能查找到,确实是在秉承以名称为查找第一优先级的原则的同时,也保证的查找的准确性。

最后@Resource还提供了兜底逻辑,如果根据属性名找不到可注入对象,会通过类型进行查找,查找逻辑和@Autowired相同,忘记的小伙伴自行查阅哦。

3.3 @Resource属性值的注入

protected void inject(Object target, @Nullable String requestingBeanName, @Nullable PropertyValues pvs)
      throws Throwable {
   if (this.isField) {
      Field field = (Field) this.member;
      ReflectionUtils.makeAccessible(field);
      // getResourceToInject 找到需要注入的对象
      field.set(target, getResourceToInject(target, requestingBeanName));
   }

@Resource属性值的注入,和@Autowired一样,都是采用反射直接注入的,时机和流程也完全一致,这里我们不重复啰嗦,忘记的小伙伴自行查阅哦。

3.4 @Resource和@Autowired依赖注入的区别

  • 注入原则不同

@Resource以名称为注入第一优先级,会最大程度的通过名称确定注入对象,如果名称实在不能确定注入对象,会使用类型操作。

@Autowired先根据类型查找注入对象,查找不到会报错;查找一个直接注入;如果查找到多个,会考虑属性名确定注入对象。

  • 依赖注入的支持不同

@Autowired支持:属性注入、构造方法注入、Setter注入

@Resource 只支持属性注入和 Setter 注入

  • 来源不同

@Autowired是Spring定义的注解,而@Resource是Java定义的注解,它来自于JSR-250,支持的范围更广。

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/57150.html

(0)
上一篇 2024-05-01 10:33
下一篇 2024-05-08 12:26

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信