使用spring容器在初始化Bean時前和后的操作
spring容器初始化Bean操作
在某些情況下,Spring容器在初始化Bean的時候,希望在初始化bean前和銷毀bean前進行一些資源的加載和釋放的操作。可以通過一下三種方式完成。
- Bean的方法加上@PostConstruct和@PreDestroy注解
- 在xml中定義init-method和destory-method方法
- Bean實現(xiàn)InitializingBean和DisposableBean接口
@PostConstruct和@PreDestroy注解
JavaBean代碼
@Component
public class PersonService {
private String message;
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
@PostConstruct
public void init() {
System.out.println("PersonService.class init method ...");
}
@PreDestroy
public void cleanUp() {
System.out.println("PersonService.class cleanUp method ...");
}
}
spring配置文件
<?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"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.2.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.2.xsd">
<!-- spring掃描的路徑 -->
<context:component-scan base-package="spring.zhujie" />
</beans>
測試代碼和結(jié)果
測試代碼
public static void main(String[] args) {
AbstractApplicationContext context = new ClassPathXmlApplicationContext("spring-zhujie.xml");
context.registerShutdownHook();
}
運行結(jié)果
PersonService.class init method ...
PersonService.class cleanUp method ...
在XML中定義init-method和destory-method方法
JavaBean代碼
public class PersonService {
private String message;
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public void init() {
System.out.println("PersonService.class init method ...");
}
public void cleanUp() {
System.out.println("PersonService.class cleanUp method ...");
}
}
spring配置文件
<bean class="spring.zhujie.PersonService" init-method="init" destroy-method="cleanUp"/>
測試代碼和結(jié)果
測試代碼
public static void main(String[] args) {
AbstractApplicationContext context = new ClassPathXmlApplicationContext("spring-xml.xml");
context.registerShutdownHook();
}
運行結(jié)果
PersonService.class init method ...
六月 23, 2017 9:42:06 下午 org.springframework.context.support.ClassPathXmlApplicationContext doClose
信息: Closing org.springframework.context.support.ClassPathXmlApplicationContext@7a94c5e7: startup date [Fri Jun 23 21:42:06 CST 2017]; root of context hierarchy
PersonService.class cleanUp method ...
Bean實現(xiàn)InitializingBean和DisposableBean接口
JavaBean代碼
public class PersonService implements InitializingBean, DisposableBean {
private String message;
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("PersonService.class init method ...");
}
@Override
public void destroy() throws Exception {
System.out.println("PersonService.class cleanUp method ...");
}
}
spring配置文件
<bean id="personService" class="spring.zhujie.PersonService" />
測試代碼和結(jié)果
測試代碼
public static void main(String[] args) {
AbstractApplicationContext context = new ClassPathXmlApplicationContext("spring-interface.xml");
context.registerShutdownHook();
}
運行結(jié)果
PersonService.class init method ...
PersonService.class cleanUp method ...
Spring bean 初始化順序
InitializingBean, init-method 和 PostConstruct
1、概述
從接口的名字上不難發(fā)現(xiàn),InitializingBean 的作用就是在 bean 初始化后執(zhí)行定制化的操作。
Spring 容器中的 Bean 是有生命周期的,Spring 允許在 Bean 在初始化完成后以及 Bean 銷毀前執(zhí)行特定的操作,常用的設(shè)定方式有以下三種:
通過實現(xiàn) InitializingBean/DisposableBean 接口來定制初始化之后/銷毀之前的操作方法;
通過 <bean> 元素的 init-method/destroy-method 屬性指定初始化之后 /銷毀之前調(diào)用的操作方法;
在指定方法上加上@PostConstruct 或@PreDestroy注解來制定該方法是在初始化之后還是銷毀之前調(diào)用。
2、InitializingBean vs init-method
接口定義如下:
public interface InitializingBean {
void afterPropertiesSet() throws Exception;
}
接口只有一個方法afterPropertiesSet,
此方法的調(diào)用入口是負責(zé)加載 spring bean 的AbstractAutowireCapableBeanFactory,源碼如下:
protected void invokeInitMethods(String beanName, Object bean,
RootBeanDefinition mbd) throws Throwable {
boolean isInitializingBean = bean instanceof InitializingBean;
if ((isInitializingBean)
&& (((mbd == null) || (!(mbd
.isExternallyManagedInitMethod("afterPropertiesSet")))))) {
if (this.logger.isDebugEnabled()) {
this.logger
.debug("Invoking afterPropertiesSet() on bean with name '"
+ beanName + "'");
}
//先調(diào)用afterPropertiesSet()進行初始化
if (System.getSecurityManager() != null) {
try {
AccessController.doPrivileged(
new PrivilegedExceptionAction(bean) {
public Object run() throws Exception {
((InitializingBean) this.val$bean)
.afterPropertiesSet();
return null;
}
}, getAccessControlContext());
} catch (PrivilegedActionException pae) {
throw pae.getException();
}
} else {
((InitializingBean) bean).afterPropertiesSet();
}
}
//然后調(diào)用InitMethod()進行初始化
if (mbd != null) {
String initMethodName = mbd.getInitMethodName();
if ((initMethodName == null)
|| ((isInitializingBean) && ("afterPropertiesSet"
.equals(initMethodName)))
|| (mbd.isExternallyManagedInitMethod(initMethodName)))
return;
invokeCustomInitMethod(beanName, bean, mbd);
}
}
從這段源碼可以得出以下結(jié)論:
spring為bean提供了兩種初始化bean的方式,實現(xiàn)InitializingBean接口,實現(xiàn)afterPropertiesSet方法,或者在配置文件中通過init-method指定,兩種方式可以同時使用
實現(xiàn)InitializingBean接口是直接調(diào)用afterPropertiesSet方法,比通過反射調(diào)用init-method指定的方法效率相對來說要高點。但是init-method方式消除了對spring的依賴
先調(diào)用afterPropertiesSet,再執(zhí)行 init-method 方法,如果調(diào)用afterPropertiesSet方法時出錯,則不調(diào)用init-method指定的方法
3、@PostConstruct
通過 debug 和調(diào)用棧找到類InitDestroyAnnotationBeanPostProcessor, 其中的核心方法,即 @PostConstruct 方法調(diào)用的入口:
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass());
try {
metadata.invokeInitMethods(bean, beanName);
}
catch (InvocationTargetException ex) {
throw new BeanCreationException(beanName, "Invocation of init method failed", ex.getTargetException());
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Failed to invoke init method", ex);
}
return bean;
}
從命名上,我們就可以得到某些信息——這是一個BeanPostProcessor。BeanPostProcessor的postProcessBeforeInitialization是在Bean生命周期中afterPropertiesSet和init-method之前被調(diào)用的。另外通過跟蹤,@PostConstruct方法的調(diào)用方式也是通過反射機制。
4、小結(jié)一下吧
spring bean的初始化執(zhí)行順序:構(gòu)造方法 --> @PostConstruct注解的方法 --> afterPropertiesSet方法 --> init-method指定的方法。具體可以參考例子
afterPropertiesSet通過接口實現(xiàn)方式調(diào)用(效率上高一點),@PostConstruct和init-method都是通過反射機制調(diào)用
同理,bean銷毀過程的順序為:@PreDestroy > DisposableBean > destroy-method
不再展開,看源碼就好
測試代碼如下:
@Slf4j
public class InitSequenceBean implements InitializingBean {
public InitSequenceBean() {
log.info("InitSequenceBean: construct");
}
@Override
public void afterPropertiesSet() throws Exception {
log.info("InitSequenceBean: afterPropertiesSet");
}
@PostConstruct
public void postConstruct() {
log.info("InitSequenceBean: postConstruct");
}
public void initMethod() {
log.info("InitSequenceBean: initMethod");
}
}
@Configuration
public class SystemConfig {
@Bean(initMethod = "initMethod", name = "initSequenceBean")
public InitSequenceBean initSequenceBean() {
return new InitSequenceBean();
}
}
@Slf4j
public class InitSequenceBeanTest extends ApplicationTests {
@Autowired
private InitSequenceBean initSequenceBean;
@Test
public void initSequenceBeanTest() {
log.info("Finish: {}", initSequenceBean.toString());
}
}
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
詳解spring boot jpa整合QueryDSL來簡化復(fù)雜操作
這篇文章主要介紹了詳解spring boot jpa整合QueryDSL來簡化復(fù)雜操作,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-04-04
說說字符串轉(zhuǎn) OffSetDateTime 你真的會用嗎
這篇文章主要介紹了字符串轉(zhuǎn) OffSetDateTime 你真的會用嗎?具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-08-08
Java使用Collections.sort對中文進行排序方式
這篇文章主要介紹了Java使用Collections.sort對中文進行排序方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-11-11

