Spring中bean的生命周期

100 阅读4分钟

Bean的扫描流程

image.png

image.png

包含过滤器

includeFilters 默认是在 ClassPathBeanDefinitionScanner初始化时,进行扫描 registerDefaultFilters方法。

image.png

MetadataReader

类的元数据读取器,主要包含了一个AnnotationMetadata,功能有

  1. 获取类的名字
  2. 获取父类的名字
  3. 获取所实现的所有接口名
  4. 获取所有内部类的名字
  5. 判断是不是抽象类
  6. 判断是不是接口
  7. 判断是不是一个注解
  8. 获取拥有某个注解的方法集合
  9. 获取类上添加的所有注解信息
  10. 获取类上添加的所有注解类型集合

值得注意的是,CachingMetadataReaderFactory解析某个.class文件得到MetadataReader对象是利用的ASM技术,并没有加载这个类到JVM。并且,最终得到的ScannedGenericBeanDefinition对象,beanClass属性存储的是当前类的名字,而不是class对象。(beanClass属性的类型是Object,它即可以存储类的名字,也可以存储class对象) ​

最后,上面是说的通过扫描得到BeanDefinition对象,我们还可以通过直接定义BeanDefinition,或解析spring.xml文件的,或者@Bean注解得到BeanDefinition对象。

默认名称的生成

protected String buildDefaultBeanName(BeanDefinition definition) { 
    String beanClassName = definition.getBeanClassName(); 
    Assert.state(beanClassName != null, "No bean class name set"); 
    String shortClassName = ClassUtils.getShortName(beanClassName); 
    return Introspector.decapitalize(shortClassName); 
} 

public static String decapitalize(String name) { 
    if (name == null || name.length() == 0) { 
        return name; 
    } 
    // 如果首字母和第二个字母都是大写,那么就直接按照原名称作为beanName 否则下面的流程会将首字
    if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) && Character.isUpperCase(name.charAt(0))){ 
        return name; 
    } 
    char chars[] = name.toCharArray(); 
    chars[0] = Character.toLowerCase(chars[0]); 
    return new String(chars); 
}

Condition的扩展

方便以后使用

image.png

单例非懒加载Bean实例化前操作

流程图如下:

image.png

合并BeanDefinition​

通过扫描得到所有BeanDefinition之后,就可以根据BeanDefinition创建Bean对象了,但是在Spring中支持父子BeanDefinition,和Java父子类类似,但是完全不是一回事。

父子BeanDefinition实际用的比较少,使用是这样的,比如:

<bean id="parent" class="com.zhouyu.service.Parent" scope="prototype"/>
<bean id="child" class="com.zhouyu.service.Child"/>

这么定义的情况下,child是单例Bean。

<bean id="parent" class="com.zhouyu.service.Parent" scope="prototype"/> 
<bean id="child" class="com.zhouyu.service.Child" parent="parent"/>

但是这么定义的情况下,child就是原型Bean了。 ​

因为child的父BeanDefinition是parent,所以会继承parent上所定义的scope属性。 ​

而在根据child来生成Bean对象之前,需要进行BeanDefinition的合并,得到完整的child的BeanDefinition。

image.png

transformedBeanName方法

image.png

这个方法在源码中也是非常常见,主要是做名称的处理转换(针对FactoryBean的),然后对别名的转换功能。

Bean的创建流程

image.png

Bean的销毁

在applicationContext里面可以设置bean的销毁,两种方式本质上都是调用doClose方法。


AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class); 
applicationContext.close(); 
//钩子函数 applicationContext.registerShutdownHook(); 

@Override 
public void registerShutdownHook() { 
    if (this.shutdownHook == null) { 
    // No shutdown hook registered yet. 
    this.shutdownHook = new Thread(SHUTDOWN_HOOK_THREAD_NAME) { 
        @Override public void run() { 
            synchronized (startupShutdownMonitor) { 
                doClose(); 
             } 
        }
    }; 
    //jvm线程正常退出时,触发钩子程序。 异常退出不会触发 例如 kill -9 
    Runtime.getRuntime().addShutdownHook(this.shutdownHook); 
    } 
 }
 destroyMethodName AutoCloseable DisposableBean DestructionAwareBeanPostProcessor
  1. 如果当前类实现了DisposableBean或者AutoCloseable接口,则需要在容器销毁时进行bean的销毁。
  2. 上面不满足,则判断当前的beanDefinition如果设置了destroyMethod(可以通过MergedBeanDefinitionPostProcessor设置),且销毁方法 == (inferred) (带括号); 或者销毁方法为null,但是实现了AutoCloseable接口;
  3. 再判断是否实现了DisposableBean的接口,没有设置的话,调用close或者shutdown方法。
  4. 调用DestructionAwareBeanPostProcessor的requiresDestruction(bean)判断是否需要销毁,两个实现类中逻辑如下:
    1. ApplicationListenerDetector中直接使得ApplicationListener是DisposableBean
    2. InitDestroyAnnotationBeanPostProcessor中使得拥有@PreDestroy注解了的方法就是DisposableBean,并且循环遍历父类
  1. 把符合上述任意一个条件的Bean适配成DisposableBeanAdapter对象,并存入disposableBeans中(一个LinkedHashMap)

spring容器正常关闭时

  1. AbstractApplicationContext调用 doClose方法。
  2. 发布ContextClosedEvent事件。
  3. 调用lifecycleProcessor的onCloese()方法,去执行stop方法。
  4. 执行单例bean的销毁方法 --destroyBeans:
    1. 遍历disposableBeans
    2. 把每个disposableBean从单例池中移除
    3. 调用disposableBean的destroy();
    4. 如果这个disposableBean还被其他Bean依赖了,那么也得销毁其他Bean;
    5. 如果这个disposableBean还包含了inner beans,将这些Bean从单例池中移除掉
    6. 清空manualSingletonNames,是一个Set,存的是用户手动注册的单例Bean的beanName
    7. 清空allBeanNamesByType,是一个Map,key是bean类型,value是该类型所有的beanName数组
    8. 清空singletonBeanNamesByType,和allBeanNamesByType类似,只不过只存了单例Bean

这里涉及到一个设计模式:适配器模式

在销毁时,Spring会找出实现了DisposableBean接口的Bean。 ​

但是我们在定义一个Bean时,如果这个Bean实现了DisposableBean接口,或者实现了AutoCloseable接口,或者在BeanDefinition中指定了destroyMethodName,那么这个Bean都属于“DisposableBean”,这些Bean在容器关闭时都要调用相应的销毁方法。

所以,这里就需要进行适配,将实现了DisposableBean接口、或者AutoCloseable接口等适配成实现了DisposableBean接口,所以就用到了DisposableBeanAdapter。

会把实现了AutoCloseable接口的类封装成DisposableBeanAdapter,而DisposableBeanAdapter实现了DisposableBean接口。