2、生命周期
通常意义上讲的bean的生命周期,指的是bean从创建到初始化,经过一系列的流程,最终销毁的过程。只不过,在Spring中,bean的生命周期是由Spring容器来管理的。在Spring中,我们可以自己来指定bean的初始化和销毁的方法。我们指定了bean的初始化和销毁方法之后,当容器在bean进行到当前生命周期的阶段时,会自动调用我们自定义的初始化和销毁方法。
以前在XML文件中我们要定义bean的生命周期方法可以直接在bean标签上加上相应的属性,这样就可以为该bean组件添加上了初始化方法与销毁方法。
public class Snake {
public Snake() {
}
public void init(){
System.out.println("蛇的初始化方法...");
}
public void destroy(){
System.out.println("蛇的销毁方法...");
}
}
需要注意的是,在Snake 类中,需要存在init()方法和destroy()方法。而且Spring中还规定,这里的init()方法和destroy()方法必须是无参方法,但可以抛出异常。
2.1、使用bean注解给bean添加生命周期方法
接下来我们来讲讲如何使用注解的方式给bean组件添加上生命周期方法
首先编写一个bean用来做演示
public class Car {
public Car() {
System.out.println("这是Car的无参数构造方法");
}
public void init(){
System.out.println("这是Car的初始化方法");
}
public void destroy(){
System.out.println("这是Car的销毁方法");
}
}
配置类
@Configuration
public class FourthConfig {
@Bean(initMethod = "init",destroyMethod = "destroy")
public Car car(){
return new Car();
}
}
我们都知道单实例对象是在容器创建的 时候就已经创建好了,而多实例对象是在调用的时候才会进行创建。现在我们来测试一下,单实例的初始化以及销毁方法是什么时候进行调用的。
@Test
public void test08(){
System.out.println("IOC容器开始创建!");
AnnotationConfigApplicationContext ioc = new AnnotationConfigApplicationContext(FourthConfig.class);
System.out.println("IOC容器创建完成!");
System.out.println("下面开始关闭容器......");
ioc.close();
System.out.println("容器已经关闭了.....");
}
可以看到,单实例的对象是在容器开始创建的时候创建对象,在创建完对象之后就开始调用对象的初始化方法,在容器关闭之前会将对象销毁,即调用销毁方法。
也就是说,在容器创建完成之前,会先创建容器里面的bean对象,待bean对象创建完成并赋值好的之后再调用bean对象的初始化方法,然后容器才创建完成;而在容器进行关闭之前,会先调用对象的销毁方法,待所有对象都销毁完之后,容器才关闭完成;
上面演示的是单实例对象的初始化与销毁情况,接下来我们看看多实例bean的初始化与销毁情况:
@Configuration
public class FourthConfig {
@Scope("prototype") //将对象设置为多实例
@Bean(initMethod ="init",destroyMethod = "destroy")
public Car car(){
Car car = new Car();
return car;
}
}
运行测试方法
@Test
public void test08(){
AnnotationConfigApplicationContext ioc = new AnnotationConfigApplicationContext(FourthConfig.class);
System.out.println("IOC容器创建完成!");
System.out.println("下面开始关闭容器......");
ioc.close();
System.out.println("容器已经关闭了.....");
}
可以看到,在容器的创建与关闭的整个过程,都没有调用bean的初始化与销毁方法,这是因为多实例对象在容器中的创建是在调用该bean对象的时候才进行的。
@Test
public void test08(){
AnnotationConfigApplicationContext ioc = new AnnotationConfigApplicationContext(FourthConfig.class);
System.out.println("IOC容器创建完成!");
System.out.println("下面开始从容器中获取对象");
Object car = ioc.getBean("car");
System.out.println("对象获取完成");
System.out.println("下面开始关闭容器......");
ioc.close();
System.out.println("容器已经关闭了.....");
}
可以看到,在获取对象的时候才会调用多实例对象的初始化方法,而它的销毁方法没有被调用。也就是说,spring的ioc容器并不会帮我们管理多实例对象的销毁方法,你想调用的话只能自己来调用。
2.2、InitializingBean、InitializingBean接口
上面讲解的是使用Bean注解进行的生命周期管理,接下来我们来说说,另一种定义生命周期的方法:在定义bean的时候实现接口InitializingBean、InitializingBean。
正如InitializingBean接口的名字所示,该接口用来设置初始化方法的接口,它里面只包括afterPropertiesSet方法,凡是继承该接口的JavaBean类,在bean的属性初始化后都会执行该方法。
而DisposableBean也是顾名思义,用来设置bean的销毁方法。
接下来我们来演示一下,这种方式如何使用
首先,编写一个JavaBean,该JavaBean实现上面两个接口并实现相应的方法
public class Fish implements InitializingBean, DisposableBean {
@Override
public void destroy() throws Exception {
System.out.println("鱼儿的销毁方法");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("鱼儿的初始化方法");
}
}
将该JavaBean加入容器
@Configuration
public class FourthConfig {
@Bean
public Fish fish(){
return new Fish();
}
}
进行测试
@Test
public void test08(){
AnnotationConfigApplicationContext ioc = new AnnotationConfigApplicationContext(FourthConfig.class);
System.out.println("IOC容器创建完成!");
System.out.println("下面开始关闭容器......");
ioc.close();
System.out.println("容器已经关闭了.....");
}
同样,使用该方式时,单实例对象的初始化方法与销毁方法都是随着容器的创建与销毁进行的。
设置为多实例对象
@Configuration
public class FourthConfig {
@Scope("prototype")
@Bean
public Fish fish(){
return new Fish();
}
}
测试方法
@Test
public void test01(){
AnnotationConfigApplicationContext ioc = new AnnotationConfigApplicationContext(FourthConfig.class);
System.out.println("IOC容器创建完成!");
System.out.println("下面开始从容器中获取对象");
Object fish = ioc.getBean("fish");
System.out.println("对象获取完成");
System.out.println("下面开始关闭容器......");
ioc.close();
System.out.println("容器已经关闭了.....");
}
说明使用Bean注解与接口这两种方式都是一样的,只是方式不一样。
2.3、BeanPostProcessor后置处理器
2.4.1、使用实例
BeanPostProcessor是一个接口,它内部声明了两个方法:postProcessBeforeInitialization和postProcessAfterInitialization,在它的源码注释中表明这两个方法分别是在Spring容器中的bean初始化前后执行,所以Spring容器中的每一个bean对象初始化前后,都会执行BeanPostProcessor接口的实现类中的这两个方法。
也就是说,postProcessBeforeInitialization方法会在bean实例化和属性设置之后,自定义初始化方法之前被调用,而postProcessAfterInitialization方法会在自定义初始化方法之后被调用。当容器中存在多个BeanPostProcessor的实现类时,会按照它们在容器中注册的顺序执行。对于自定义的BeanPostProcessor实现类,还可以让其实现Ordered接口自定义排序。
因此我们可以在每个bean对象初始化前后,加上自己的逻辑。实现方式是自定义一个BeanPostProcessor接口的实现类,例如MyBeanPostProcessor,然后在该类的postProcessBeforeInitialization和postProcessAfterInitialization这俩方法中添加上自己的逻辑。并将该实现类加入容器。
现在我们来编写一个实现类看看
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;
/**
* 后置处理器,在初始化前后进行处理工作
*/
@Component // 将后置处理器加入到容器中,这样的话,Spring就能让它工作了
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
// TODO Auto-generated method stub
System.out.println("postProcessBeforeInitialization..." + beanName + "=>" + bean);
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
// TODO Auto-generated method stub
System.out.println("postProcessAfterInitialization..." + beanName + "=>" + bean);
return bean;
}
}
之前我们学过用InitializingBean、InitializingBean这两个接口来定义初始化方法,我们来看看加上这两个接口方法后,这些初始化方法、后置处理器方法会如何执行
JavaBean
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
public class Fish implements InitializingBean, DisposableBean {
public Fish() {
System.out.println("鱼儿的无参构造方法");
}
@Override
public void destroy() throws Exception {
System.out.println("鱼儿的销毁方法:DisposableBean的destroy方法");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("鱼儿的初始化方法:InitializingBean的afterPropertiesSet方法");
}
}
配置类
@ComponentScan(value = ".lsp.processor") //扫描处理器包,将处理器注册进容器
@Configuration
public class FourthConfig {
@Bean //单实例
public Fish fish(){
return new Fish();
}
}
测试
@Test
public void test10() {
AnnotationConfigApplicationContext ioc = new AnnotationConfigApplicationContext(FourthConfig.class);
System.out.println("容器创建完成!");
String[] names = ioc.getBeanDefinitionNames();
for (String name :
names) {
System.out.println("name: "+name);
}
System.out.println("容器开始关闭");
ioc.close();
}