1、@Bean注解方式
public class TestBean {
public TestBean() {
System.out.println("constructor");
}
/**
* 初始化的方法名称不一定要使用init,
* 使用init主要是为了使类结构更清晰,并且不会让人误以为此方法是一个普通方法
*/
public void init() {
System.out.println("init");
}
/**
* 销毁的方法名不一定要使用destroy
*/
public void destroy() {
System.out.println("destroy");
}
}
- 添加配置类
使用此配置类,将bean添加到IoC容器中
注意@Bean注解中的initMethod = "init"
和destroyMethod = "destroy"
,init
和destroy
是TestBean 类中的定义的初始化和销毁所对应的方法名
@Configuration
public class BeanConfig {
/**
* 如果没有在@Bean注解中指定bean的名称,默认bean的名称是 方法名 也就是 testBean
* ps: 可以通过注入ApplicationContext调用getBeanDefinitionNames()方法来获取所有IoC容器中bean的名称
*/
@Bean(initMethod = "init", destroyMethod = "destroy")
public TestBean testBean() {
return new TestBean();
}
}
运行结果
2021-03-29 11:25:56.426 INFO 10884 --- [ main] com.example.demo.DemoApplication : No active profile set, falling back to default profiles: default
constructor
init
2021-03-29 11:25:56.845 INFO 10884 --- [ main] com.example.demo.DemoApplication : Started DemoApplication in 0.817 seconds (JVM running for 1.339)
destroy
2、 使用@PostConstruct和@PreDestroy
public class TestBean {
public TestBean() {
System.out.println("constructor");
}
@PostConstruct
public void init() {
System.out.println("init");
}
@PreDestroy
public void destroy() {
System.out.println("destroy");
}
}
配置类
@Configuration
public class BeanConfig {
@Bean
public TestBean testBean() {
return new TestBean();
}
}
运行结果
2021-03-29 11:32:21.130 INFO 11056 --- [ main] com.example.demo.DemoApplication : No active profile set, falling back to default profiles: default
constructor
init
2021-03-29 11:32:21.530 INFO 11056 --- [ main] com.example.demo.DemoApplication : Started DemoApplication in 0.677 seconds (JVM running for 1.217)
destroy
3、xml配置方式
在spring的xml配置文件中进行如下配置
<bean id="testBean" class="com.example.demo" init-method="init" destroy-method="destroy">
可使用以下方式运行
public class MainTest {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
context.close();
}
}
如果不是SpringBoot的项目,可使用如下方式获取应用上下文
public class MainTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(BeanConfig.class);
context.close();
}
}
init与InitializingBean
工作中经常使用到 InitializingBean
接口
实现此接口就要实现其抽象方法:afterPropertiesSet。在此方法中可进行另外的初始化工作,那么init方法和afterPropertiesSet方法的调用顺序是怎样的呢?
1、 使用 @PostConstruct 和 @PreDestroy
public class TestBean implements InitializingBean {
static {
System.out.println("静态块");
}
private String testStr;
@Value("${test.str:hello}")
public void setTestStr(String testStr) {
System.out.println("set : hello");
this.testStr = testStr;
}
public TestBean() {
System.out.println("constructor");
}
@PostConstruct
public void init() {
System.out.println("post construct");
}
@PreDestroy
public void destroy() {
System.out.println("pre destroy");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("afterPropertiesSet " + this.testStr);
}
}
@Configuration
public class BeanConfig {
@Bean
public TestBean testBean() {
return new TestBean();
}
}
运行结果
2021-03-29 14:01:33.594 INFO 10252 --- [ main] com.example.demo.DemoApplication : No active profile set, falling back to default profiles: default
静态块
constructor
set : hello
post construct
afterPropertiesSet hello
2021-03-29 14:01:33.988 INFO 10252 --- [ main] com.example.demo.DemoApplication : Started DemoApplication in 0.684 seconds (JVM running for 1.278)
pre destroy
执行的顺序是:
static block(加载类结构)
> constructor(执行构造方法进行实例化)
> auto wire(为成员变量赋值)
> post constructor method
> afterPropertiesSet
> pre destroy method
2、在@Bean注解中进行配置
public class TestBean implements InitializingBean {
static {
System.out.println("静态块");
}
private String testStr;
@Value("${test.str:hello}")
public void setTestStr(String testStr) {
System.out.println("set : hello");
this.testStr = testStr;
}
public TestBean() {
System.out.println("constructor");
}
public void init() {
System.out.println("init");
}
public void destroy() {
System.out.println("destroy");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("afterPropertiesSet " + this.testStr);
}
}
@Configuration
public class BeanConfig {
@Bean(initMethod = "init", destroyMethod = "destroy")
public TestBean testBean() {
return new TestBean();
}
}
2021-03-29 14:11:56.940 INFO 6308 --- [ main] com.example.demo.DemoApplication : No active profile set, falling back to default profiles: default
静态块
constructor
set : hello
afterPropertiesSet hello
init
2021-03-29 14:11:57.345 INFO 6308 --- [ main] com.example.demo.DemoApplication : Started DemoApplication in 0.687 seconds (JVM running for 1.249)
destroy
执行的顺序是:
static block(加载类结构)
> constructor(执行构造方法进行实例化)
> auto wire(为成员变量赋值)
> afterPropertiesSet
> init method
> destroy method
3、尝试1和2这两种方式同时使用
public class TestBean implements InitializingBean {
static {
System.out.println("静态块");
}
private String testStr;
@Value("${test.str:hello}")
public void setTestStr(String testStr) {
System.out.println("set : hello");
this.testStr = testStr;
}
public TestBean() {
System.out.println("constructor");
}
@PostConstruct
public void postConstruct() {
System.out.println("postConstruct");
}
public void init() {
System.out.println("init");
}
@PreDestroy
public void preDestroy() {
System.out.println("preDestroy");
}
public void destroy() {
System.out.println("destroy");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("afterPropertiesSet " + this.testStr);
}
}
@Configuration
public class BeanConfig {
@Bean(initMethod = "init", destroyMethod = "destroy")
public TestBean testBean() {
return new TestBean();
}
}
运行结果
2021-03-29 14:16:48.056 INFO 10724 --- [ main] com.example.demo.DemoApplication : No active profile set, falling back to default profiles: default
静态块
constructor
set : hello
postConstruct
afterPropertiesSet hello
init
2021-03-29 14:16:48.435 INFO 10724 --- [ main] com.example.demo.DemoApplication : Started DemoApplication in 0.657 seconds (JVM running for 1.244)
preDestroy
destroy
总结:
- 定义bean的 init 和 destroy 有多种方式可供选择,既可以在@Bean注解中进行定义,也可以使用
@PostConstruct
和@PreDestroy
,大多数情况下可达到我们想要的效果。 @Bean(initMethod = "")
在结合InitializingBean
使用时,需要注意的是 Spring 会先调用 afterPropertiesSet 再调用 init method; 而如果使用的是@PostConstruct
来定义 init method,顺序与前者相反,因为PostConstruct直译过来就是构造方法执行后
- constructor
- auto wire
- post construct
- afterPropertiesSet
- init method
这个细节需要注意一下,避免以后踩到坑。
附上一段InitializingBean相关的源代码
protected void invokeInitMethods(String beanName, final Object bean, RootBeanDefinition mbd) throws Throwable {
//判断bean是否实现了InitializingBean接口
boolean isInitializingBean = (bean instanceof InitializingBean);
if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
if (logger.isDebugEnabled()) {
logger.debug("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
}
if (System.getSecurityManager() != null) {
try {
AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
public Object run() throws Exception {
//调用afterPropertiesSet
((InitializingBean) bean).afterPropertiesSet();
return null;
}
}, getAccessControlContext());
} catch (PrivilegedActionException pae) {
throw pae.getException();
}
} else {
//调用afterPropertiesSet
((InitializingBean) bean).afterPropertiesSet();
}
}
if (mbd != null) {
String initMethodName = mbd.getInitMethodName();
//判断是否指定了init-method
if (initMethodName != null && !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
!mbd.isExternallyManagedInitMethod(initMethodName)) {
invokeCustomInitMethod(beanName, bean, mbd);
}
}
}
BeanPostProcessor
另外,如果使用到了BeanPostProcessor
@Component
public class PostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if ("testBean".equals(beanName)) {
System.out.println("------> beforeInitialization " + beanName);
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if ("testBean".equals(beanName)) {
System.out.println("------> afterInitialization " + beanName);
}
return bean;
}
}
运行结果:
2021-03-29 16:38:02.670 INFO 3336 --- [ main] com.example.demo.DemoApplication : No active profile set, falling back to default profiles: default
静态块
constructor
set : hello
------> beforeInitialization testBean
postConstruct
afterPropertiesSet hello
init
------> afterInitialization testBean
2021-03-29 16:38:03.090 INFO 3336 --- [ main] com.example.demo.DemoApplication : Started DemoApplication in 0.71 seconds (JVM running for 1.329)
preDestroy
destroy
postProcessBeforeInitialization
方法的执行要先于@PostConstruct
所标注的方法
postProcessAfterInitialization
方法将在 init method 方法执行后被调用
参考自:
https://blog.youkuaiyun.com/tuzongxun/article/details/53580695
https://blog.youkuaiyun.com/weixin_39222112/article/details/89000959