Spring-Bean的字段填充阶段处理


​ 对CommonAnnotationBeanPostProcessor和AutowiredAnnotationBeanPostProcessor从源码分析了其依赖的解析、创建流程

CommonAnnotationBeanPostProcessor

​ 专用来处理非Srping官方提供,而是javax的通用如下注解:

  • @javax.annotation.Resource
  • @javax.annotation.PostConstruct
  • @javax.annotation.PreDestroy
  • @javax.ejb.EJB
  • @javax.xml.ws.WebServiceRef

其主要有两个重要过程,一是提取这些注解所在字段或方法的元数据。之后才是使用这些注解

其架构如下图所示。1被用在解析注解所在字段的元数据,2则是使用这些注解的处理器接口,3则表示它也具备Init和Destory注解解析的功能

CommonAnnotationBeanPostProcessor架构

CommonAnnotationBeanPostProcessor架构

解析Field和方法


public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
      // 调用父接口,解析@PostConstruct和@PreDestroy相关方法
    super.postProcessMergedBeanDefinition(beanDefinition, beanType, beanName);
        // 解析其余的注解
    InjectionMetadata metadata = findResourceMetadata(beanName, beanType, null);
    metadata.checkConfigMembers(beanDefinition);
}

private InjectionMetadata findResourceMetadata(String beanName, final Class<?> clazz, @Nullable PropertyValues pvs) {
    // 构建当前bean的缓存key,用于存放这个bean的所有依赖
        String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
        // 先从injectionMetadataCache缓存中拿,没有再构造,最后放入缓存
        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 = buildResourceMetadata(clazz);
                    this.injectionMetadataCache.put(cacheKey, metadata);
                }
            }
        }
        return metadata;
    }
// 开始解析@WebServiceRef,@EJB和@Resource
// 这里面任意一个解析后都会进行Modifier.isStatic判断。可知静态字段或方法不支持自动注入,直接抛出了异常
private InjectionMetadata buildResourceMetadata(final Class<?> clazz) {
        List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
        Class<?> targetClass = clazz;

        do {
            final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();

      // 遍历这个class的所有field,依次对每个field进行处理
            ReflectionUtils.doWithLocalFields(targetClass, field -> {
                // @WebServiceRef注解处理
                if (webServiceRefClass != null && field.isAnnotationPresent(webServiceRefClass)) {
                    if (Modifier.isStatic(field.getModifiers())) {
                        throw new IllegalStateException("@WebServiceRef annotation is not supported on static fields");
                    }
                    currElements.add(new WebServiceRefElement(field, field, null));
                }
                // @EJB注解处理
                else if (ejbRefClass != null && field.isAnnotationPresent(ejbRefClass)) {
                    if (Modifier.isStatic(field.getModifiers())) {
                        throw new IllegalStateException("@EJB annotation is not supported on static fields");
                    }
                    currElements.add(new EjbRefElement(field, field, null));
                }
                // @Resource注解处理
                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));
                    }
                }
            });

      // 再遍历这个class的所有method,处理以set方法进行注入的方式
            ReflectionUtils.doWithLocalMethods(targetClass, method -> {
                Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
                if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
                    return;
                }
                if (method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
                    if (webServiceRefClass != null && bridgedMethod.isAnnotationPresent(webServiceRefClass)) {
                        if (Modifier.isStatic(method.getModifiers())) {
                            throw new IllegalStateException("@WebServiceRef annotation is not supported on static methods");
                        }
                        if (method.getParameterCount() != 1) {
                            throw new IllegalStateException("@WebServiceRef annotation requires a single-arg method: " + method);
                        }
                        PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
                        currElements.add(new WebServiceRefElement(method, bridgedMethod, pd));
                    }
                    else if (ejbRefClass != null && bridgedMethod.isAnnotationPresent(ejbRefClass)) {
                        if (Modifier.isStatic(method.getModifiers())) {
                            throw new IllegalStateException("@EJB annotation is not supported on static methods");
                        }
                        if (method.getParameterCount() != 1) {
                            throw new IllegalStateException("@EJB annotation requires a single-arg method: " + method);
                        }
                        PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
                        currElements.add(new EjbRefElement(method, bridgedMethod, pd));
                    }
                    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);

        return new InjectionMetadata(clazz, elements);
    }

ResourceElement源码

​ 以@Resource注解解析后的数据ResourceElement为例,可以发现beanName默认为@Resource的name字段。如果没有则为field字段(注解在field上)。或者则去掉前面的set,再将下个字母变为小写作为beanName(注解在set方法上)

private class ResourceElement extends LookupElement {

    /**
     * 存在@Lazy注解,且value为true,这个值就为true
     */
    private final boolean lazyLookup;

    public ResourceElement(Member member, AnnotatedElement ae, @Nullable PropertyDescriptor pd) {
       super(member, pd);
       Resource resource = ae.getAnnotation(Resource.class);
       // @Resource的name字段
       String resourceName = resource.name();
       Class<?> resourceType = resource.type();
       this.isDefaultName = !StringUtils.hasLength(resourceName);
       if (this.isDefaultName) { // name字段为空
          // 先获取这个字段名
          resourceName = this.member.getName();
          if (this.member instanceof Method && resourceName.startsWith("set") && resourceName.length() > 3) { // 注解在set方法上,则去掉前面的set,再将下个字母变为小写作为beanName
             resourceName = Introspector.decapitalize(resourceName.substring(3));
          }
       }
       else if (embeddedValueResolver != null) { // 支持占位符解析
          resourceName = embeddedValueResolver.resolveStringValue(resourceName);
       }
       if (Object.class != resourceType) {
          checkResourceType(resourceType);
       }
       else {
          // No resource type specified... check field/method.
          resourceType = getResourceType();
       }
       // 默认字段名
       this.name = (resourceName != null ? resourceName : "");
       // 默认当前字段的Class类型
       this.lookupType = resourceType;
       String lookupValue = resource.lookup();
       this.mappedName = (StringUtils.hasLength(lookupValue) ? lookupValue : resource.mappedName());
       Lazy lazy = ae.getAnnotation(Lazy.class);
       this.lazyLookup = (lazy != null && lazy.value());
    }

   /**
           字段注入方法,如果需要懒加载,则会创建一个代理对象
   */
    @Override
    protected Object getResourceToInject(Object target, @Nullable String requestingBeanName) {
       return (this.lazyLookup ? buildLazyResourceProxy(this, requestingBeanName) :
             getResource(this, requestingBeanName));
    }
}

字段注入

bean的populateBean调用了InstantiationAwareBeanPostProcessor#postProcessProperties方法,开始字段的注入

CommonAnnotationBeanPostProcessor#postProcessProperties

public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
        // 找出上一步缓存的元数据
    InjectionMetadata metadata = findResourceMetadata(beanName, bean.getClass(), pvs);
    try {
      // 开始注入
       metadata.inject(bean, beanName, pvs);
    }
    catch (Throwable ex) {
       throw new BeanCreationException(beanName, "Injection of resource dependencies failed", ex);
    }
    return pvs;
}

InjectionMetadata.InjectedElement#inject

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实现,获取对应的bean。会触发依赖bean的创建
       // 依赖bean创建完毕并返回后,再将其赋予给field
       field.set(target, getResourceToInject(target, requestingBeanName));
    }
    else { // set方法注入
       if (checkPropertySkipping(pvs)) {
          return;
       }
       try {
          Method method = (Method) this.member;
          ReflectionUtils.makeAccessible(method);
          method.invoke(target, getResourceToInject(target, requestingBeanName));
       }
       catch (InvocationTargetException ex) {
          throw ex.getTargetException();
       }
    }
}

总结

CommonAnnotationBeanPostProcessor 是标准注解(@Resource/@PostConstruct/@PreDestroy)的解析者,通过元数据提前解析+生命周期钩子回调,实现了依赖注入和生命周期管理。

  • CommonAnnotationBeanPostProcessor#postProcessMergedBeanDefinition:负责扫描 @Resource@PostConstruct@PreDestroy 注解,把解析结果封装成 InjectionMetadata(字段、方法对应的 ResourceElement 等),再缓存下来,等待后续注入或生命周期回调使用
  • CommonAnnotationBeanPostProcessor#postProcessProperties:负责根据解析好的 InjectionMetadata 真正进行注入
  • InitDestroyAnnotationBeanPostProcessor#postProcessBeforeInitialization: 在 Bean 初始化前,调用所有 @PostConstruct 标注的方法
    • InitDestroyAnnotationBeanPostProcessor#postProcessBeforeDestruction:在 Bean 销毁前,调用所有 @PreDestroy 标注的方法。

AutowiredAnnotationBeanPostProcessor

根据构造方法可知,它专用来处理Spring提供的这些注解

public AutowiredAnnotationBeanPostProcessor() {
      // @Autowired支持
    this.autowiredAnnotationTypes.add(Autowired.class);
      // @Value支持
    this.autowiredAnnotationTypes.add(Value.class);
    try {
       // javax提供的@Value支持
       this.autowiredAnnotationTypes.add((Class<? extends Annotation>)
             ClassUtils.forName("javax.inject.Value", 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.
    }
}
  • @org.springframework.beans.factory.annotation.Autowired
  • @org.springframework.beans.factory.annotation.Value
  • @javax.inject.Inject

​ 整体流程和上面的CommonAnnotationBeanPostProcessor差不多。都是在postProcessMergedBeanDefinition方法里进行bean内部有上诉3个注解的字段或方法进行解析并缓存。然后在postProcessProperties进行依赖注入