在这篇文章之前,写过了官网上容器扩展点相关的知识,包括 FactoryBean,BeanFactoryPostProcessor,BeanPostProcessor,其中 BeanPostProcessor 还剩一个很重要的知识没有介绍,就是相关的 BeanPostProcessor 中方法的执行时机。之所以在之前的文章没有写这块内容,主要是涉及到 bean 的生命周期。这篇文章开始写 Bean 的生命周期相关的知识,整个 bean 的生命周期可以分为以下几个阶段:
- 实例化(得到一个还没有经过属性注入和初始化的对象)
- 属性注入(得到了一个经过属性注入还没有初始化的对象)
- 初始化(得到一个初始化还没有经过 AOP 的对象,AOP 会在后置处理器中执行)
- 销毁
上面几个阶段中,BeanPostProcessor 将会穿插执行。而在初始化和销毁又分为两部分
- 生命周期回调方法的执行
- aware 相关接口方法的执行
这篇文章中,我们先完成 bean 的生命周期整个初始化的学习,对比官网中的章节为 1.6 小节
生命周期回调
- Bean 初始化回调
实现初始化回调有三种形式:
- 实现 InitializingBean 接口
如下:
public class AnotherExampleBean implements InitializingBean {
@Override
public void afterPropertiesSet() throws Exception {
}
}
- 使用 Bean 标签中的 init-method 属性
配置如下:
<bean id="exampleInitBean" class="com.xxl.root.ExampleBean" init-method="init"/>
public class ExampleBean {
private void init() {
}
}
- 使用@PostConstruct 注解
配置如下:
public class ExampleBean {
@PostConstruct
private void init() {
}
}
- Bean 销毁回调
实现销毁回调方法有三种形式:
- 实现 DisposableBean 接口
public class DisExampleBean implements DisposableBean {
@Override
public void destroy() throws Exception {
}
}
- 使用 Bean 标签中的 destroy-method 属性
<bean id="exampleInitBean" class="com.xxl.root.ExampleBean" destroy-method="cleanup"/>
public class ExampleBean {
private void cleanup() {
}
}
- 使用@PreDestroy 注解
public class ExampleBean {
@PreDestroy
private void cleanup() {
}
}
- 配置默认的初始化及销毁方法
我们可以通过如下这种配置,为多个 bean 同时指定初始化或销毁方法
<beans default-init-method="init" default-destroy-method="destory">
<bean id="blogService" class="com.something.DefaultBlogService">
<property name="blogDao" ref="blogDao" />
</bean>
</beans>
在上面的 XML 配置中,Spring 会将所有处于 beans 标签下的 bean 的初始化方法名默认为 init,销毁方法名默认为 destory。
- 执行顺序
如果我们在配置中同时让一个 bean 实现了回调接口,又在 bean 标签中指定了初始化方法,还进行了@PostContruct 注解配置的话,那么它们的执行顺序如下:
- 被@PostConstruct 所标记的方法
- InitializingBean 接口中的 afterPropertiesSet()方法
- Bean 标签中的 init()方法
对于销毁方法执行顺序如下:
- 被@PreDestroy 所标记的方法
- DisposableBean 接口中的 destroy()方法
- Bean 标签中的 destroy()方法
我们可以总结如下:
注解的优先级 > 实现接口的优先级 > XML 配置的优先级
同时还需要注意,官网推荐我们使用注解的形式定义生命周期回调犯法,这是因为相比于实现接口,采用注解这种形式使我们代码和 Spring 框架本身的耦合度更加低。
- 容器启动或停止回调 Lifecycle 接
- Lifecycle 接口
编写 Demo 如下:
public class Test {
public static void main(String[] args) {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(Appconfig.class);
ac.start();
ac.stop();
}
}
@Configuration
@ComponentScan("com.xxl.service")
public class Appconfig {
}
@Component
public class LifeCycleService implements Lifecycle {
boolean isRunning;
@Override
public void start() {
isRunning = true;
System.out.println("LifeCycleService start");
}
@Override
public void stop() {
isRunning = false;
System.out.println("LifeCycleService stop");
}
@Override
public boolean isRunning() {
return isRunning;
}
运行上面的代码可以发现程序正常打印启动和停止的日志,在上面的例子中需要注意的是,一定要在 start 方法执行时将容器的运行状态 isRunning 置为 true,否则 stop 方法不会调用
在 Spring 容器中,当接收到 start 和 stop 信号时,容器会将这些传递到所有实现了 Lifecycle 的组件上,在 Spring 内部是通过 LifecycleProcessor 接口来完成这一功能的。
- LifecycleProcessor
- 从上面的代码我们可以知道 LifecycleProcessor 本身也是 Lifecycle 接口的扩展,它添加了两个额外的方法在容器刷新和关闭时执行。
- 当我们实现 Lifecycle 接口时,如果我们想要其 start 或 stop 执行,必须显示的调用容器的 start()或 stop()方法。
- stop 方法不一定能保证在我们之前介绍的销毁方法之前执行。
当我们在容器中对多个 bean 配置了容器启动会停止时的调用,那么这些 bean 中的 start 方法和 stop 方法调用的顺序就很重要了。如果两个 bean 之间有明确的依赖关系,比如我们通过@DependsOn 注解,或者@AutoWired 注解向容器表明了 bean 之间的依赖关系,如下:
@Component
@DependsOn("b")
public class A {
// @Autowired
// B b;
}
@Component
public class B {
}
这种情况下,b 作为被依赖项,其 start 方法会在 a 的 start 方法前调用,stop 方法会在 a 的 stop 方法后调用。
但是,在某些情况下 bean 之间没有直接的依赖关系,可能我们只知道实现了接口 A 的所有 bean 的方法优先级要高于实现了接口 B 的 bean。在这种情况下,我们就需要用到 SmartLifecycle 接口
- SmartLifecycle
其继承关系如下:
它本身除了继承 ifecycle 接口,还继承了一个 Phased 接口,其接口定义如下:
通过上面接口定义的方法,我们可以指定不同 bean 方法回调方法执行的优先级。
再来看 SmartLifecycle 接口本身的定义:
一般情况下,我们并不会重写 isAutoStartup 和 stop 方法,但是为了指定方法执行的优先级,我们通常会覆盖其中的 getPhase 方法,默认情况下它的优先级最低。我们需要知道的是,当我们启动容器时,如果有 bean 实现了 SmartLifecycle 接口,其 getPhase()方法返回的值越小,那么对于 start 方法执行的时间就越早,stop 方法执行的时机就越晚。因此,一个实现了 SmartLifecycle 的对象,它的 getPhase()方法返回 Integer.MIX_VALUE 将是第一个执行 start 方法的 bean 和最后一个执行 stop 方法的 bean。
另外我们可以看到
源码分析,分为两个阶段:
- 启动阶段,整个流程图如下:
我们主要分析的代码在其中的 3-12-2 和 3-12-3 步骤中
3-12-2 解析,代码如下:
这段代码很简单,就做了一件事:判断当前容器中是否有一个 lifecycleProcessor 的 bean 或者 BeanDefinition。如果有的话,采用这个提供的 lifecycleProcessor,如果没有的话直接 new 一个 DefaultLifecycleProcessor。这个类主要负责将启动或停止信息传播到具体的 bean 中,我们稍后分析的代码基本都在这个类。
3-12-3 解析:
其中 getLifecycleProcessor(),就是获取我们上一步提供的 lifecycleProcessor,然后调用 onRefresh 方法,代码如下:
之后调用 startBeans 方法:
跟踪代码可以发现,start 方法最终调用到了 doStart 方法:
上面的逻辑可以归结为第一句话:获取这个 bean 依赖的其他 bean,在启动时先启动其依赖的 bean,这也验证了我们从官网得出的结论。
- 停止阶段
停止容器有两种方法,一种是显示的调用容器的 stop 或者 close 方法,如下:
public class Test {
public static void main(String[] args) {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
ac.register(Appconfig.class);
ac.refresh();
ac.stop();
// ac.close();
}
}
而另外一种就是主场一个 JVM 退出时的钩子,如下:
public class Test {
public static void main(String[] args) {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
ac.register(Appconfig.class);
// 当 main 函数运行完成后,会调用容器 doClose 方法
ac.registerShutdownHook();
ac.refresh();
}
}
无论是上面哪一种方法,最终都会调用到 DefaultLifecycleProcessor 的 onClose 方法,代码如下:
整个 stop 方法和 start 方法对比,逻辑上并没有很大的区别,除了执行时顺序相反外:
- start 方法,先找出这个份 bean 的所有依赖,然后启动这个 bean 的依赖
- stop 方法,先找出哪些 bean 依赖了当前 bean,然后停止这些被依赖的 bean,之后再停止当前的 bean。
Aware 接口
在整个 bean 的生命周期的初始化阶段,有一个很重要的步骤就是执行相关的 Aware 接口,而整个 Aware 接口执行又可以分为两个阶段:
- 第一个阶段,执行 BeanXXXAware 接口
- 执行其它 Aware 接口
至于为什么需要这样分,我们在进行源码分析的时候就明白了
我们可以发现,所有的 Aware 接口都是为了能让我们拿到容器中相关的资源,比如 BeanNameAwar,可以拿到 bean 的名称,ApplicationContextAware 可以拿到整个容器。但是使用 Aware 接口也会相应的带来一些麻烦,当我们去实现这些接口时,意味着我们的应用程序和 Spring 容器发生了强耦合,违背了 IOC 的原则。所以一般情况下,不推荐这种方式,除非在编写一些整个应用基础的组件。
Spring 内部提供了如下这些 Aware 接口:
初始化过程源码分析
回顾我们之前的流程图,可以看到,创建 bean 的动作主要发生在 3-11-6-4 步骤中,主要分为三步:
- createBeanInstance,创建实例
- populateBean,属性注入
- initializeBean,初始化
我们今天要分析的代码主要就是 3-11-6-4-3 步,其完成的功能主要就是初始化,相对于我们之前分析的代码来说,这段代码算比较简单:
第一步:执行部分 aware 接口中的方法
可以看到,在 invokeAwareMethods 这个方法中,并不是所有的 Aware 接口都会被执行,只有 BeanNameAware,BeanClassLoaderAware,BeanFactoryAware 这三个接口会被执行,这也是为什么我单独讲 BeanXXXAware 这一类的接口划为一组的原因。这三个 Aware 接口分别实现的功能:
BeanNameAware:获取 bean 的名字
BeanClassLoaderAware:获取加载这个 bean 的类加载器
BeanFactoryAware:获取当前的 BeanFactory
第二步:完成 Aware 接口方法的执行,以及@PostConstructor,@PreDestroy 注解的处理
- Aware 接口执行,除了上面介绍的三个 Aware 接口,其余的接口都会在这个阶段执行,例如我们之前说的 ApplicationContestAware 接口,它会被一个专门的后置处理器 ApplicationContextAwareProcessor 处理,其余的接口也是类似的操作,
- @PostConstructor,@PreDestroy 两个注解的处理,这两个注解会被 CommonAnnotationBeanPostProcessor 这个后置处理器处理,需要注意的是@Resource 注解也是被这个后置处理器处理的。
第三步:执行 InitializingBean 完成初始化方法执行
整段代码的逻辑还是很简单的,先判断是否实现了对象的生命周期回调的接口(InitializingBean),如果实现了接口,先调用接口中的 afterPropertiesSet 方法,之后再判断是否提供了 initMethod,也就是 XML 中的 Bean 标签中的 init-method 属性。
第四部:完成 AOP 代理
AOP 代理实现的具体过程以后在写,暂且只需要知道 AOP 是在 bean 完成了所有初始化方法后完成即可。也不难理解,在进行 AOP 之前必须保证我们的 bean 已经被充分装配了。
总结
就目前而言,我们可以将整个 Bean 的生命周期总结如下:
在上图中,实例化和属性注入的过程还没有分析,在后续的文章中在详细些了。销毁节点并不复杂,这里也不做分析了,直接给出结论,大概可以直接阅读代码,入口在容器的 close 方法。
另外,我这里并没有实现 LifeCycle 接口的 bean 中的 start 方法和 stop 方法算入到整个 bean 的生命周期中,大家只有知道,如果实现了 SmartLifecyle 接口,那么在容器启动时也会默认调用其 start 方法,并且调用的时机在 bean 完成初始化后,而 stop 方法将在 bean 销毁前调用。
标签:Spring,stop,bean,接口,start,Bean,源码,方法,public From: https://blog.51cto.com/u_15668812/7409529