Spring IoC容器初始化源码—populateBeaninitializeBean填充Bean字段反射和setter方法依赖注入以及IoC容器初始化总结四万字

Posted 刘Java

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring IoC容器初始化源码—populateBeaninitializeBean填充Bean字段反射和setter方法依赖注入以及IoC容器初始化总结四万字相关的知识,希望对你有一定的参考价值。

  基于最新Spring 5.x,详细介绍了bean的“初始化过程”,主要包括applyMergedBeanDefinitionPostProcessors、populateBean、initializeBean、registerDisposableBeanIfNecessary核心方法,最后对Spring IoC容器初始化进行了总结!

  上一篇文章:Spring IoC容器初始化源码(7)—createBean实例化Bean以及构造器自动注入中,我们主要讲解了ClassPathXmlApplicationContext IoC容器初始化的refresh方法中的createBean方法的全部流程以及该方法内部的createBeanInstance方法的源码——bean的创建以及基于构造器的自动注入的过程,即bean的“实例化”
  现在我们继续向下学习refresh()方法的中的后面的内容,接下来就是bean的“初始化”过程,主要知识点包括:applyMergedBeanDefinitionPostProcessors方法查找解析各种回调注解或者自动注入注解、populateBean方法填充bean实例、initializeBean方法对bean实例进行各种回调、registerDisposableBeanIfNecessary方法尝试注册bean销毁回调方法。最后,我们会对这几篇文章讲解的Spring IoC容器初始化源码进行简单的总结!

Spring IoC容器初始化源码 系列文章

Spring IoC容器初始化源码(1)—setConfigLocations设置容器配置信息

Spring IoC容器初始化源码(2)—prepareRefresh准备刷新、obtainFreshBeanFactory加载XML资源、解析<beans/>标签

Spring IoC容器初始化源码(3)—parseDefaultElement、parseCustomElement解析默认、扩展标签,registerBeanDefinition注册Bean定义

Spring IoC容器初始化源码(4)—<context:component-scan/>标签解析、spring.components扩展点、自定义Spring命名空间扩展点

Spring IoC容器初始化源码(5)—prepareBeanFactory、invokeBeanFactoryPostProcessors、registerBeanPostProcessors方法

Spring IoC容器初始化源码(6)—finishBeanFactoryInitialization实例化Bean的整体流程以及某些扩展点

Spring IoC容器初始化源码(7)—createBean实例化Bean的整体流程以及构造器自动注入

Spring IoC容器初始化源码(8)—populateBean、initializeBean填充Bean、字段反射和setter方法依赖注入以及IoC容器初始化总结【四万字】

< context:property-placeholder/>标签以及PropertySourcesPlaceholderConfigurer占位符解析器源码深度解析

三万字的ConfigurationClassPostProcessor配置类后处理器源码深度解析

基于JavaConfig的AnnotationConfigApplicationContext IoC容器初始化源码分析

文章目录


   我们接着上一篇文章继续向下讲doCreateBean方法在createBeanInstance之后的源码!

1 applyMergedBeanDefinitionPostProcessors应用MergedBeanDefinitionPostProcessor后处理器

  createBeanInstance实例化bean完毕之后,方法或者字段依赖注入之前,会接着调用applyMergedBeanDefinitionPostProcessors方法,正如方法名那样,这个方法将会查找全部MergedBeanDefinitionPostProcessor类型的后处理器,回调它们的postProcessMergedBeanDefinition方法,该方法可以改变已合并的bean定义,当然还能做更多的事情,最常见的就是解析字段和方法上的某些注解。
  很多的后处理器都是MergedBeanDefinitionPostProcessor的子类型,比如CommonAnnotationBeanPostProcessor、AutowiredAnnotationBeanPostProcessor、ApplicationListenerDetector这三个常见的后处理器,它们的postProcessMergedBeanDefinition方法都将在这里被依次回调,即在bean实例化之后被回调。

  1. CommonAnnotationBeanPostProcessor的postProcessMergedBeanDefinition方法最开始还会调用父类InitDestroyAnnotationBeanPostProcessor的同名方法用于处理方法上的@PostConstruct、@PreDestroy注解,随后自己处理字段和方法上的@WebServiceRef、@EJB、@Resource注解。
  2. AutowiredAnnotationBeanPostProcessor的postProcessMergedBeanDefinition方法用于处理方法和字段上的@Autowired、@Value、@Inject注解。
  3. ApplicationListenerDetector的postProcessMergedBeanDefinition方法用于记录ApplicationListener类型的bean是否是单例的,后面监听器过滤的时候才会用到。

  注意,在这一步(applyMergedBeanDefinitionPostProcessors方法)仅仅是简单解析这些注解,相当于查找并缓存起来,后面某些时候,比如依赖注入的时候会被再次调用,到时候将进一步深度解析它们的配置属性和功能。

/**
 * 将MergedBeanDefinitionPostProcessors后处理器应用于指定的 bean 定义,调用其postProcessMergedBeanDefinition方法。
 *
 * @param mbd      已合并的bean定义
 * @param beanType bean的类型
 * @param beanName beanName
 */
protected void applyMergedBeanDefinitionPostProcessors(RootBeanDefinition mbd, Class<?> beanType, String beanName) 
    //获取、遍历全部已注册的后处理器
    for (BeanPostProcessor bp : getBeanPostProcessors()) 
        //如果bp属于MergedBeanDefinitionPostProcessor
        if (bp instanceof MergedBeanDefinitionPostProcessor) 
            //那么强制转型
            MergedBeanDefinitionPostProcessor bdp = (MergedBeanDefinitionPostProcessor) bp;
            //回调postProcessMergedBeanDefinition方法,该方法可用于修改给定bean的已合并的RootBeanDefinition
            bdp.postProcessMergedBeanDefinition(mbd, beanType, beanName);
        
    

1.1 CommonAnnotationBeanPostProcessor.postProcessMergedBeanDefinition回调

  该方法首先会调用父类InitDestroyAnnotationBeanPostProcessor的同名方法用于处理PostConstruct、PreDestroy注解。随后自己处理@WebServiceRef、@EJB、@Resource注解。

/**
 * CommonAnnotationBeanPostProcessor的方法
 * <p>
 * 调用父类InitDestroyAnnotationBeanPostProcessor的同名方法用于处理:PostConstruct、PreDestroy注解。
 * 随后自己处理:@WebServiceRef、@EJB、@Resource注解。
 *
 * @param beanDefinition      已合并的bean定义
 * @param beanType bean的类型
 * @param beanName beanName
 */
@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) 
    /*调用父类的InitDestroyAnnotationBeanPostProcessor的同名方法用于处理@PostConstruct、@PreDestroy注解*/
    super.postProcessMergedBeanDefinition(beanDefinition, beanType, beanName);
    /*自己处理@WebServiceRef、@EJB、@Resource注解*/
    InjectionMetadata metadata = findResourceMetadata(beanName, beanType, null);
    //检查配置
    metadata.checkConfigMembers(beanDefinition);

  很多后处理器在创建的时候会初始化很多的属性,CommonAnnotationBeanPostProcessor也不例外,最重要的是它会设置父类的init 和 destroy 注解类型默认为javax.annotation.PostConstruct和javax.annotation.PreDestroy。因此我们也可以自定义初始化和销毁注解。

/**
 * CommonAnnotationBeanPostProcessor的构造器
 * <p>
 * 设置父类的init 和 destroy 注解类型为javax.annotation.PostConstruct和javax.annotation.PreDestroy
 * 因此我们也可以自定义初始化和销毁注解
 */
public CommonAnnotationBeanPostProcessor() 
    //设置父类InitDestroyAnnotationBeanPostProcessor的order属性
    //用于排序
    setOrder(Ordered.LOWEST_PRECEDENCE - 3);
    //设置父类InitDestroyAnnotationBeanPostProcessor的initAnnotationType属性
    //表示初始化回调注解的类型
    setInitAnnotationType(PostConstruct.class);
    //设置父类InitDestroyAnnotationBeanPostProcessor的destroyAnnotationType属性
    //表示销毁回调注解的类型
    setDestroyAnnotationType(PreDestroy.class);
    //设置自己的ignoredResourceTypes属性
    //表示在解析@Resource注解时忽略给定的资源类型。
    ignoreResourceType("javax.xml.ws.WebServiceContext");

1.1.1 InitDestroyAnnotationBeanPostProcessor. postProcessMergedBeanDefinition回调

  内部就是调用两个方法,findLifecycleMetadata用于解析该Class,获取全部具有@PostConstruct、@PreDestroy注解的方法的LifecycleMetadata包装对象。
  checkConfigMembers用于检查配置信息,主要是防止后续重复调用回调方法。

/**
 * InitDestroyAnnotationBeanPostProcessor的方法
 * <p>
 * 处理@PostConstruct、@PreDestroy注解
 *
 * @param beanDefinition      已合并的bean定义
 * @param beanType bean的类型
 * @param beanName beanName
 */
@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) 
    //获取生命周期相关的元数据对象,LifecycleMetadata是该类的内部类,内部持有当前的class以及对应的具有@PostConstruct、@PreDestroy注解的方法
    LifecycleMetadata metadata = findLifecycleMetadata(beanType);
    //检查配置信息
    metadata.checkConfigMembers(beanDefinition);

1.1.1.1 findLifecycleMetadata处理@PostConstruct、@PreDestroy注解

  用于获取该Class的LifecycleMetadata,内部保存了具有@PostConstruct、@PreDestroy注解的方法。
  首先尝试从lifecycleMetadataCache缓存中获取,没有缓存就进行解析,解析之后会将结果存入缓存,后续相同的Class不再解析。

/**
 * InitDestroyAnnotationBeanPostProcessor的方法
 * <p>
 * 获取LifecycleMetadata
 *
 * @param clazz bean的类型
 * @return LifecycleMetadata对象,内部持有当前的class以及对应的具有@PostConstruct、@PreDestroy注解的方法
 */
private LifecycleMetadata findLifecycleMetadata(Class<?> clazz) 
    //如果lifecycleMetadataCache缓存为null
    if (this.lifecycleMetadataCache == null) 
        // 创建该类型的LifecycleMetadata
        return buildLifecycleMetadata(clazz);
    
    //否则,查询缓存
    LifecycleMetadata metadata = this.lifecycleMetadataCache.get(clazz);
    //如果存在该类型的缓存,从缓存获取
    if (metadata == null) 
        synchronized (this.lifecycleMetadataCache) 
            //加锁之后再次获取,防止并发
            metadata = this.lifecycleMetadataCache.get(clazz);
            //如果metadata为null
            if (metadata == null) 
                //那么创建该类型的LifecycleMetadata
                metadata = buildLifecycleMetadata(clazz);
                //存入缓存汇中
                this.lifecycleMetadataCache.put(clazz, metadata);
            
            //返回
            return metadata;
        
    
    //如果缓存不为null,那么直接返回缓存
    return metadata;

1.1.1.1.1 buildLifecycleMetadata创建LifecycleMetadata

  关键方法就是buildLifecycleMetadata方法,用于解析该Class的LifecycleMetadata。
  大概逻辑是:遍历该Class及其父Class,查找具有@PostConstruct和@PreDestroy注解的方法,封装成为一个LifecycleElement对象,存入两个集合中,父类的回调方法的LifecycleElement在集合的前面,最后会根据该Class以及初始化、销毁集合创建一个LifecycleMetadata对象返回。

//------InitDestroyAnnotationBeanPostProcessor的相关属性------

/**
 * 初始化回调注解的类型,默认@PostConstruct
 */
@Nullable
private Class<? extends Annotation> initAnnotationType;
/**
 * 销毁回调注解的类型,默认@PreDestroy
 */
@Nullable
private Class<? extends Annotation> destroyAnnotationType;
/**
 * 排序优先级
 */
private int order = Ordered.LOWEST_PRECEDENCE;


/**
 1. InitDestroyAnnotationBeanPostProcessor的方法
 2. <p>
 3. 根据给定的类型,创建LifecycleMetadata
 4. 遍历该类及其父类查找初始化注解和销毁注解
 5.  6. @param clazz bean的类型
 7. @return LifecycleMetadata对象,内部持有当前的class以及对应的具有@PostConstruct、@PreDestroy注解的方法
 */
private LifecycleMetadata buildLifecycleMetadata(final Class<?> clazz) 
    //确定给定类是否是承载指定注释的候选项(在类型、方法或字段级别)。
    //如果任何一个注解的全路径名都不是以"java."开始,并且该Class全路径名以"start."开始,或者Class的类型为Ordered.class,那么返回false,否则其他情况都返回true
    if (!AnnotationUtils.isCandidateClass(clazz, Arrays.asList(this.initAnnotationType, this.destroyAnnotationType))) 
        //如果该类型不能承载这两个主机,那么直接返回emptyLifecycleMetadata,这是一个空的LifecycleMetadata实例
        return this.emptyLifecycleMetadata;
    
    //到这里表示允许承载,那么尝试查找
    //初始化回调方法集合
    List<LifecycleElement> initMethods = new ArrayList<>();
    //销毁回调方法集合
    List<LifecycleElement> destroyMethods = new ArrayList<>();
    //目标类型
    Class<?> targetClass = clazz;
    /*循环遍历该类及其父类,直到父类为Object*/
    do 
        //当前的初始化回调方法集合
        final List<LifecycleElement> currInitMethods = new ArrayList<>();
        //当前的销毁回调方法集合
        final List<LifecycleElement> currDestroyMethods = new ArrayList<>();
        /*
         * 循环过滤所有的方法(不包括构造器),查找被初始化注解@PostConstruct和销毁注解@PreDestroy标注的方法
         * 这两个注解都是标注在方法上的,构造器上没有标注
         */
        ReflectionUtils.doWithLocalMethods(targetClass, method -> 
            //如果initAnnotationType不为null,并且存在该类型的注解
            if (this.initAnnotationType != null && method.isAnnotationPresent(this.initAnnotationType)) 
                //那么根据当前方法新建一个LifecycleElement,添加到currInitMethods中
                //LifecycleElement表示了一个具有@PostConstruct、@PreDestroy等生命周期注解的方法
                LifecycleElement element = new LifecycleElement(method);
                currInitMethods.add(element);
                if (logger.isTraceEnabled()) 
                    logger.trace("Found init method on class [" + clazz.getName() + "]: " + method);
                
            
            //如果initAnnotationType不为null,并且存在该类型的注解
            if (this.destroyAnnotationType != null && method.isAnnotationPresent(this.destroyAnnotationType)) 
                //那么根据当前方法新建一个LifecycleElement,添加到currDestroyMethods中
                currDestroyMethods.add(new LifecycleElement(method));
                if (logger.isTraceEnabled()) 
                    logger.trace("Found destroy method on class [" + clazz.getName() + "]: " + method);
                
            
        );

        //currInitMethods集合整体添加到initMethods集合的开头
        initMethods.addAll(0, currInitMethods);
        //currDestroyMethods集合整体添加到destroyMethods集合的开头
        destroyMethods.addAll(currDestroyMethods);
        //获取下一个目标类型,是当前类型的父类型
        targetClass = targetClass.getSuperclass();
    
    //如果目标类型不为null并且不是Object.class类型,那么继续循环,否则结束循环
    while (targetClass != null && targetClass != Object.class);
    //如果initMethods和destroyMethods都是空集合,那么返回一个空的LifecycleMetadata实例
    //否则返回一个新LifecycleMetadata,包含当前的class以及对应的找到的initMethods和destroyMethods
    return (initMethods.isEmpty() && destroyMethods.isEmpty() ? this.emptyLifecycleMetadata :
            new LifecycleMetadata(clazz, initMethods, destroyMethods));

1.1.1.1.2 LifecycleElement生命周期回调方法包装

  LifecycleElementInit是DestroyAnnotationBeanPostProcessor的内部类,一个LifecycleElement对象封装了一个生命周期回调方法,及其标识符,标识符用于方法重复调用。
  在创建LifecycleElement的时候做两件事:

  1. 进行参数校验,只有0个参数的方法才能作为初始化或者销毁回调方法,否则抛出异常:Lifecycle method annotation requires a no-arg method……
  2. 计算当前方法的identifier标识符,用于避免重复调用回调方法。如果是私有方法,则标识符为该方法的全路径名;如果是非私有方法,则标识符为该方法简单名字。
/**
 * InitDestroyAnnotationBeanPostProcessor的内部类
 * 一个LifecycleElement对象封装了一个回调方法,及其标识符,标识符用于方法重复调用
 */
private static class LifecycleElement 
    /**
     * 当前方法
     */
    private final Method method;
    /**
     * 标识符
     */
    private final String identifier;

    public LifecycleElement(Method method) 
        //如果方法参数不为0,那么抛出异常,从这里可知,初始化和销毁回调方法都不能有参数
        if (method.getParameterCount() != 0) 
            throw new IllegalStateException("Lifecycle method annotation requires a no-arg method: " + method);
        
        this.method = method;
        //计算identifier
        //如果是私有方法,则标识符为:该方法的全路径名,如果是非私有方法,则标识符为该方法简单名字
        this.identifier = (Modifier.isPrivate(method.getModifiers()) ?
                ClassUtils.getQualifiedMethodName(method) : method.getName());
    

1.1.1.2 checkConfigMembers检查配置

  设置相关的方法到checkedInitMethods和checkedDestroyMethods中,后续直接调用。同时设置到mbd的externallyManagedInitMethods中,防止重复调用同一个方法,后面会讲。

//-------LifecycleMetadata的属性----------

/**
 * 目标类型
 */
private final Class<?> targetClass;

/**
 * 初始化回调方法集合
 */
private final Collection<LifecycleElement> initMethods;
/**
 * 销毁回调方法集合
 */
private final Collection<LifecycleElement> destroyMethods;

/**
 * 检查的初始化回调方法集合
 */
@Nullable
private volatile Set<LifecycleElement> checkedInitMethods;
/**
 * 检查的销毁回调方法集合
 */
@Nullable
private volatile Set<LifecycleElement> checkedDestroyMethods;

/**
 * LifecycleMetadata的构造器
 * @param targetClass 目标类型
 * @param initMethods 初始化回调方法
 * @param destroyMethods 销毁回调方法
 */
public LifecycleMetadata(Class<?> targetClass, Collection<LifecycleElement> initMethods,
                         Collection<LifecycleElement> destroyMethods) 
    //为属性赋值
    this.targetClass = targetClass;
    this.initMethods = initMethods;
    this.destroyMethods = destroyMethods;


/**
 * LifecycleMetadata的方法
 * 
 * 检查配置,设置相关的方法到checkedInitMethods和checkedDestroyMethods中,后续直接调用
 * 同时设置到mbd的externallyManagedInitMethods中,防止重复调用同一个方法
 * @param beanDefinition 当前bean定义
 */
public void checkConfigMembers(RootBeanDefinition beanDefinition) 
    //创建被检查的初始化回调方法集合
    Set<LifecycleElement> checkedInitMethods = new LinkedHashSet<>(this.initMethods.size());
    //遍历检查initMethods
    for (LifecycleElement element : this.initMethods) 
        //获取标识符
        String methodIdentifier = element.getIdentifier();
        //如果mbd的externallyManagedInitMethods不包含当前回调方法
        if (!beanDefinition.isExternallyManagedInitMethod(methodIdentifier)) 
            //设置到mbd的externallyManagedInitMethods中
            beanDefinition.registerExternallyManagedInitMethod(methodIdentifier);
            //设置到checkedInitMethods中
            checkedInitMethods.add(element);
            if (logger.isTraceEnabled()) 
                logger.trace("Registered init method on class [" + this.targetClass.getName() + "]: " + element);
            
        
    
    Set<LifecycleElement> checkedDestroyMethods = new LinkedHashSet<>(this.destroyMethods.size());
    for (LifecycleElement element : this.destroyMethods) 
        //获取标识符
        String methodIdentifier = element.getIdentifier();
        //如果mbd的externallyManagedInitMethods不包含当前回调方法
        if (!beanDefinition.isExternallyManagedDestroyMethod(methodIdentifier)) 
            //设置到mbd的externallyManagedInitMethods中以上是关于Spring IoC容器初始化源码—populateBeaninitializeBean填充Bean字段反射和setter方法依赖注入以及IoC容器初始化总结四万字的主要内容,如果未能解决你的问题,请参考以下文章

spring源码分析IOC容器初始化

Spring IOC容器初始化流程源码分析

Spring IOC容器初始化流程源码分析

Spring源码分析总结-IOC容器初始化

spring源码分析IOC容器初始化(总结)

Spring-IOC源码解读2-容器的初始化过程