Spring Bean的初始化和銷燬方式詳解
最近在專案中需要封裝kafka的服務,其中使用到了工廠模式,該模式涉及到了Spring Bean的初始化和銷燬,如是研究了一番,總結如下,和大家共勉之
Spring Bean的初始化和銷燬Bean有幾種方法了?答案是3種
方法一:使用@PostConstruct註解初始化,使用@PreDestroy註解銷燬Bean
示例程式碼如下:
public class PostConstructInit { @PostConstruct public void PostConstruct() { System.out.println("執行PostConstructInit: PostConstruct"); } @PreDestroy public void PreDestory(){ System.out.println("執行PostConstructInit: PreDestory"); } public static void main(String[] args) { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext( "classpath:beans.xml"); context.close(); } }
配置檔案如下:
<context:annotation-config/>
<context:component-scan base-package="com.chhliu.myself.spring.test"></context:component-scan>
<bean id="postConstructor" class="com.chhliu.myself.spring.test.PostConstructInit"></bean>
測試結果如下:
執行PostConstructInit: PostConstruct 執行PostConstructInit: PreDestory |
方法二:實現InitializingBean, DisposableBean這兩個介面,並複寫afterPropertiesSet()和destroy()方法
示例程式碼如下:
測試結果如下:public class InitializingDisposableInit implements InitializingBean, DisposableBean { @Override public void destroy() throws Exception { System.out.println("執行InitializingDisposableInit: destroy"); } @Override public void afterPropertiesSet() throws Exception { System.out.println("執行InitializingDisposableInit: afterPropertiesSet"); } public static void main(String[] args) { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext( "classpath:beans-impl.xml"); context.close(); } }
執行InitializingDisposableInit: afterPropertiesSet 執行InitializingDisposableInit: destroy |
方法三:使用init-method和destroy-method配置方法
示例程式碼如下:
public class MethodInit {
public void initMethod() {
System.out.println("執行MethodInit: init-method");
}
public void destroyMethod() {
System.out.println("執行MethodInit: destroy-method");
}
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
"classpath:beans-method.xml");
context.close();
}
}
配置檔案如下:
<bean id="methodInit" class="com.chhliu.myself.spring.test.MethodInit" init-method="initMethod" destroy-method="destroyMethod"></bean>
測試結果如下:
執行MethodInit: init-method 執行MethodInit: destroy-method |
那麼三種初始化和銷燬Spring Bean的方式就講完了,那麼這三種方式的執行順序是如何了,我們接著來做個測試
測試程式碼如下:
public class InitAndDestroySeqBean implements InitializingBean, DisposableBean {
public InitAndDestroySeqBean() {
System.out.println("執行InitAndDestroySeqBean: 構造方法");
}
@PostConstruct
public void postConstruct() {
System.out.println("執行InitAndDestroySeqBean: postConstruct");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("執行InitAndDestroySeqBean: afterPropertiesSet");
}
public void initMethod() {
System.out.println("執行InitAndDestroySeqBean: init-method");
}
@PreDestroy
public void preDestroy() {
System.out.println("執行InitAndDestroySeqBean: preDestroy");
}
@Override
public void destroy() throws Exception {
System.out.println("執行InitAndDestroySeqBean: destroy");
}
public void destroyMethod() {
System.out.println("執行InitAndDestroySeqBean: destroy-method");
}
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
"classpath:applicationContext.xml");
context.close();
}
}
測試結果如下:
執行InitAndDestroySeqBean: 構造方法
執行InitAndDestroySeqBean: postConstruct
執行InitAndDestroySeqBean: afterPropertiesSet
執行InitAndDestroySeqBean: init-method
執行InitAndDestroySeqBean: preDestroy
執行InitAndDestroySeqBean: destroy
執行InitAndDestroySeqBean: destroy-method
最後我們得出的結果如下:
初始化順序:
Constructor > @PostConstruct >InitializingBean > init-method
銷燬的順序如下:
@PreDestroy > DisposableBean > destroy-method
最後,我們總結下這幾種方式的特點
1、如果我們在afterPropertiesSet()方法中丟擲異常,那麼初始化會被終止,不會執行後面的init-method對應的初始化方法
2、Spring中初始化的Bean的類為
Org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory
3、afterPropertiesSet()方法的效率比init-method高,因為init-method使用的是反射來尋找對應的方法,而afterPropertiesSet()則是直接執行的,相關原始碼如下:
init-method:
String initMethodName = mbd.getInitMethodName();
final Method initMethod = (mbd.isNonPublicAccessAllowed() ?
BeanUtils.findMethod(bean.getClass(), initMethodName) :
ClassUtils.getMethodIfAvailable(bean.getClass(), initMethodName));
從上面的程式碼可以看出,使用的是放射方式
afterPropertiesSet():
protected void invokeInitMethods(String beanName, final Object bean, RootBeanDefinition mbd)
throws Throwable {
// 如果存在afterPropertiesSet方法且例項是InitializingBean型別的話,就直接執行afterPropertiesSet()方法
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 {
((InitializingBean) bean).afterPropertiesSet();
return null;
}
}, getAccessControlContext());
}
catch (PrivilegedActionException pae) {
throw pae.getException();
}
}
else {
((InitializingBean) bean).afterPropertiesSet();
}
}
}
4、init-method只能是無參無返回的public方法