您好,登錄后才能下訂單哦!
本篇文章給大家分享的是有關(guān)使用spring容器在初始化Bean時(shí)前和后的操作是怎樣的,小編覺得挺實(shí)用的,因此分享給大家學(xué)習(xí),希望大家閱讀完這篇文章后可以有所收獲,話不多說(shuō),跟著小編一起來(lái)看看吧
在某些情況下,Spring容器在初始化Bean的時(shí)候,希望在初始化bean前和銷毀bean前進(jìn)行一些資源的加載和釋放的操作。可以通過(guò)一下三種方式完成。
Bean的方法加上@PostConstruct和@PreDestroy注解
在xml中定義init-method和destory-method方法
Bean實(shí)現(xiàn)InitializingBean和DisposableBean接口
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>
測(cè)試代碼和結(jié)果
測(cè)試代碼
public static void main(String[] args) { AbstractApplicationContext context = new ClassPathXmlApplicationContext("spring-zhujie.xml"); context.registerShutdownHook(); }
運(yùn)行結(jié)果
PersonService.class init method ...
PersonService.class cleanUp 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"/>
測(cè)試代碼和結(jié)果
測(cè)試代碼
public static void main(String[] args) { AbstractApplicationContext context = new ClassPathXmlApplicationContext("spring-xml.xml"); context.registerShutdownHook(); }
運(yùn)行結(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 ...
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" />
測(cè)試代碼和結(jié)果
測(cè)試代碼
public static void main(String[] args) { AbstractApplicationContext context = new ClassPathXmlApplicationContext("spring-interface.xml"); context.registerShutdownHook(); }
運(yùn)行結(jié)果
PersonService.class init method ...
PersonService.class cleanUp method ...
InitializingBean, init-method 和 PostConstruct
從接口的名字上不難發(fā)現(xiàn),InitializingBean 的作用就是在 bean 初始化后執(zhí)行定制化的操作。
Spring 容器中的 Bean 是有生命周期的,Spring 允許在 Bean 在初始化完成后以及 Bean 銷毀前執(zhí)行特定的操作,常用的設(shè)定方式有以下三種:
通過(guò)實(shí)現(xiàn) InitializingBean/DisposableBean 接口來(lái)定制初始化之后/銷毀之前的操作方法;
通過(guò) <bean> 元素的 init-method/destroy-method 屬性指定初始化之后 /銷毀之前調(diào)用的操作方法;
在指定方法上加上@PostConstruct 或@PreDestroy注解來(lái)制定該方法是在初始化之后還是銷毀之前調(diào)用。
接口定義如下:
public interface InitializingBean { void afterPropertiesSet() throws Exception; }
接口只有一個(gè)方法afterPropertiesSet,
此方法的調(diào)用入口是負(fù)責(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()進(jìn)行初始化 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()進(jìn)行初始化 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的方式,實(shí)現(xiàn)InitializingBean接口,實(shí)現(xiàn)afterPropertiesSet方法,或者在配置文件中通過(guò)init-method指定,兩種方式可以同時(shí)使用
實(shí)現(xiàn)InitializingBean接口是直接調(diào)用afterPropertiesSet方法,比通過(guò)反射調(diào)用init-method指定的方法效率相對(duì)來(lái)說(shuō)要高點(diǎn)。但是init-method方式消除了對(duì)spring的依賴
先調(diào)用afterPropertiesSet,再執(zhí)行 init-method 方法,如果調(diào)用afterPropertiesSet方法時(shí)出錯(cuò),則不調(diào)用init-method指定的方法
通過(guò) 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; }
從命名上,我們就可以得到某些信息——這是一個(gè)BeanPostProcessor。BeanPostProcessor的postProcessBeforeInitialization是在Bean生命周期中afterPropertiesSet和init-method之前被調(diào)用的。另外通過(guò)跟蹤,@PostConstruct方法的調(diào)用方式也是通過(guò)反射機(jī)制。
spring bean的初始化執(zhí)行順序:構(gòu)造方法 --> @PostConstruct注解的方法 --> afterPropertiesSet方法 --> init-method指定的方法。具體可以參考例子
afterPropertiesSet通過(guò)接口實(shí)現(xiàn)方式調(diào)用(效率上高一點(diǎn)),@PostConstruct和init-method都是通過(guò)反射機(jī)制調(diào)用
同理,bean銷毀過(guò)程的順序?yàn)椋篅PreDestroy > DisposableBean > destroy-method
不再展開,看源碼就好
測(cè)試代碼如下:
@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()); } }
以上就是使用spring容器在初始化Bean時(shí)前和后的操作是怎樣的,小編相信有部分知識(shí)點(diǎn)可能是我們?nèi)粘9ぷ鲿?huì)見到或用到的。希望你能通過(guò)這篇文章學(xué)到更多知識(shí)。更多詳情敬請(qǐng)關(guān)注億速云行業(yè)資訊頻道。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。