为什么要关注Spring容器中Bean的生命周期?
因为我们需要依据Bean注册到容器的不同时间点进行拦截或其它一系列操作,使得程序员对控制反转这一过程具有更好的控制力和介入能力。
实例化对象在容器中的生命周期如下:
上图中,的四个方法,分别在Bean的不同生命周期内执行,其中红色的两个节点分别在Bean的初始化后和销毁前执行,一共有三种实现方式:
1.通过@Bean注解只当intit-method和destroy-method方法完成对Bean初始化方法和销毁方法的指定。
定义一个类名称为YuanQiShaoNv:
public class YuanQiShaoNv {
public YuanQiShaoNv(){
System.out.println("元气少女执行构造方法。。。");
}
public void init(){
System.out.println("元气少女执行初始化之后执行的方法");
}
public void destroy(){
System.out.println("元气少女指定销毁之前执行的方法");
}
}
配置类如下:
@Configuration//告诉Spring容器,这个类是一个配置类需要在执行是被加载
public class Config2 {
/**
* 通过在注解中指定Bean对象的生死监听,就是指定Bean对象的Init方法和Destroy方法
* @return
*/
@Bean(value = "YuanQiShaoNv",initMethod = "init",destroyMethod = "destroy")
public YuanQiShaoNv getStudent(){
return new YuanQiShaoNv();
}
}
可以看出来,我们在@Bean标签中通过init-method和destroy-method指定了初始化和销毁方法。
测试类如下:
@Test
public void test01(){
//因为使用的是注解方式注册,所以,需要创建的是AnnotationConfigApplicationContext类来获取Ioc容器而不是xml方式的ClassPathXmlApplicationContext类
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(Config2.class);
String[] beanDefinitionNames = annotationConfigApplicationContext.getBeanDefinitionNames();
for(String name : beanDefinitionNames){
System.out.println(name);
}
annotationConfigApplicationContext.close();
}
可以从测试类看出,我们先获取了容器中的所用Bean对象的名称并将其打印出来,最后关闭容器(不关闭容器destroy-method无法执行)
这里要说明一点,容器中的对象必须是单例的,因为只有单例的容器才会从头到尾管理你的生命周期(管生还管养,管杀还管埋),但是如果你是ProtoType(多例) ,那么容器将只会管理你的创建,而不管理你创建后的其它生命周期。也就是说多例模式无法看到destroy-method的效果。
运行结果如下:
2.通过实现 InitializingBean 接口 DisposableBean 接口来使用对象的初始化和销毁方法。
实现类如下YAAG(young and active girl):
@Component
public class YAAG implements InitializingBean, DisposableBean {
public void destroy() throws Exception {
System.out.println("我爱人民大会堂!");
}
public void afterPropertiesSet() throws Exception {
System.out.println("我爱北京天安门!");
}
}
配置类如下:
@Configuration//告诉Spring容器,这个类是一个配置类需要在执行是被加载
@ComponentScan("com.sdp.Entity")
public class Config2 {
}
测试类如下:
@Test
public void test01(){
//因为使用的是注解方式注册,所以,需要创建的是AnnotationConfigApplicationContext类来获取Ioc容器而不是xml方式的ClassPathXmlApplicationContext类
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(Config2.class);
String[] beanDefinitionNames = annotationConfigApplicationContext.getBeanDefinitionNames();
for(String name : beanDefinitionNames){
System.out.println(name);
}
annotationConfigApplicationContext.close();
}
测试结果如下:
3.使用@PostConstruct和@PreDestroy注解在方法上,使用初始化和销毁方法。
创建类YuanQiShaoNv:
public class YuanQiShaoNv {
public YuanQiShaoNv(){
System.out.println("元气少女执行构造方法。。。");
}
@PostConstruct
public void init(){
System.out.println("元气少女执行初始化之后执行的方法");
}
@PreDestroy
public void destroy(){
System.out.println("元气少女指定销毁之前执行的方法");
}
}
配置文件如下:
@Configuration//告诉Spring容器,这个类是一个配置类需要在执行是被加载
@Import(value=YuanQiShaoNv.class)
public class Config2 {
}
测试类如下:
@Test
public void test01(){
//因为使用的是注解方式注册,所以,需要创建的是AnnotationConfigApplicationContext类来获取Ioc容器而不是xml方式的ClassPathXmlApplicationContext类
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(Config2.class);
String[] beanDefinitionNames = annotationConfigApplicationContext.getBeanDefinitionNames();
for(String name : beanDefinitionNames){
System.out.println(name);
}
annotationConfigApplicationContext.close();
}
测试结果如下:
下来我们在来说一下绿色圈圈住的两个方法
这两个方法是BeanPostProcessor接口中的,该接乐意被称为Bean后处理器,功能非常强大,一旦你在当前容器中注入的该接口的实现类,容器中的所有Bean对象都会执行该接口中的两个方法(一般情况下,特殊情况除外)。
下面我们来测试一下这个方法:
定义测试类YuanQiShaoNv:
public class YuanQiShaoNv {
public YuanQiShaoNv(){
System.out.println("元气少女执行构造方法。。。");
}
@PostConstruct
public void init(){
System.out.println("元气少女执行初始化之后执行的方法");
}
@PreDestroy
public void destroy(){
System.out.println("元气少女指定销毁之前执行的方法");
}
}
配置类如下:
@Configuration//告诉Spring容器,这个类是一个配置类需要在执行是被加载
@Import(value={YuanQiShaoNv.class,BeanPostProcessorImpl.class})
public class Config2 {
}
测试类如下:
@Test
public void test01(){
//因为使用的是注解方式注册,所以,需要创建的是AnnotationConfigApplicationContext类来获取Ioc容器而不是xml方式的ClassPathXmlApplicationContext类
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(Config2.class);
String[] beanDefinitionNames = annotationConfigApplicationContext.getBeanDefinitionNames();
for(String name : beanDefinitionNames){
System.out.println(name);
}
annotationConfigApplicationContext.close();
}
测试结果如下:
我们可以看出来,在容器中注册每一个Bean的时候都会执行BeanPostProcessor中的方法
postProcessBeforeInitialization()
和方法
postProcessAfterInitialization()
还有要说的就是:BeanPostProcessor是一个非常重要的接口,在我们的Spring注解式开发中,很多地方的底层实现都使用了Bean后处理器,比如
@AutoWired自动注入标签底层使用了Bean后处理器。
@PostConstruct和@PreDestroy初始化和销毁注解也使用了Bean后处理器。
等等,所以深入理解Bean后处理器是很有助于我们深入理解Spring源码的。