1. 程式人生 > >Spring Bean的初始化和銷燬方式詳解

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-methoddestroy-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