Spring源碼深度解析-初始化bean

1、Spring初始化bean

在我們的項目中會使用@Controller、@Service等註解方式或標籤配置文件的形式向Spring容器注入各種bean,其本質是Spring框架掃描我們添加了@Controller、@Service等註解的類或解析標籤的class屬性利用java反射機制創建bean對象的過程。本篇主要從Spring源碼級別,討論Spring在創建bean對象後,Spring是如何調用各種初始化方法來初始化這個bean對象的。

2、BeanFactory的初始化Bean

2.1 BeanFactory接口和ApplicationContext接口

BeanFactory是Spring框架中最基本的一個接口,注意體會這個最字和接口。即這個接口是定義了一個bean容器的最基本功能方法,比如獲取bean實例getBean()、獲取bean實例的類型getType(),那麼它的具體實現類有DefaultListableBeanFactory。
ApplicationContext是Spring框架中功能更加全面的,也是在我們的項目中經常能看到它的身影的一個接口。它繼承了BeanFactory接口,是Spring更高一級的容器,它在BeanFactory的基礎上提供了更多的功能,比如國際化、消息發送、AOP等,那麼它的實現類有ClassPathXmlApplicationContext、FileSystemXmlApplicationContext、
AnnotationConfigApplicationContext。
假如把我們的bean比作是一滴一滴的水,一個bean就是一滴水,那麼BeanFactory就好比是一個能夠盛水的水壺,能做到的只是簡單的盛水,而ApplicationContext就是一個電水壺不僅能盛水,還能加熱,水開了還能提示。比如我們的DefaultListableBeanFactory就是一個普通的玻璃水壺,而ClassPathXmlApplicationContext就是一個電水壺。

2.2 BeanFactory的初始化Bean

那麼,BeanFactory的實現類AbstractAutowireCapableBeanFactory是如何初始化bean的呢?具體的初始化步驟如下圖。
在這裏插入圖片描述
Spring容器就是按照這個步驟,一步一步的順序進行bean的初始化代碼執行,最終初始化完成交給給我們使用的。網上很多的博客都是每一個步驟簡單解釋了一下,然後就不了了之,只能讓我去死記硬背這個流程,現在咱們看看spring源碼是如何實現這個流程。如果讓咱們自己寫代碼實現這個流程呢?

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean(String, Object, RootBeanDefinition)
protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
	if (System.getSecurityManager() != null) {
        // 暫時忽略,以後再分析
		AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
			invokeAwareMethods(beanName, bean);
			return null;
		}, getAccessControlContext());
	}
	else {
        //1、對實現了BeanNameAware、BeanClassLoaderAware、
        // BeanFactoryAware的bean進行處理
		invokeAwareMethods(beanName, bean);
	}

	Object wrappedBean = bean;
	if (mbd == null || !mbd.isSynthetic()) {
        //2、調用  bean後置處理器  的初始化前置方法
		wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
	}

	try {
        //3、先調用InitializingBean的afterPropertiesSet()方法
        //後調用用戶自定義的初始化方法init-method屬性指定的方法
		invokeInitMethods(beanName, wrappedBean, mbd);
	}
	catch (Throwable ex) {
		throw new BeanCreationException(
				(mbd != null ? mbd.getResourceDescription() : null),
				beanName, "Invocation of init method failed", ex);
	}
	if (mbd == null || !mbd.isSynthetic()) {
        //4、調用  bean後置處理器  的初始化前置方法
		wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
	}
	//最後返回包裝後的bean
	return wrappedBean;
}

以上就是spring的BeanFactory初始化bean的源碼,總共分爲4步,源碼中這4步的執行順序決定了流程圖的執行步驟,這次知道流程圖是咋來的了吧。具體對應如下關係如下:

  1. invokeAwareMethods方法,對應流程圖的(1)(2)(3)步,如果bean實現了BeanNameAware、BeanClassLoaderAware、 BeanFactoryAware的接口,則調用bean實現的相對應的set方法。
  2. applyBeanPostProcessorsBeforeInitialization方法,對應流程圖的(4)步,將調用spring容器裏的所有的實現了BeanPostProcessor接口的bean的
    postProcessBeforeInitialization()方法。
  3. invokeInitMethods方法,如果bean實現了InitializingBean接口,則先調用bean實現的afterPropertiesSet()方法,對應(5)步;否則直接執行init-method屬性指定的方法,對應第(6)步。
  4. applyBeanPostProcessorsAfterInitialization方法,對應流程圖的第(7)步,將調用spring容器裏的所有的實現了BeanPostProcessor接口的bean的
    postProcessAfterInitialization()方法。
    下面我依次分析這四個方法,並且將點出來其中的與spring相關的知識點。

2.3 Spring的Aware接口

Aware中文意思:知道、意識到。那麼xxxAware接口就是知道XXX,比如BeanNameAware,如果我們的bean實現了BeanNameAware接口,就是讓我們的bean通過重寫的setBeanName方法知道beanName是什麼,即Spring將beanName爲參數調用bean重寫的setBeanName方法,這樣我們的bean便知道了beanName。同理我們項目中常用的ApplicationContextAware、BeanFactoryAware都有對應的set方法,讓我們的bean知道applicationContext、beanFactory。有了這樣的理解,咱們再看看invokeAwareMethods方法的源碼。

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#invokeAwareMethods
private void invokeAwareMethods(final String beanName, final Object bean) {
	if (bean instanceof Aware) {
		if (bean instanceof BeanNameAware) {
			((BeanNameAware) bean).setBeanName(beanName);
		}
		if (bean instanceof BeanClassLoaderAware) {
			ClassLoader bcl = getBeanClassLoader();
			if (bcl != null) {
				((BeanClassLoaderAware) bean).setBeanClassLoader(bcl);
			}
		}
		if (bean instanceof BeanFactoryAware) {
			((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
		}
	}
}

此段代碼,只是依次判斷bean是否實現了BeanNameAware、BeanClassLoaderAware、BeanFactoryAware接口並調用對應的set方法。

2.4 BeanPostProcessor所有bean的後置處理器

BeanPostProcessor是一個接口,翻譯成中文就是Bean後置處理器,什麼作用呢?見名知意,用於擴展bean創建後的處理邏輯。這個接口只有兩個方法分別是:

public interface BeanPostProcessor {
	@Nullable
	default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}
	@Nullable
	default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}

}

如果我們的bean AAA實現了BeanPostProcessor接口,那麼AAA的
postProcessBeforeInitialization見名知意,方法會在每一個bean對象的初始化方法調用之前回調;postProcessAfterInitialization同樣見名知意,方法會在每個bean對象的初始化方法調用之後被回調。Spring的命名是多麼的優雅!!!重點注意下每一個bean對象。
那麼有咱們分析的AbstractAutowireCapableBeanFactory#initializeBean方法的源碼決定了這兩個方法的調用順序,而applyBeanPostProcessorsBeforeInitialization方法就是真正的調用AAA實現的postProcessBeforeInitialization方法

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInitialization
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
			throws BeansException {

	Object result = existingBean;
	for (BeanPostProcessor processor : getBeanPostProcessors()) {
		Object current = processor.postProcessBeforeInitialization(result, beanName);
		if (current == null) {
			return result;
		}
		result = current;
	}
	return result;
}

2.4 InitializingBean接口和init-method屬性

直接上源碼,看看如果我們的bean實現了InitializingBean接口,或者定義了init-method屬性,Spring會如何處理。

org.springframework.beans.factory.InitializingBean
public interface InitializingBean {
	void afterPropertiesSet() throws Exception;
}

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#invokeInitMethods
protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd)
			throws Throwable {

	//1、判斷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((PrivilegedExceptionAction<Object>) () -> {
					((InitializingBean) bean).afterPropertiesSet();
					return null;
				}, getAccessControlContext());
			}
			catch (PrivilegedActionException pae) {
				throw pae.getException();
			}
		}
		else {
			//執行afterPropertiesSet方法
			((InitializingBean) bean).afterPropertiesSet();
		}
	}

	//判斷是否指定init-method屬性
	if (mbd != null && bean.getClass() != NullBean.class) {
		String initMethodName = mbd.getInitMethodName();
		if (StringUtils.hasLength(initMethodName) &&
				!(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
				!mbd.isExternallyManagedInitMethod(initMethodName)) {
			// 執行用戶通過init-method方法指定的自定義初始化方法
			invokeCustomInitMethod(beanName, bean, mbd);
		}
	}
}

如果我們的bean實現了InitializingBean接口,那麼將先執行afterPropertiesSet方法,這個方法沒有任何入參,只是單純的執行初始化邏輯。之後會判斷我們的bean有沒有指定init-method屬性,如果制定了將通過invokeCustomInitMethod方法來執行自定義的方法。
invokeCustomInitMethod方法的邏輯就是利用java反射機制來執行init-method指定的方法,這裏偷個懶不做詳細分析,Spring源碼博大進深,請原諒一個辛苦碼字的小夥子。

2.5 BeanPostProcessor的bean初始化後置方法

同2.3節相呼應,執行實現了BeanPostProcessor接口的bean的初始化後置方法
postProcessAfterInitialization方法,具體的源碼如下

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsAfterInitialization
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
			throws BeansException {

		Object result = existingBean;
		for (BeanPostProcessor processor : getBeanPostProcessors()) {
			Object current = processor.postProcessAfterInitialization(result, beanName);
			if (current == null) {
				return result;
			}
			result = current;
		}
		return result;
	}

這裏單獨指出一點,BeanPostProcessor還具備一個包裝、加工bean的功能,爲什麼這麼說呢?BeanPostProcessor的兩個方法postProcessBeforeInitialization、
postProcessAfterInitialization入參都是bean和beanName,並且返回值都是必須是bean,那麼咱們就可以在BeanPostProcessor的實現類裏,通過這兩個方法操作bean,對bean進行一定的處理後返回。由源碼
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
看這個變量名的中文含義:被包裝的Bean,從這個變量的命名也能看出Spring設計BeanPostProcessor時想提供的包裝的含義。

3、 總結

  1. 本文主要分析了實現了BeanFactory的接口,未實現ApplicationContext接口的簡單容器的bean 初始化步驟,源碼的執行順序決定了下面的初始化bean初始化步驟,概括起來如下圖:
    在這裏插入圖片描述
  2. 針對Aware系列接口,如BeanNameAware、ApplicationContextAware、BeanFactoryAware的作用,我們的bean通過實現此類接口,通過上面分析的源碼,Spring在初始化bean的時候來調用set方法進行賦值。
  3. BeanPostProcessor的字面意思是bean的後置處理器,所以這個接口的作用於所有bean的初始化流程,可以用於對每一個bean在初始化的時候做統一的處理。
  4. 最後,spring將先調用InitializingBean的afterPropertiesSet()方法,後調用用戶自定義的初始化方法init-method屬性指定的方法。

寫在最後:寫到這裏,我相信已經能夠完全理解Spring的BeanFactory是如何去初始化咱們的bean了,美中不足的是沒有分析ApplicationContext的初始化流程,既然開了這個坑,以後分析。如果有啥不明白的,歡迎點贊留言,咱們可以探討一番。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章