Bootstrap

Java八股文-Spring Bean生命周期

AbstractApplicationContext.refresh方法中的finishBeanFactoryInitialization流程会初始化容器中非延迟的单例Bean。
bean 的生命周期从调用 beanFactory 的 getBean 开始,到这个 bean 被销毁,可以总结为以下七个阶段:

  1. 处理名称,检查缓存
  2. 处理父子容器
  3. 处理 dependsOn
  4. 选择 scope 策略
  5. 创建 bean
  6. 类型转换处理
  7. 销毁 bean

在这里插入图片描述

1. 处理名称,检查缓存

这一步会处理别名,将别名解析为实际名称
对 FactoryBean 也会特殊处理,如果以 & 开头表示要获取 FactoryBean 本身,否则表示要获取其产品
这里针对单例对象会检查一级、二级、三级缓存

  • singletonFactories 三级缓存,存放单例工厂对象
  • earlySingletonObjects 二级缓存,存放单例工厂的产品对象
    • 如果发生循环依赖,产品是代理;无循环依赖,产品是原始对象
  • singletonObjects 一级缓存,存放单例成品对象

2. 处理父子容器

如果当前容器根据名字找不到这个 bean,此时若父容器存在,则执行父容器的 getBean 流程
父子容器的 bean 名称可以重复

思考:如何构建父子容器的情况?怎么区分父子容器

3. 处理 dependsOn

如果当前 bean 有通过 dependsOn 指定了非显式依赖的 bean,这一步会提前创建这些 dependsOn 的 bean
所谓非显式依赖,就是指两个 bean 之间不存在直接依赖关系,但需要控制它们的创建先后顺序

4. 选择 scope 策略

对于 singleton scope,首先到单例池去获取 bean,如果有则直接返回,没有再进入创建流程
对于 prototype scope,每次都会进入创建流程
对于自定义 scope,例如 request,首先到 request 域获取 bean,如果有则直接返回,没有再进入创建流程

static class Bean1 {
        @PostConstruct
        public void init() {
            LoggerUtils.get().debug("{} - init", this);
        }

        @PreDestroy
        public void destroy() {
            LoggerUtils.get().debug("{} - destroy", this);
        }
    }

单例 bean 从 refresh 被创建, 到 close 被销毁, BeanFactory 会记录哪些 bean 要调用销毁方法

private static void testSingletonScope() {
    GenericApplicationContext context = new GenericApplicationContext();
    context.registerBean("bean1", Bean1.class);
    //为了解析@PostConstruct、@PreDestroy注解
    context.registerBean(CommonAnnotationBeanPostProcessor.class);
    context.refresh(); // getBean
    context.close();
}

多例 bean 从首次 getBean 被创建, 到调用 BeanFactory 的 destroyBean 被销毁. 容器启动和关闭不会调用Bean的初始化

private static void testPrototypeScope() {
     GenericApplicationContext context = new GenericApplicationContext();
     context.registerBean("bean1", Bean1.class, bd -> bd.setScope("prototype"));
     context.registerBean(CommonAnnotationBeanPostProcessor.class);
     LoggerUtils.get().debug("context - refresh");
     context.refresh();

     //getBean时才调用初始化方法
     Bean1 bean = context.getBean(Bean1.class);
     //没谁记录该 bean 要调用销毁方法, 需要我们自行调用
     context.getDefaultListableBeanFactory().destroyBean(bean);
     context.close();
     LoggerUtils.get().debug("context - close");
 }

在这里插入图片描述

request bean 从首次 getBean 被创建, 到 request 结束前被销毁

private static void testRequestScope() {
    GenericApplicationContext context = new GenericApplicationContext();
    context.getDefaultListableBeanFactory().registerScope("request", new RequestScope());
    context.registerBean("bean1", Bean1.class, bd -> bd.setScope("request"));
    context.registerBean(CommonAnnotationBeanPostProcessor.class);
    context.refresh();

    for (int i = 0; i < 2; i++) {
        new Thread(() -> {
            MockHttpServletRequest request = new MockHttpServletRequest();
            // 每个 webRequest 对象会记录哪些 bean 要调用销毁方法
            ServletWebRequest webRequest = new ServletWebRequest(request);
            RequestContextHolder.setRequestAttributes(webRequest);

            Bean1 bean = context.getBean(Bean1.class);
            LoggerUtils.get().debug("{}", bean);
            LoggerUtils.get().debug("{}", request.getAttribute("bean1"));

            // request 请求结束前调用这些销毁方法
            webRequest.requestCompleted();
        }).start();
    }

}

5. 创建 bean

虽然有不同的作用域,但是都会走到第5步创建Bean的流程。整体分为4个阶段

  1. 创建阶段
  2. 依赖注入阶段
  3. 初始化阶段
  4. 注册和销毁Bean阶段

每个阶段都会有BeanPostProcessor对Bean的功能做扩展和增强

在这里插入图片描述

5.1 创建 bean 实例

要点总结
有自定义 TargetSource 的情况AnnotationAwareAspectJAutoProxyCreator 创建代理返回
Supplier 方式创建 bean 实例为 Spring 5.0 新增功能,方便编程方式创建 bean 实例
FactoryMethod 方式 创建 bean 实例① 分成静态工厂与实例工厂;
② 工厂方法若有参数,需要对工厂方法参数进行解析,利用 resolveDependency;
③ 如果有多个工厂方法候选者,还要进一步按权重筛选
AutowiredAnnotationBeanPostProcessor① 优先选择带 @Autowired 注解的构造;
② 若没有@Autowired 但是有唯一的带参构造,也会选择带参构造
mbd.getPreferredConstructors选择所有公共构造,这些构造之间按权重筛选
采用默认构造如果上面的后处理器和 BeanDefiniation 都没找到构造,采用默认构造,即使是私有的也会暴力反射创建

5.2 依赖注入

要点总结
AutowiredAnnotationBeanPostProcessor识别 @Autowired 及 @Value 标注的成员,封装为 InjectionMetadata 进行依赖注入
CommonAnnotationBeanPostProcessor 识别 @Resource 标注的成员,封装为 InjectionMetadata 进行依赖注入
resolveDependency用来查找要装配的值,可以识别:
① Optional;
② ObjectFactory 及 ObjectProvider;
③ @Lazy 注解;
④ @Value 注解(${ }, #{ }, 类型转换);
⑤ 集合类型(Collection,Map,数组等);
⑥ 泛型和 @Qualifier(用来区分类型歧义);
⑦ primary 及名字匹配(用来区分类型歧义)
AUTOWIRE_BY_NAME根据成员名字找 bean 对象,修改 mbd 的 propertyValues,不会考虑简单类型(String 、Integer等)的成员
AUTOWIRE_BY_TYPE根据成员类型执行 resolveDependency 找到依赖注入的值,修改 mbd 的 propertyValues
applyPropertyValues根据 mbd 的 propertyValues 进行依赖注入(即xml中 <property name ref|value/>

实验哪种注入优先级高

  1. 优先级最高的:精确指定注入 bean 的名称
  2. 优先级次之的:通过 AUTOWIRE_BY_NAME 匹配
  3. 优先级最低的:@Autowired 匹配
static class Bean1 {
    MyInterface bean;

    // 优先级最低的:@Autowired 匹配
    @Autowired @Qualifier("bean4")
    public void setBean3(MyInterface bean) {
        System.out.println(bean);
        this.bean = bean;
    }
}

interface MyInterface {
}

static class Bean2 implements MyInterface {
}

static class Bean3 implements MyInterface {
}

static class Bean4 implements MyInterface {
}
public static void main(String[] args) {
    GenericApplicationContext context = new GenericApplicationContext();
    AnnotationConfigUtils.registerAnnotationConfigProcessors(context.getDefaultListableBeanFactory());
    context.registerBean("bean1", Bean1.class, bd -> {
        // 优先级最高的:精确指定注入 bean 的名称 <property name="bean3" ref="bean2"/>
        bd.getPropertyValues().add("bean3", new RuntimeBeanReference("bean2"));
        // 优先级次之的:通过 AUTOWIRE_BY_NAME 匹配
        ((RootBeanDefinition) bd).setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_NAME);
    });
    context.registerBean("bean2", Bean2.class);
    context.registerBean("bean3", Bean3.class);
    context.registerBean("bean4", Bean4.class);

    context.refresh();
}

5.3 初始化

要点总结
内置 Aware 接口的装配包括 BeanNameAware,BeanFactoryAware 等
扩展 Aware 接口的装配由 ApplicationContextAwareProcessor 解析,执行时机在 postProcessBeforeInitialization
@PostConstruct由 CommonAnnotationBeanPostProcessor 解析,执行时机在 postProcessBeforeInitialization
InitializingBean通过接口回调执行初始化
initMethod根据 BeanDefinition 得到的初始化方法执行初始化,即 <bean init-method> 或 @Bean(initMethod)
创建 aop 代理AnnotationAwareAspectJAutoProxyCreator 创建,执行时机在 postProcessAfterInitialization

实验初始化方法的优先级:输出 4 2 1 3

  1. 先执行Aware接口
  2. 执行注解初始化:@PostConstruct
  3. 执行接口初始化:afterPropertiesSet接口回调
  4. 执行自定义初始化:配置的initMethod
public class TestInitialization {

    public static void main(String[] args) {
        GenericApplicationContext context = new GenericApplicationContext();
        context.registerBean(CommonAnnotationBeanPostProcessor.class);
        // 相当于配置了XML标签 <bean init-method="initMethod">
        context.registerBean("bean1", Bean1.class, bd -> bd.setInitMethodName("initMethod"));
        context.refresh();
    }

    static class Bean1 implements InitializingBean, BeanFactoryAware {

        @Override
        public void afterPropertiesSet() throws Exception {
            System.out.println(1);
        }

        @PostConstruct
        public void init() {
            System.out.println(2);
        }

        public void initMethod() {
            System.out.println(3);
        }

        @Override
        public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
            System.out.println(4);
        }
    }
}

5.4 注册可销毁 bean

在这一步判断并登记可销毁 bean

  • 判断依据
    • 如果实现了 DisposableBean 或 AutoCloseable 接口,则为可销毁 bean
    • 如果自定义了 destroyMethod,则为可销毁 bean
    • 如果采用 @Bean 没有指定 destroyMethod,则采用自动推断方式获取销毁方法名(提供了 close,shutdown方法)
    • 如果有 @PreDestroy 标注的方法
  • 存储位置
    • singleton scope 的可销毁 bean 会存储于 beanFactory 的成员当中
    • 自定义 scope( request、session、application) 的可销毁 bean 会存储于对应的域对象当中
    • prototype scope 不会存储,需要自己找到此对象销毁
  • 存储时都会封装为 DisposableBeanAdapter 类型对销毁方法的调用进行适配 (适配器模式

6. 类型转换处理

如果 getBean 的 requiredType 参数与实际得到的对象类型不同,会尝试进行类型转换
在这个阶段Bean已经被创建并返回

7. 销毁 bean

  • 销毁时机
    • singleton bean 的销毁在 ApplicationContext.close 时,此时会找到所有 DisposableBean 的名字,逐一销毁
    • 自定义 scope bean 的销毁在作用域对象生命周期结束时
    • prototype bean 的销毁可以通过自己手动调用 AutowireCapableBeanFactory.destroyBean 方法执行销毁
  • 同一 bean 中不同形式销毁方法的调用次序
    • 优先后处理器销毁,即 @PreDestroy
    • 其次 DisposableBean 接口销毁
    • 最后 destroyMethod 销毁(包括自定义名称,推断名称,AutoCloseable 接口 多选一)
;