Spring 依赖注入[2]

122 阅读4分钟

「这是我参与11月更文挑战的第10天,活动详情查看:2021最后一次更文挑战

Autowired注解(不带指定类型)

这部分就对应这里了:

boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);
​
PropertyDescriptor[] filteredPds = null;
if (hasInstAwareBpps) {
   if (pvs == null) {
      pvs = mbd.getPropertyValues();
   }
   for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
      PropertyValues pvsToUse = bp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
      if (pvsToUse == null) {
         if (filteredPds == null) {
            filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
         }
         pvsToUse = bp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
         if (pvsToUse == null) {
            return;
         }
      }
      pvs = pvsToUse;
   }
}

这里:

  • autowired注解,是通过AutowiredAnnotationBeanPP实现的。
  • resource注解,是通过CommonAnnotationBeanPP实现的。

这里就看看autowired的:

AutowiredAnnotationBeanPP

public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationAwareBeanPostProcessor,
      MergedBeanDefinitionPostProcessor, PriorityOrdered, BeanFactoryAware {}
  • SmartInstantiationAwareBeanPostProcessor接口简单来说就是更强大的InstantiationAwareBeanPostProcessor:

    • 推断构造方法
    • 解决循环依赖
    • 处理属性

注入之前

同时,这个PP也继承了MergedBeanDefinitionPostProcessor,也就是说会在bean属性注入之前被调用一次

postProcessMergedBeanDefinition方法:

@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
   InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null);
   metadata.checkConfigMembers(beanDefinition);
}

这里会将bean中需要注入的值(注入点,被autowired修饰的方法或者属性)先找出来并做缓存:

  • 查找注入点
private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {
   // Fall back to class name as cache key, for backwards compatibility with custom callers.
   String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
   // Quick check on the concurrent map first, with minimal locking.
   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;
}
  • 构造注入点相关信息
private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
    //在找注入点之前,会判断:当前的类是否是候选类【1】
   if (!AnnotationUtils.isCandidateClass(clazz, this.autowiredAnnotationTypes)) {
      return InjectionMetadata.EMPTY;
   }
​
   List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
   Class<?> targetClass = clazz;
​
   do {
      final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();
​
       //找字段
      ReflectionUtils.doWithLocalFields(targetClass, field -> {
          //查找是否有对应的注解(autowired,value,inject)
         MergedAnnotation<?> ann = findAutowiredAnnotation(field);
         if (ann != null) {
             //这里需要注意:如果是static的字段就直接return了
            if (Modifier.isStatic(field.getModifiers())) {
               if (logger.isInfoEnabled()) {
                  logger.info("Autowired annotation is not supported on static fields: " + field);
               }
               return;
            }
             //针对autowired(require = true),默认就是true
             //这里就代表是否一定要有这个bean,没有则报错;如果是false,则不会
            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;
         }
         MergedAnnotation<?> 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);
            currElements.add(new AutowiredMethodElement(method, required, pd));
         }
      });
​
      elements.addAll(0, currElements);
       //这里会再往上查找父类的
      targetClass = targetClass.getSuperclass();
   }
   while (targetClass != null && targetClass != Object.class);
​
    //最后再把字段和方法,封装并返回
   return InjectionMetadata.forElements(elements, clazz);
}
  • 什么时候类不需要有注入点?

    • 就是上面所说的基础类型
  • 为什么不给static赋值?

    • 如果是原型bean,那么对应的多个实例这部分的属性会导致同一个bean的同一个static属性,在不同的时候获取到的是不同的,会造成混乱
    • 如果实在是要为static属性赋值,可以通过set方法里为static赋值,并设置为单例bean来绕过。
  • 桥接方法 bridgedMethod

    • 这部分和继承实现相关
    • 如果接口是泛型,并且实现类指定了类型,那么编译后会出现两个同样的set方法但类型不同(一个是指定的类型,一个是Object,这里的Object被称为桥接方法)
    • 因此这里会找一下子类上被桥接的方法,如果是桥接方法就不会当作注入点

    注入过程调用

上面做完了,在Spring中后续会调用这个方法:

@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
   InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
   try {
      metadata.inject(bean, beanName, pvs);
   }
   catch (BeanCreationException ex) {
      throw ex;
   }
   catch (Throwable ex) {
      throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
   }
   return pvs;
}

在这里就是会给注入点来进行注入。

对应代码如下:

public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
   Collection<InjectedElement> checkedElements = this.checkedElements;
   Collection<InjectedElement> elementsToIterate =
         (checkedElements != null ? checkedElements : this.injectedElements);
   if (!elementsToIterate.isEmpty()) {
      for (InjectedElement element : elementsToIterate) {
         element.inject(target, beanName, pvs);
      }
   }
}
​
//值的inject,autowiredFieldMetadata
protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
            Field field = (Field) this.member;
            Object value;
            if (this.cached) {
                try {
                    value = resolvedCachedArgument(beanName, this.cachedFieldValue);
                }
                catch (NoSuchBeanDefinitionException ex) {
                    // Unexpected removal of target bean for cached argument -> re-resolve
                    value = resolveFieldValue(field, bean, beanName);
                }
            }
            else {
                value = resolveFieldValue(field, bean, beanName);
            }
    //底层还是通过反射来取值的
            if (value != null) {
                ReflectionUtils.makeAccessible(field);
                field.set(bean, value);
            }
        }
​
//方法的inject,autowiredMethodMetadata
        protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
            //这里就是判断是否已经手动给属性赋值了,但只在方法注入的时候判断了,并没有在属性注入的时候判断
            if (checkPropertySkipping(pvs)) {
                return;
            }
            Method method = (Method) this.member;
            Object[] arguments;
            if (this.cached) {
                try {
                    arguments = resolveCachedArguments(beanName);
                }
                catch (NoSuchBeanDefinitionException ex) {
                    // Unexpected removal of target bean for cached argument -> re-resolve
                    arguments = resolveMethodArguments(method, bean, beanName);
                }
            }
            else {
                arguments = resolveMethodArguments(method, bean, beanName);
            }
            //一样是反射
            if (arguments != null) {
                try {
                    ReflectionUtils.makeAccessible(method);
                    method.invoke(bean, arguments);
                }
                catch (InvocationTargetException ex) {
                    throw ex.getTargetException();
                }
            }
        }
  • 如果在BD中赋值,那么虽然字段部分的autowired不会忽略,但BD中的值会在autowired之后再执行,因此BD中的赋值会在后续中再盖上去。

这里的关键是resolveFieldValueresolveMethodArguments,实际上调用的是beanFactory.resolveDependency(currDesc, beanName, autowiredBeans, typeConverter)