Chapter 03 - Bean的生命周期
Bean的声明周期是指Bean创建➡️初始化➡️销毁的过程
XML配置注册Bean时,bean标签除了id class属性还有init-method和destroy-method,这两个方法就是配置bean的初始化方法和销毁方法
Section 01 - 单实例Bean的生命周期
以Person实体类为例,增加初始化方法和销毁方法,自定义Bean初始化和销毁
public void init(){ System.out.println("Person Bean 初始化方法调用"); } public void destroy(){ System.out.println("Person Bean 销毁方法调用"); } 复制代码
在config包中定一个BeanLifeCycleConfig配置类,声明了initMethod和destroyMethod,注意不要加(),代码如下
@Configuration public class BeanLifeCycleConfig { @Bean(value = "stark", initMethod = "init", destroyMethod = "destroy") public Person stark(){ System.out.println("stark被实例化"); Person person = new Person(); person.setName("stark"); person.setAge(40); return person; } } 复制代码
在test包中新增一个BeanLifeCycleTest
public class BeanLifeCycleTest { @Test public void getBeanByImport(){ ApplicationContext context = new AnnotationConfigApplicationContext(BeanLifeCycleConfig.class); System.out.println("IoC容器初始化完成"); String[] beanDefinitionNames = context.getBeanDefinitionNames(); for (String beanDefinitionName : beanDefinitionNames) { System.out.println(beanDefinitionName); } // 关闭容器 ((AnnotationConfigApplicationContext)context).close(); } } 复制代码
执行测试
close()方法底层最终调用的是map的clear()方法,也就是清空map容器
容器入口类AnnotationConfigApplicationContext
容器创建Bean最终是调用的BeanUtils的instantiateClass()方法,该方法中使用了反射中的newInstance方法来实例化Bean
Section 02 - 多实例Bean的生命周期
在配置类中@Bean注解中配置多实例
@Configuration public class BeanLifeCycleConfig { @Scope("prototype") @Bean(value = "stark", initMethod = "init", destroyMethod = "destroy") public Person stark(){ System.out.println("stark被实例化"); Person person = new Person(); person.setName("stark"); person.setAge(40); return person; } } 复制代码
执行测试,初始化和销毁方法都没有调用
多实例时只有调用getBean()方法时才会创建Bean,修改测试方法,增加获取Bean的代码
@Test public void getBeanByImport(){ AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(BeanLifeCycleConfig.class); System.out.println("IoC容器初始化完成"); String[] beanDefinitionNames = context.getBeanDefinitionNames(); for (String beanDefinitionName : beanDefinitionNames) { System.out.println(beanDefinitionName); } Person bean = context.getBean(Person.class); // 关闭容器 context.close(); } 复制代码
控制台打印如下,只调用了初始化方法
对于多实例的bean,容器只负责初始化, 但不会管理bean, 容器关闭时不会调用销毁方法
Section 03 - 自定义Bean生命周期的第二种方式
- 实现InitializingBean接口的afterPropertiesSet()方法,当beanFactory创建好对象,且把bean所有属性设置好之后,会调这个方法,相当于初始化方法
- 实现DisposableBean的destory()方法,当bean销毁时,会把单实例bean进行销毁 在entity包中新建一个Item实体类,实现InitializingBean和DisposableBean
public class Item implements InitializingBean, DisposableBean { private Integer id; private String itemName; public Item(Integer id, String itemName) { System.out.println("Item 有参构造方法被调用"); this.id = id; this.itemName = itemName; } public Integer getId() { System.out.println("getId()方法被调用"); return id; } public void setId(Integer id) { System.out.println("setId()方法被调用"); this.id = id; } public String getItemName() { System.out.println("getItemName()方法被调用"); return itemName; } public void setItemName(String itemName) { System.out.println("setItemName()方法被调用"); this.itemName = itemName; } public Item(){ System.out.println("Item 空参构造方法被调用"); } // Bean销毁时调用 @Override public void destroy() throws Exception { System.out.println("Item Bean 销毁方法被调用"); } // Bean属性赋值和初始化完成后调用 @Override public void afterPropertiesSet() throws Exception { System.out.println("Item Bean 初始化方法被调用,属性被赋值后调用"); } } 复制代码
修改BeanLifeCycleConfig配置类,增加@Import注解,把Item注册到IoC容器中
@Configuration @Import(value = Item.class) public class BeanLifeCycleConfig { } 复制代码
注释BeanLifeCycleTest测试类方法中获取Person Bean的代码
public class BeanLifeCycleTest { @Test public void getBeanByImport(){ AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(BeanLifeCycleConfig.class); System.out.println("IoC容器初始化完成"); String[] beanDefinitionNames = context.getBeanDefinitionNames(); for (String beanDefinitionName : beanDefinitionNames) { System.out.println(beanDefinitionName); } // Person bean = context.getBean(Person.class); // 关闭容器 context.close(); } } 复制代码
执行测试,查看控制台打印结果
Section 04 - 自定义Bean生命周期的第三种方式
可以使用JSR250规则定义的(java规范)两个注解来实现
- @PostConstruct: 在Bean创建完成,且属于赋值完成后进行初始化,属于JDK规范的注解
- @PreDestroy: 在bean将被移除之前进行通知, 在容器销毁之前进行清理工作 在entity包中新增一个实体类Address
public class Address { private Integer id; private String addressDetail; public Address() { System.out.println("Address空参构造方法被调用"); } public Address(Integer id, String addressDetail) { System.out.println("Address有参数构造方法被调用"); this.id = id; this.addressDetail = addressDetail; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getAddressDetail() { return addressDetail; } public void setAddressDetail(String addressDetail) { this.addressDetail = addressDetail; } @PostConstruct public void init(){ System.out.println("Address Bean 的 @PostContruct注解形式的Bean初始化方法"); } @PreDestroy public void destroy(){ System.out.println("Address Bean 的 @PreDestry注解形式的Bean销毁方法"); } } 复制代码
修改BeanLifeCycleConfig方法@Import注解,改@Import(Address.class),即往容器中注入Address Bean,BeanLifeCycleTest类不需要修改,直接执行测试方法,控制它打印如下
Section 05 - Bean生命周期之BeanPostProcessor
Bean的后置处理器,在bean初始化之前调用进行拦截,在bean初始化前后进行一些处理工作,使用BeanPostProcessor如何控制Bean的生命周期,实现BeanPostProcessor的两个接口即可
- postProcessBeforeInitialization()
- postProcessAfterInitialization()
自定义类CustBeanPostProcessor,实现BeanPostProcessor
public class CustBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println(beanName + "postProcessBeforeInitialization前置处理器调用"); return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println(beanName + "postProcessAfterInitialization后置处理器调用"); return bean; } } 复制代码
修改BeanLifeCycleConfig,将自定义的CustBeanPostProcessor注册到容器中,在注册一些其他的Bean,Person是使用第一种生命周期定义方式,Item是第二种,Address是第三种,
@Configuration //@Import(value = Item.class) //@Import(value = Address.class) @Import({CustBeanPostProcessor.class, Item.class, Address.class}) public class BeanLifeCycleConfig { @Bean(initMethod = "init",destroyMethod = "destroy") public Person person(){ System.out.println("Person Bean被实例化"); Person person = new Person(); person.setName("Stark"); person.setAge(40); return person; } } 复制代码
执行测试类,确定postProcessBeforeInitialization(),postProcessAfterInitialization()是否支持这三种初始化方法前后调用
CustBeanPostProcessor的初始化前后拦截是针对容器中的所有业务Bean,不包括它本身
多实例情况下如何?修改BeanLifeCycleConfig,给Person添加多实例注解@Scope
@Configuration //@Import(value = Item.class) //@Import(value = Address.class) @Import({CustBeanPostProcessor.class, Item.class, Address.class}) public class BeanLifeCycleConfig { @Scope("prototype") @Bean(initMethod = "init",destroyMethod = "destroy") public Person person(){ System.out.println("Person Bean被实例化"); Person person = new Person(); person.setName("Stark"); person.setAge(40); return person; } } 复制代码
在打印方法中添加bean,将Bean实例也打印在控制台
public class CustBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println(beanName + "postProcessBeforeInitialization前置处理器调用" + bean); return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println(beanName + "postProcessAfterInitialization后置处理器调用" + bean); return bean; } } 复制代码
在BeanLifeCycleTest中两次获取Person Bean
public class BeanLifeCycleTest { @Test public void getBeanByImport(){ AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(BeanLifeCycleConfig.class); System.out.println("IoC容器初始化完成"); String[] beanDefinitionNames = context.getBeanDefinitionNames(); for (String beanDefinitionName : beanDefinitionNames) { System.out.println(beanDefinitionName); } Person bean = context.getBean(Person.class); Person bean1 = context.getBean(Person.class); // 关闭容器 context.close(); } } 复制代码
查看控制台打印
自定义CustBeanPostProcessor对初始化方法的前后拦截针对多实例Bean同样适用