目录
一、Bean的生命周期
- 一图胜千言
- 什么是钩子?(如何理解钩子?)
- 钩子可以扩展程序的执行流程。
二、正确使用bean创建后和销毁前的钩子
0 概览:3套方法
创建bean之后做的钩子 | 销毁bean之前做的钩子 |
---|---|
@PostConstruct | @PreDestroy |
覆写InitializingBean的afterPropertiesSet()方法 | 覆写DisposableBean的destroy()方法 |
xml配bean,配置init-method | xml配bean,配置destroy-method |
1 钩子:创建bean之后做的处理
1.1 为啥@PostConstruct没效果?
- 代码
public class Hello implements InitializingBean {
public Hello() {
System.out.println("Hello: construct");
}
@PostConstruct
public void postConstruct() {
System.out.println("hello: postConstruct");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("hello: afterPropertiesSet");
}
public void init() {
System.out.println("hello: init");
}
}
public class Application {
public static void main(String[] args) {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("lifecycle.xml");
Arrays.stream(applicationContext.getBeanDefinitionNames()).forEach(System.out::println);
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="hello" class="com.forrest.learnspring.lifecycle.example1.bean.Hello" init-method="init" />
</beans>
- 结果:
Hello: construct
hello: afterPropertiesSet
hello: init
hello
- 为啥
@PostConstruct
没效果?- 因为,通过xml的方式创建bean,要让@PostConstruct生效,需要补上:
<context:annotation-config/>
- 因为,通过xml的方式创建bean,要让@PostConstruct生效,需要补上:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config />
<bean id="hello" class="com.forrest.learnspring.lifecycle.example1.bean.Hello" init-method="init" />
</beans>
- 为啥这么写就有效果?
- 处理
@PostConstruct
这个注解需要的注解处理器是InitDestroyAnnotationBeanPostProcessor
类的postProcessBeforeInitialization
方法。【<context:annotation-config/>
会注入CommonAnnotationBeanPostProcessor
,它继承了InitDestroyAnnotationBeanPostProcessor
】 - 这个后置处理器会识别并处理
@PostConstruct
和@PreDestroy
注解,分别用于创建Bean之后做的处理和销毁Bean之前做的处理。
- 处理
Hello: construct
hello: postConstruct
hello: afterPropertiesSet
hello: init
hello
1.2 3种方法的优先级
- @PostConstruct > afterPropertiesSet() > init-method
实际开发中,极少会同时使用三套方法。
1.3 一般用:@PostConstruct 或 afterPropertiesSet()【我更喜欢@PostConstruct】
- 我个人觉得:实现InitializingBean接口并覆写afterPropertiesSet()方法的代码侵入性较高,因此,我更喜欢@PostConstruct。
- 示例:
@Component
public class Hello {
@PostConstruct
public void postConstruct() {
System.out.println("Hello.postConstruct()");
}
public void say() {
System.out.println("Hello World!");
}
}
@ComponentScan
public class Application {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Application.class);
Hello hello = applicationContext.getBean(Hello.class);
hello.say();
}
}
Hello.postConstruct()
Hello World!
2 钩子:销毁bean之前做的处理
2.1 3种方法的示例
public class Hello implements DisposableBean {
public Hello() {
System.out.println("Hello: constructor");
}
@PreDestroy
public void preDestroy() {
System.out.println("Hello: preDestroy");
}
@Override
public void destroy() throws Exception {
System.out.println("Hello: destroy");
}
public void xmlDestroyMethod() {
System.out.println("Hello: xmlDestroyMethod");
}
}
public class Application {
public static void main(String[] args) {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("lifecycle2.xml");
applicationContext.close();
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
<bean id="hello" class="com.forrest.learnspring.lifecycle.example3.bean.Hello" destroy-method="xmlDestroyMethod" />
</beans>
- 结果
Hello: constructor
Hello: preDestroy
Hello: destroy
Hello: xmlDestroyMethod
2.2 3种方法的优先级
- @PreDestroy > destroy() > destroy-method
2.3 一般用@PreDestroy或destroy() 【我更喜欢@PreDestroy】
- 首先,有了注解后,xml配bean的写法比较少了,能看懂这种写法即可。
- 而且,提供的xmlDestroyMethod方法和普通方法的定义没任何区别,代码可读性也不高。
- 其次,实现DisposableBean接口并覆写destroy()的代码侵入性较高。
- 因此,我更喜欢@PreDestroy。
目前觉得注解真是不错的语法。
- 示例:
@Component
public class Hello {
@PreDestroy
public void preDestroy() {
System.out.println("Hello: preDestroy");
}
}
@ComponentScan
public class Application {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Application.class);
applicationContext.close();
}
}