Spring原始碼解析之四(容器的功能擴充套件)
容器的功能擴充套件
1、概述
之前的文章中BeanFactory介面以及它的預設實現類XmlBeanFactory為例進行分析,但是Spring中還提供了另一個介面ApplicationContext,用於擴充套件BeanFactory中現有的功能。
ApplicationContext和BeanFactory兩者都是用於載入Bean的,但是相比之下,ApplicationContext提供了更多的擴充套件功能,簡而言之:ApplicationContext包含BeanFactory的所有功能。通常建議比優先使用ApplicationContext
我們就以ClassPathXmlApplicationContext作為切入點,開始對整體功能進行分析,程式碼如下:
/** * Create a new ClassPathXmlApplicationContext with the given parent, * loading the definitions from the given XML files. * @param configLocations array of resource locations * @param refresh whether to automatically refresh the context, * loading all bean definitions and creating all singletons. * Alternatively, call refresh manually after further configuring the context. * @param parent the parent context * @throws BeansException if context creation failed * @see #refresh() */ public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException { super(parent); setConfigLocations(configLocations); if (refresh) { refresh(); } }
2、設定路徑( setConfigLocations(configLocations);)
/** * Set the config locations for this application context. * <p>If not set, the implementation may use a default as appropriate. */ public void setConfigLocations(String... locations) { if (locations != null) { Assert.noNullElements(locations, "Config locations must not be null"); this.configLocations = new String[locations.length]; for (int i = 0; i < locations.length; i++) { this.configLocations[i] = resolvePath(locations[i]).trim(); } } else { this.configLocations = null; } }
3、擴充套件功能refresh();
refresh函式中包含了幾乎ApplicationContext中提供的全部功能.
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
catch (BeansException ex) {
logger.warn("Exception encountered during context initialization - cancelling refresh attempt", ex);
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
}
}
我們簡單的分析下程式碼的步驟:
(1)初始化前的準備工作,例如對系統屬性或者環境變數進行準備及驗證。
在某種情況下專案的使用需要讀取某些系統變數,而這個變數的設定很可能會影響著系統的正確性,那麼ClassPathXmlApplicationContext為我們提供的這個準備函式就顯得非常必要,他可以在spring啟動的時候提前對必須的環境變數進行存在性驗證。
(2)初始化BeanFactory,並進行XML檔案讀取。
之前提到ClassPathXmlApplicationContext包含著對BeanFactory所提供的一切特徵,那麼這一步中將會複用BeanFactory中的配置檔案讀取解析其他功能,這一步之後ClassPathXmlApplicationContext實際上就已經包含了BeanFactory所提供的功能,也就是可以進行Bean的提取等基本操作了。
(3)對BeanFactory進行各種功能填充
@Qualifier和@Autowired應該是大家非常熟悉的註解了,那麼這兩個註解正是在這一步驟中增加支援的。
(4)子類覆蓋方法做額外處理。
(5)啟用各種BeanFactory處理器
(6)註冊攔截bean建立的bean處理器,這裡只是註冊,真正的呼叫是在getBean時候
(7)為上下文初始化Message源
(8)初始化應用訊息廣播器,並放入“applicationEventMulticaster”bean中
(9)留給子類來初始化其他的bean
(10)在所有註冊的bean中查詢listener bean,註冊到訊息廣播器中
(11)初始化剩下的單例項(非惰性的)
(12)完成重新整理過程,通知生命週期處理器lifecycleProcessor重新整理過程,同時發出ContextRefreshEvent通知別人。
接下來我們就詳細的講解每一個過程:
4、環境準備prepareRefresh();
/**
* Prepare this context for refreshing, setting its startup date and
* active flag as well as performing any initialization of property sources.
*/
protected void prepareRefresh() {
this.startupDate = System.currentTimeMillis();
this.active.set(true);
if (logger.isInfoEnabled()) {
logger.info("Refreshing " + this);
}
// Initialize any placeholder property sources in the context environment
initPropertySources();
// Validate that all properties marked as required are resolvable
// see ConfigurablePropertyResolver#setRequiredProperties
getEnvironment().validateRequiredProperties();
}
(1) initPropertySources,這正符合Spring的開放式結構設計,給使用者最大擴充套件Spring的能力。使用者可以根據自身的需要重寫initPropertySourece方法,並在方法中進行個性化的屬性處理及設定。
(2)validateRequiredProperties則是對屬性進行驗證
5、載入BeanFactory()(obtainFreshBeanFactory()方法)
/**
* Tell the subclass to refresh the internal bean factory.
* @return the fresh BeanFactory instance
* @see #refreshBeanFactory()
* @see #getBeanFactory()
*/
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
refreshBeanFactory();
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (logger.isDebugEnabled()) {
logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
}
return beanFactory;
}
核心在refreshBeanFactory方法中,方法的實現是在AbstractRefreshableApplicationContext中:
/**
* This implementation performs an actual refresh of this context's underlying
* bean factory, shutting down the previous bean factory (if any) and
* initializing a fresh bean factory for the next phase of the context's lifecycle.
*/
@Override
protected final void refreshBeanFactory() throws BeansException {
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
customizeBeanFactory(beanFactory);
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
我們詳細分析下上面程式碼的步驟:
(1)建立DefaultListableBeanFactory
以前我們分析BeanFactory的時候,不知道是否還有印象,宣告方式為:BeanFactory bf = new XmlBeanFactory(“beanFactoryTest.xml”),其中的XmlBeanFactory繼承自DefaulltListableBeanFactory;,並提供了XmlBeanDefinitionReader型別的reader屬性,也就是說DefaultListableBeanFactory是容器的基礎。必須首先要例項化,這裡就是建立DefaultListableBeanFactory的步驟。
(2)指定序列號ID
(3)定製BeanFactory
(4)載入BeanDefinition
(5)使用全域性變數儲存BeanFactory
因為DefaultListableBeanFactory型別的變數beanFactory是函式內部的區域性變數, 所以要使用全域性變數記錄解析結果
5.1 定製BeanFactory(通過customizeBeanFactory(beanFactory);)
/**
* Customize the internal bean factory used by this context.
* Called for each {@link #refresh()} attempt.
* <p>The default implementation applies this context's
* {@linkplain #setAllowBeanDefinitionOverriding "allowBeanDefinitionOverriding"}
* and {@linkplain #setAllowCircularReferences "allowCircularReferences"} settings,
* if specified. Can be overridden in subclasses to customize any of
* {@link DefaultListableBeanFactory}'s settings.
* @param beanFactory the newly created bean factory for this context
* @see DefaultListableBeanFactory#setAllowBeanDefinitionOverriding
* @see DefaultListableBeanFactory#setAllowCircularReferences
* @see DefaultListableBeanFactory#setAllowRawInjectionDespiteWrapping
* @see DefaultListableBeanFactory#setAllowEagerClassLoading
*/
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
if (this.allowBeanDefinitionOverriding != null) {
beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
if (this.allowCircularReferences != null) {
beanFactory.setAllowCircularReferences(this.allowCircularReferences);
}
}
5.2 載入BeanFactory(通過loadBeanDefinitions(beanFactory);實現)
/**
* Loads the bean definitions via an XmlBeanDefinitionReader.
* @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
* @see #initBeanDefinitionReader
* @see #loadBeanDefinitions
*/
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// Create a new XmlBeanDefinitionReader for the given BeanFactory.
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// Configure the bean definition reader with this context's
// resource loading environment.
beanDefinitionReader.setEnvironment(getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// Allow a subclass to provide custom initialization of the reader,
// then proceed with actually loading the bean definitions.
initBeanDefinitionReader(beanDefinitionReader);
loadBeanDefinitions(beanDefinitionReader);
}
除了初始化DefaultListableBeanFactory外,還需要XmlBeanDefinitionReader來讀取XML,那麼在loadBeanDefinitions方法中首先要做的就是初始化XmlBeanDefinitonReader。在初始化了DefaultListableBeanFactory和XmlBeanDefinitionReader後,就可以進行配置檔案的讀取了。繼續進入到loadBeanDefinitions(beanDefinitionReader)方法體中,程式碼如下:
/**
* Load the bean definitions with the given XmlBeanDefinitionReader.
* <p>The lifecycle of the bean factory is handled by the refreshBeanFactory method;
* therefore this method is just supposed to load and/or register bean definitions.
* <p>Delegates to a ResourcePatternResolver for resolving location patterns
* into Resource instances.
* @throws IOException if the required XML document isn't found
* @see #refreshBeanFactory
* @see #getConfigLocations
* @see #getResources
* @see #getResourcePatternResolver
*/
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {
String[] configLocations = getConfigLocations();
if (configLocations != null) {
for (String configLocation : configLocations) {
reader.loadBeanDefinitions(configLocation);
}
}
}
因為在XmlBeanDefinitionReader中已經將之前初始化的DefaultListableBeanFactory註冊進去了,所以XmlBeanDefinitionReader所讀取的BeanDefinitionHolder都會註冊到DefinitionListableBeanFactory中,也就是經過這個步驟,DefaultListableBeanFactory的變數beanFactory已經包含了所有解析好的配置。
6、功能擴充套件
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
/**
* Configure the factory's standard context characteristics,
* such as the context's ClassLoader and post-processors.
* @param beanFactory the BeanFactory to configure
*/
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
// Tell the internal bean factory to use the context's class loader etc.
beanFactory.setBeanClassLoader(getClassLoader());
beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
// Configure the bean factory with context callbacks.
beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
// BeanFactory interface not registered as resolvable type in a plain factory.
// MessageSource registered (and found for autowiring) as a bean.
beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
beanFactory.registerResolvableDependency(ResourceLoader.class, this);
beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
beanFactory.registerResolvableDependency(ApplicationContext.class, this);
// Detect a LoadTimeWeaver and prepare for weaving, if found.
if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
// Set a temporary ClassLoader for type matching.
beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
}
// Register default environment beans.
if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
}
if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
}
if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
}
}
詳細分析下程式碼發現上面函式主要是在以下方法進行了擴充套件:
(1)對SPEL語言的支援
(2)增加對屬性編輯器的支援
(3)增加對一些內建類的支援,如EnvironmentAware、MessageSourceAware的注入
(4)設定了依賴功能可忽略的介面
(5)註冊一些固定依賴的屬性
(6)增加了AspectJ的支援
(7)將相關環境變數及屬性以單例模式註冊
接下來分析下以上步驟。
6.1 增加對SPEL語言的支援
beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
6.2 增加屬性編輯器
beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
7、設定忽略依賴
// Configure the bean factory with context callbacks.
beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
8、註冊依賴
// BeanFactory interface not registered as resolvable type in a plain factory.
// MessageSource registered (and found for autowiring) as a bean.
beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
beanFactory.registerResolvableDependency(ResourceLoader.class, this);
beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
beanFactory.registerResolvableDependency(ApplicationContext.class, this);
9、BeanFactory的後處理