Spring加載Bean過程簡析

1 定義bean的方式

常見的定義Bean的方式有:java

  • 經過xml的方式,例如:
<bean id="dictionaryRelMap" class="java.util.HashMap"/>
  • 經過註解的方式,在Class上使用@Component等註解,例如
@Component
public class xxxServicer{
    ....
}
  • 經過在@Configuration類下的@Bean的方式,例如
@Configuration
public class xxxConfiguration{
    @Bean
    public myBean myBean(){
        return new myBean();
    }
}

雖然這三種定義Bean的方式不同,對應的處理細節也不同,可是從大的邏輯上來看,都是同樣。主要的流程以下圖: 最關鍵的就是問題就是這麼去找到定義Bean的方式,而後生成BeanDefinition後註冊到Spring上下文中,由Spring自動建立Bean的實例。
輸入圖片說明spring

2 BeanDefinition

BeanDefinition是一個接口,用來描述一個Bean實例,例如是SINGLETON仍是PROTOTYPE,屬性的值是什麼,構造函數的參數是什麼等。簡單來講,經過一個BeanDefinition咱們就能夠完成一個Bean實例化。 BeanDefinition及其主要的子類:app

輸入圖片說明
下面簡單說一下各個子類:ide

  • RootBeanDefinition和ChildBeanDefinition: 這2個BeanDefinition是相對的關係,自Spring 2.5 出來之後,已經被GenericBeanDefinition代替。由於這樣強迫咱們在編寫代碼的時候就必須知道他們之間的關係。
  • GenericBeanDefinition: 相比於RootBeanDefinition和ChildBeanDefinition在定義的時候就必須硬編碼,GenericBeanDefinition的優勢能夠動態的爲GenericBeanDefinition設置parent。
  • AnnotatedBeanDefinition:看名字就是知道是用來讀取經過註解定義Bean。

3 經過xml文件定義Bean

經過xml定義Bean是最先的Spring定義Bean的方式。所以,怎麼把xml標籤解析爲BeanDefinition(), 入口是在org.springframework.beans.factory.xml.XmlBeanDefinitionReader這個類,可是實際幹活的是在org.springframework.beans.factory.xml.BeanDefinitionParserDelegate。代碼不少,但實際邏輯很簡單,就是解析Spring定義的<bean> <property> 等標籤 。 以前寫過一篇文章介紹過如何自定義Spring標籤 ,並解析後註冊到Spring中——傳送門函數

4 經過@Component等Spring支持的註解加載Bean

若是要使用@Component等註解定義Bean,一個前提條件是:有<context:component-scan/>或者@ComponentScan註解。但這2個方式仍是有一點點區別:post

4.1 context:component-scan/

因爲<context:component-scan/>是一個xml標籤,所以是在解析xml,生成的類org.springframework.context.annotation.ComponentScanBeanDefinitionParser,關鍵代碼:編碼

@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
        //獲取base-package標籤
	String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE);
	basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage);
	String[] basePackages = StringUtils.tokenizeToStringArray(basePackage,
			ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);

	// 實際處理類是ClassPathBeanDefinitionScanner 
	ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
    //掃描basePackage下全部的類,若是有@Component等標籤就是註冊到Spring中
	Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);
	registerComponents(parserContext.getReaderContext(), beanDefinitions, element);
	return null;
}

4.2 @ComponentScan

註解對應生成的類是org.springframework.context.annotation.ComponentScanAnnotationParser 其實最後實際幹活的仍是ClassPathBeanDefinitionScanner這個。ComponentScanAnnotationParser類的生成是伴隨着@Configuration這個註解處理過程當中(意思說@ComponentScan必須和@Configuration一塊兒使用)。而處理@Configuration實際上是org.springframework.context.annotation.ConfigurationClassPostProcessor。是否是感受有點繞。
其實簡單來講,在處理@Configuration的時候發現有@ComponentScan註解,就會生成ComponentScanAnnotationParser去掃描@Component註解lua

4.3 ClassPathBeanDefinitionScanner

上面說到了,不管註解仍是標籤的方式,最後都會交給ClassPathBeanDefinitionScanner這個類來處理,這個類作的就是1.掃描basePackage下全部class,若是有@Component等註解,讀取@Component相關屬性,生成ScannedGenericBeanDefinition,註冊到Spring中。.net

5 經過@Bean方式

前面說了@ComponentScan是在@Configuration處理過程當中的一環,既然@Bean註解也是必須和@Configuration一塊兒使用,那麼說明@Bean的處理也是在@Configuration中,其實最後是交給ConfigurationClassBeanDefinitionReader這個類來處理的,關鍵代碼:code

private void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass,
		TrackedConditionEvaluator trackedConditionEvaluator) {

       //若是本身是經過@Import註解定義的,那麼須要把本身註冊到Spring中
	if (configClass.isImported()) {
		registerBeanDefinitionForImportedConfigurationClass(configClass);
	}
    //這裏就是處理方法上的@Bean
	for (BeanMethod beanMethod : configClass.getBeanMethods()) {
		loadBeanDefinitionsForBeanMethod(beanMethod);
	}
    //處理@ImportResource,裏面解析xml就是上面說到的解析xml的XmlBeanDefinitionReader
	loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
	loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}

爲了保證org.springframework.context.annotation.ConfigurationClassPostProcessor

6 把BeanDefinition實例化

前面分別說了怎麼把不一樣定義Bean的方式轉換爲BeanDefinition加入到Spring中去(確切來講是保持在BeanFactory的BeanDefinitionMap中),實例是在ApplicationContext最後階段,關鍵代碼在DefaultListableBeanFactory中

@Override
	public void preInstantiateSingletons() throws BeansException {

		for (String beanName : beanNames) {
			RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
			if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
				if (isFactoryBean(beanName)) {
					final FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName);
					boolean isEagerInit;
					if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
						isEagerInit = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
							@Override
							public Boolean run() {
								return ((SmartFactoryBean<?>) factory).isEagerInit();
							}
						}, getAccessControlContext());
					}
					else {
						isEagerInit = (factory instanceof SmartFactoryBean &&
								((SmartFactoryBean<?>) factory).isEagerInit());
					}
					if (isEagerInit) {
						getBean(beanName);
					}
				}
				else {
					getBean(beanName);
				}
			}
		}

經過getBean最後最後實例的代碼,在AbstractAutowireCapableBeanFactory中

protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {
        //處理xxAware接口
		if (System.getSecurityManager() != null) {
			AccessController.doPrivileged(new PrivilegedAction<Object>() {
				@Override
				public Object run() {
					invokeAwareMethods(beanName, bean);
					return null;
				}
			}, getAccessControlContext());
		}
		else {
			invokeAwareMethods(beanName, bean);
		}

		Object wrappedBean = bean;
		if (mbd == null || !mbd.isSynthetic()) {
            // 調用BeanPostProcessors#postProcessBeforeInitialization
			wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
		}

		try {
           //初始化,先判斷是不是InitializingBean,
			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()) {
               // 調用BeanPostProcessors#postProcessAfterInitialization
			wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
		}
		return wrappedBean;
	}

從上面初始化能夠看出,InitializeBean和BeanPostProcessors的調用順序

7 總結

綜上分析,Spring加載Bean其實大的思想都是同樣的,先讀取相關信息生成BeanDefinition,而後經過BeanDefinition初始化Bean。若是知道了上面了套路之後,就能夠清楚怎麼自定義Xml標籤或者自定義註解向Spring中注入Bean。

相關文章
相關標籤/搜索