轉(zhuǎn)眼間畢業(yè)已經(jīng)兩年多了,工作中一直也在使用Spring,中途也陸續(xù)的看過其中的用法以及部分的實(shí)現(xiàn)原理。 最近工作之余想整體的看一下Spring的具體原理,研究下源碼,理解下這個(gè)經(jīng)典框架的設(shè)計(jì)思路,以及其中的設(shè)計(jì)模式。 不過開始看了一周后,感覺也是似懂非懂,回想起來總是不知道該從哪說起。
為了防止這次看了又跟沒看一樣的慘劇發(fā)生,所以決定在看的過程中,記錄一下相關(guān)細(xì)節(jié)。 說實(shí)話準(zhǔn)備開始寫這篇文章時(shí),我還是有點(diǎn)不知道從哪說起,不過還是決定硬著頭皮寫吧,總要嘗試一下的嘛。
這篇文章主要說明 Spring IOC 相關(guān)的源代碼實(shí)現(xiàn),使用 xml 配置的方式,雖然大家在使用中基本上不會(huì)使用這種方式,最起碼不是完全使用 XML 配置,不過從研究源代碼的角度,這種方式無疑是最合適的,理解了xml配置的方式,注解的實(shí)現(xiàn)基本上原理也是類似的過程,也許在過幾天我抽空也會(huì)寫篇文章講解下注解實(shí)現(xiàn)的相關(guān)源代碼。
由于文章篇幅較長(zhǎng),這里分為上下兩篇,此篇為上篇,包含目錄如下:

《Spring容器基本實(shí)現(xiàn)之源碼分析-下篇 傳送門》
Spring容器的功能加載
ApplicationContext bf = new ClassPathXmlApplicationContext("beans.xml");
如上面這行代碼,我們使用ApplicationCdontext方式加載XML。以ClassPathXmlApplicationContext作為切入點(diǎn),開始對(duì)整體功能進(jìn)行分析。
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
throws BeansException {
super(parent);
// 設(shè)置路徑
setConfigLocations(configLocations);
if (refresh) {
refresh();
}
}
如上,設(shè)置路徑是必不可少的步驟。
容器啟動(dòng)流程
設(shè)置好了路徑之后,便可以根據(jù)路徑做配置文件的解析以及各種功能的實(shí)現(xiàn)了。而Spring容器的整個(gè)啟動(dòng)過程幾乎全部都在refresh 函數(shù)中,而且此函數(shù)中的邏輯非常清晰明了,讓我們?cè)陂喿x時(shí)很容易分析對(duì)應(yīng)的層次和邏輯;
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
// 準(zhǔn)備刷新的上下文環(huán)境
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
// 初始化BeanFactory,并進(jìn)行XML文件讀取
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
// 對(duì)BeanFactory進(jìn)行各種功能填充
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
// 子類覆蓋方法做額外的處理
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
// 激活各種BeanFactory處理器
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
// 注冊(cè)攔截Bean創(chuàng)建的Bean處理器,此處只是注冊(cè),真正的調(diào)用在getBean時(shí)發(fā)生
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
// 為上下文初始化Message源,國(guó)際化處理
initMessageSource();
// Initialize event multicaster for this context.
// 初始化應(yīng)用消息廣播器,并放入"applicationEventMulticaster"bean中
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
// 留給子類來初始化其他的Bean
onRefresh();
// Check for listener beans and register them.
// 在所有注冊(cè)的bean中查找listener bean,注冊(cè)到消息廣播器中
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
// 初始化剩下的所有單實(shí)例bean(非延遲加載)
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
// 完成刷新過程,通知生命周期處理器lifeCycleProcessor刷新過程,同時(shí)發(fā)出ContextRefreshEvent通知?jiǎng)e人
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
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;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
流程概括
下面主要概括一下上面初始化步驟,并簡(jiǎn)要解釋一下它為我們提供的功能。
(1) 初始化前的準(zhǔn)備工作,例如對(duì)系統(tǒng)屬性以及環(huán)境變量進(jìn)行準(zhǔn)備和驗(yàn)證。
? 在某些情況下,項(xiàng)目的使用需要讀取某些系統(tǒng)變量,而這個(gè)變量可能影響著系統(tǒng)的正確性,那么prepareRefresh這些準(zhǔn)備函數(shù)就顯得非常必要,它可以在Spring啟動(dòng)前對(duì)必須的變量進(jìn)行驗(yàn)證。
(2) 初始化BeanFactory,并進(jìn)行XML文件讀取
? 這一步驟會(huì)復(fù)用BeanFactory中的配置文件讀取解析以及其他功能,這一步之后,其實(shí)ClasspathXmlApplicationContext實(shí)際上就已經(jīng)包含了BeanFactory所提供的功能,也就是可以進(jìn)行Bean的提取等基礎(chǔ)操作了。
(3) 對(duì)BeanFactory進(jìn)行各種功能補(bǔ)充
? @Qualifier與@Autowired應(yīng)該是大家非常熟悉的注解,那么這兩個(gè)注解正是在這一步驟中增加的支持
(4) 子類覆蓋方法做額外的處理
? Spring之所以強(qiáng)大,為世人所推崇,除了它功能上為大家提供了便利外,還有一方面是它的完美架構(gòu),開放式的架構(gòu)讓使用它的程序員很容易根據(jù)實(shí)際業(yè)務(wù)做相應(yīng)的功能擴(kuò)展。這種開放式的設(shè)計(jì)在Spring中隨處可見,例如本例中postProcessBeanFactory函數(shù)就是提供的空函數(shù)用來讓程序員在業(yè)務(wù)上做進(jìn)一步的擴(kuò)展。
(5) 激活各種BeanFactory處理器。
(6) 注冊(cè)各種攔截Bean創(chuàng)建的bean處理器,這里只是注冊(cè),真正的調(diào)用在getBean的時(shí)候。
(7) 為上下文初始化Message源,即對(duì)不同的語(yǔ)言消息體進(jìn)行國(guó)際化處理。
(8) 初始化應(yīng)用消息廣播器,并放入applicationEventMulticaster中。
(9) 留給子類來初始化其他的bean。
(10) 在所有注冊(cè)的bean中查找 listener bean ,注冊(cè)到消息廣播器中。
(11)初始化剩下的單實(shí)例(非惰性加載)bean。
(12) 完成刷新過程,通知生命周期處理器 lifecycleProcessor 刷新過程,同時(shí)發(fā)出 ContextRefreshEvent通知?jiǎng)e人。
一探究竟
上面我們分析了Spring容器啟動(dòng)要經(jīng)歷的基本流程,接下來我們來一起探究具體每一步都做了什么事情。
環(huán)境準(zhǔn)備
prepareRefresh函數(shù)主要是做準(zhǔn)備工作,例如對(duì)系統(tǒng)屬性的以及環(huán)境變量的初始化以及驗(yàn)證。
/**
* 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.closed.set(false);
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
// 驗(yàn)證所需要的屬性文件是否都已放入環(huán)境中
// see ConfigurablePropertyResolver#setRequiredProperties
getEnvironment().validateRequiredProperties();
// Allow for the collection of early ApplicationEvents,
// to be published once the multicaster is available...
this.earlyApplicationEvents = new LinkedHashSet<ApplicationEvent>();
}
我在第一次看到這個(gè)函數(shù)時(shí),覺得好像沒什么用,因?yàn)槟芸闯鰜碜詈髢删浯a是關(guān)鍵,可能卻沒有什么邏輯,initPropertySources 是空的,而且 getEnvironment().validateRequiredProperties() 也因?yàn)闆]有需要驗(yàn)證的屬性而沒有任何處理。其實(shí)不然,是因?yàn)闆]有徹底理解才會(huì)有上面的錯(cuò)覺,這個(gè)函數(shù)如果用好了,作用是非常大的。下面先說下每個(gè)函數(shù)的作用,再結(jié)合一個(gè)例子我們一起看下。
(1) initPropertySources 正符合 Spring 的開放式設(shè)計(jì),給用戶最大擴(kuò)展 Spring 的能力。用戶可以根據(jù)自身的需要重寫 initPropertySources 方法,并在方法中進(jìn)行個(gè)性化的屬性處理及設(shè)置。
(2) validateRequiredProperties 則是對(duì)屬性的驗(yàn)證,那么如何驗(yàn)證呢?
假如,現(xiàn)在有這樣一個(gè)需求,工程在運(yùn)行過程中,用到的某個(gè)設(shè)置(例如PATH)是從系統(tǒng)環(huán)境變量中取得的,如果用戶沒有在系統(tǒng)環(huán)境變量中配置這個(gè)屬性,那么運(yùn)行啟動(dòng)肯定會(huì)報(bào)錯(cuò)。這一要求在 Spring 中可以這樣做,你可以直接修改 Spring 的源碼,例如修改ClasspathXmlApplicationContext。當(dāng)然最好的方法還是對(duì) Spring 源碼進(jìn)行擴(kuò)展,如下:
public class MyClassPathXmlApplicationContext extends ClassPathXmlApplicationContext{
public MyClassPathXmlApplicationContext(String configLocation) throws BeansException {
super(configLocation);
}
@Override
protected void initPropertySources() {
// 添加驗(yàn)證要求
getEnvironment().setRequiredProperties("PATH");
}
}
? 這里自定義了繼承 ClassPathXmlApplicationContext 的 MyClassPathXmlApplicationContext 并重寫了 initPropertySources 方法,在方法中添加了我們的個(gè)性化需求,那么在驗(yàn)證的時(shí)候也就是程序走到 getEnvironment().validateRequiredProperties() 的時(shí)候,如果系統(tǒng)系統(tǒng)檢測(cè)并沒有需要的PATH 環(huán)境變量,那么將拋出異常。當(dāng)然我們需要在使用的時(shí)候替換ClassPathXmlApplicationContext:
@Test
public void testInitProperties() {
ApplicationContext applicationContext = new MyClassPathXmlApplicationContext("beans.xml");
Dog dog = (Dog) applicationContext.getBean("dog");
}
加載 BeanFactory
obtainFreshBeanFactory 方法從字面上理解就是獲取 BeanFactory 。 上面也有提過 ApplicatioContext 是對(duì)BeanFactory 的功能上的擴(kuò)展,不但包含了 BeanFactory 的全部功能更在其基礎(chǔ)上添加了大量的擴(kuò)展應(yīng)用,那么obtainFreshBeanFactory 正是實(shí)現(xiàn) BeanFactory 的地方,也就是說經(jīng)過了這個(gè)函數(shù)后 ApplicationContext 就已經(jīng)擁有了 BeanFactory 的全部功能。
/**
* Tell the subclass to refresh the internal bean factory.
* @return the fresh BeanFactory instance
* @see #refreshBeanFactory()
* @see #getBeanFactory()
*/
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
// 初始化BeanFactory,并進(jìn)行XML文件讀取,將得到的BeanFactory記錄到當(dāng)前實(shí)體的屬性中
refreshBeanFactory();
// 返回當(dāng)前實(shí)體的BeanFactory屬性
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (logger.isDebugEnabled()) {
logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
}
return beanFactory;
}
/**
* 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.
*/
// 此方法為核心實(shí)現(xiàn)
@Override
protected final void refreshBeanFactory() throws BeansException {
// 關(guān)閉已經(jīng)存在的beanFactory
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
// 創(chuàng)建DefaultListableBeanFactory
DefaultListableBeanFactory beanFactory = createBeanFactory();
// 為了序列化指定id,如果需要的話讓其從id反序列化為BeanFactory對(duì)象
beanFactory.setSerializationId(getId());
// 定制beanFactory,設(shè)置相關(guān)屬性,包括是否允許覆蓋同名稱不同定義的對(duì)象以及循環(huán)依賴,以及設(shè)置@Autowired和@Qualifier注解解析器 QualifierAnnotationAutowireCandidateResolver
customizeBeanFactory(beanFactory);
// 初始化DocumentReader,并進(jìn)行XML文件讀取及解析
loadBeanDefinitions(beanFactory);
// 使用全局變量記錄BeanFactroy類實(shí)例
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
定制 BeanFactory
上面已經(jīng)提到這里開始了對(duì) BeanFactory 的擴(kuò)展,再基本容器的基礎(chǔ)上,增加了是否允許覆蓋是否允許擴(kuò)展的設(shè)置并提供了注解 @Qualifier 和 @Autowired 的支持。
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
// 如果allowBeanDefinitionOverriding 屬性不為空,設(shè)置給beanFactory相應(yīng)的屬性,此屬性的含義:是否允許覆蓋同名稱的不同定義的對(duì)象
if (this.allowBeanDefinitionOverriding != null) {
beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
// 此屬性的含義:是否允許bean之間存在循環(huán)依賴
if (this.allowCircularReferences != null) {
beanFactory.setAllowCircularReferences(this.allowCircularReferences);
}
}
看了代碼,我們可以得知對(duì)于上面是否允許覆蓋的兩個(gè)屬性只是判斷了是否為空,如果不為空要進(jìn)行設(shè)置,但是并沒有看到在哪里設(shè)置,究竟這個(gè)設(shè)置是在哪里呢?其實(shí)還是和上面一樣,提現(xiàn)了Spring的高度擴(kuò)展性,使用子類覆蓋方法,例如:
@Override
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
super.setAllowBeanDefinitionOverriding(false);
super.setAllowCircularReferences(false);
super.customizeBeanFactory(beanFactory);
}
加載 BeanDefinition
再上一個(gè)步驟,我們看到已經(jīng)初始化了 DefaultListableFactory , 這是實(shí)現(xiàn)配置文件加載的第一步,還需要XmlBeanDefinitionReader 來讀取XML。那么接下來的步驟首先要做的就是初始化 XmlBeanDefinitionReader 。
/**
* 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.
// 為指定beanFactory創(chuàng)建XmlBeanDefinitionReader
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// Configure the bean definition reader with this context's
// resource loading environment.
// 為beanDefinitionReader進(jìn)行環(huán)境變量的設(shè)置
beanDefinitionReader.setEnvironment(this.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.
// 對(duì)beanDefinitionReader進(jìn)行設(shè)置,可以覆蓋
initBeanDefinitionReader(beanDefinitionReader);
loadBeanDefinitions(beanDefinitionReader);
}
在初始化了 DefaultListableBeanFactory 和 XmlBeanDefinitionReader 后就可以進(jìn)行配置文件的讀取了。
/**
* Load the bean definitions with the given XmlBeanDefinitionReader.
* <p>The lifecycle of the bean factory is handled by the {@link #refreshBeanFactory}
* method; hence this method is just supposed to load and/or register bean definitions.
* @param reader the XmlBeanDefinitionReader to use
* @throws BeansException in case of bean registration errors
* @throws IOException if the required XML document isn't found
* @see #refreshBeanFactory
* @see #getConfigLocations
* @see #getResources
* @see #getResourcePatternResolver
*/
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
Resource[] configResources = getConfigResources();
if (configResources != null) {
reader.loadBeanDefinitions(configResources);
}
String[] configLocations = getConfigLocations();
if (configLocations != null) {
reader.loadBeanDefinitions(configLocations);
}
}
使用 XmlBeanDefinitionReader 的 loadBeanDefinitions 方法進(jìn)行配置文件的加載注冊(cè)。因?yàn)?XmlBeanDefinitionReader 在創(chuàng)建時(shí)已經(jīng)將初始化好的 DefaultListableBeanFactory 注冊(cè)進(jìn)去了,所以XmlBeanDefinitionReader 所讀取的 BeanDefinitionHolder 都會(huì)注冊(cè)到 DefaultListableBeanFactory 中,也就是經(jīng)過此步驟,類型 DefaultListableBeanFactory 的實(shí)例變量 beanFactory 已經(jīng)包含了所有解析好的配置。
功能擴(kuò)展
進(jìn)入函數(shù) prepareBeanFactory 之前,Spring 已經(jīng)完成了對(duì)配置的解析,而 ApplicationContext 在功能上的擴(kuò)展就在此展開。
/**
* 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) {
// 設(shè)置beanFactory的classloader為當(dāng)前context的classLoader
beanFactory.setBeanClassLoader(getClassLoader());
// 設(shè)置beanFactory的表達(dá)式語(yǔ)言處理器,spring3開始增加了表達(dá)式語(yǔ)言的支持,默認(rèn)可以使用#{bean.xxx}的形式調(diào)用相關(guān)屬性值。
beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
// 為beanFactory增加一個(gè)默認(rèn)的propertyEditor,這個(gè)主要是對(duì)bean的屬性設(shè)置等管理的一個(gè)工具
beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
// 添加beanPostProcessor
beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
// 設(shè)置幾個(gè)忽略自動(dòng)裝配的接口
beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
// BeanFactory interface not registered as resolvable type in a plain factory.
// MessageSource registered (and found for autowiring) as a bean.
// 設(shè)置幾個(gè)自動(dòng)裝配的特殊規(guī)則
beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
beanFactory.registerResolvableDependency(ResourceLoader.class, this);
beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
beanFactory.registerResolvableDependency(ApplicationContext.class, this);
// Register early post-processor for detecting inner beans as ApplicationListeners.
beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));
// Detect a LoadTimeWeaver and prepare for weaving, if found.
// 增加對(duì)AspectJ的支持
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()));
}
// 添加默認(rèn)的系統(tǒng)環(huán)境bean
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());
}
}
上面函數(shù)中主要進(jìn)行了幾個(gè)方面的擴(kuò)展:
- 增加對(duì)SPEL語(yǔ)言的支持
- 增加對(duì)屬性編輯器的支持;
- 增加對(duì)一些內(nèi)置類,比如 EnvironmentAware、MessageSourceAware 的信息注入。
- 設(shè)置了依賴功能可忽略的接口;
- 注冊(cè)一些固定依賴的屬性;
- 增加AspectJ的支持;
- 將相關(guān)環(huán)境變量以及系統(tǒng)屬性以單例模式注冊(cè);
上面我們總結(jié)了這一階段的幾個(gè)步驟,但是對(duì)于具體含義可能并不理解,下面將對(duì)各個(gè)步驟進(jìn)行分析;
增加SPEL語(yǔ)言支持
Spring 表達(dá)式語(yǔ)言全稱為“Spring Expression Language”,縮寫為"SPEL",能在運(yùn)行時(shí)構(gòu)建復(fù)雜表達(dá)式、存取對(duì)象屬性、對(duì)象方法調(diào)用等,并且能與Spring功能完美整合,比如能用來配置bean定義。SPEL是單獨(dú)模塊,只依賴于core 模塊,不依賴于其他模塊,可以單獨(dú)使用。SpEL 使用#{...} 作為界定符。這里只是為了喚醒大家的記憶幫助我們來理解源碼,有興趣的可以進(jìn)一步深入研究。
在源碼中通過代碼 beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader())) 注冊(cè)語(yǔ)言解析器,就可以對(duì)SPEL進(jìn)行解析了,那么注冊(cè)解析器后Spring又是在什么時(shí)候調(diào)用解析器的呢?
Spring 在 bean進(jìn)行初始化的時(shí)候會(huì)有屬性填充的一步,而這一步會(huì)調(diào)用 AbstractAutowireCapableBeanFactory 類的 applyPropertyValues 函數(shù)來完成功能。而就在這個(gè)函數(shù)中,會(huì)通過構(gòu)造 BeanDefinitionValueResolver 類型實(shí)例valueResolver 來進(jìn)行屬性值的解析。同時(shí),也就是這個(gè)步驟中一般通過AbstractBeanFactory 中的evaluateBeanDefinitionString 方法去完成SPEL的解析。
/**
* Evaluate the given String as contained in a bean definition,
* potentially resolving it as an expression.
* @param value the value to check
* @param beanDefinition the bean definition that the value comes from
* @return the resolved value
* @see #setBeanExpressionResolver
*/
protected Object evaluateBeanDefinitionString(String value, BeanDefinition beanDefinition) {
if (this.beanExpressionResolver == null) {
return value;
}
Scope scope = (beanDefinition != null ? getRegisteredScope(beanDefinition.getScope()) : null);
return this.beanExpressionResolver.evaluate(value, new BeanExpressionContext(this, scope));
}
當(dāng)調(diào)用這個(gè)方法的時(shí)候,會(huì)判斷是否存在語(yǔ)言解析器,如果存在則調(diào)用解析器的方法進(jìn)行解析,解析的過程是在Spring的epression 的包內(nèi),這里不做過多解釋。我們通過對(duì) evaluateBeanDefinitionString 方法的調(diào)用層次可以看出,應(yīng)用語(yǔ)言解析器的調(diào)用主要是在解析依賴注入bean的時(shí)候,以及在完成bean的初始化和屬性獲取后進(jìn)行屬性填充的時(shí)候。
增加屬性注冊(cè)編輯器
在Spring DI的時(shí)候可以把普通屬性注入進(jìn)來,但是像Date類型就無法被識(shí)別,例如:
public class UserManager {
private Date dateValue;
@Override
public String toString() {
return "UserManager{" +
"dateValue=" + dateValue +
'}';
}
public Date getDateValue() {
return dateValue;
}
public void setDateValue(Date dateValue) {
this.dateValue = dateValue;
}
// 上面代碼中需要對(duì)日期進(jìn)行屬性注入
<bean id="userManager" class="com.ccgogoing.domain.UserManager">
<property name="dateValue">
<value>2019-01-27</value>
</property>
</bean>
// 測(cè)試代碼
@Test
public void testDate() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
UserManager userManager = (UserManager) applicationContext.getBean("userManager");
System.out.println(userManager);
}
如果直接這樣使用,程序會(huì)報(bào)錯(cuò),類型轉(zhuǎn)換失敗。因?yàn)樵赨serManager中的dateValue 屬性是Date類型的,而在XML中配置的確實(shí)String類型的,因此當(dāng)然會(huì)報(bào)錯(cuò)。
Spring針對(duì)此問題提供了兩種解決辦法:
-
使用自定義屬性編輯器
(1) 編寫自定義的屬性編輯器
public class DatePropertyEditor extends PropertyEditorSupport {
private String format = "yyyy-MM-dd";
public void setFormat (String format) {
this.format = format;
}
@Override
public void setAsText(String text) throws IllegalArgumentException {
System.out.println("text = " + text);
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(format);
try {
Date d = simpleDateFormat.parse(text);
this.setValue(d);
} catch (ParseException e) {
e.printStackTrace();
}
super.setAsText(text);
}
}
? (2) 將自定義屬性編輯器注冊(cè)到Spring中
<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
<property name="customEditors">
<map>
<entry key="java.util.Date">
<bean class="com.ccgogoing.extend.DatePropertyEditor">
<property name="format" value="yyyy-MM-dd"/>
</bean>
</entry>
</map>
</property>
</bean>
在配置文件中引入類型為 org.springframework.beans.factory.config.CustomEditorConfigurer 的bean,并在屬性 customEditors 中加入自定義的屬性編輯器,其中key 為屬性編輯器所對(duì)應(yīng)的類型。 通過這樣的配置,當(dāng)spring 注入bean 的屬性時(shí),一旦碰到Date類型就會(huì)調(diào)用自定義的DatePropertyEditor解析器進(jìn)行解析,并用解析結(jié)果代替配置屬性進(jìn)行注入。
-
注冊(cè)Spring自帶的屬性編輯器CustomDateEditor
具體代碼如下:
// 定義屬性編輯器 public class DatePropertyEditorRegistrar implements PropertyEditorRegistrar { @Override public void registerCustomEditors(PropertyEditorRegistry registry) { registry.registerCustomEditor(Date.class,new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"),true)); } } // 注冊(cè)到Spring中 <bean class="org.springframework.beans.factory.config.CustomEditorConfigurer"> <property name="propertyEditorRegistrars"> <list> <bean class="com.ccgogoing.extend.DatePropertyEditorRegistrar"/> </list> </property> </bean>
我們了解了自定義屬性編輯器的作用,但是與 beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment())) 并無聯(lián)系,因?yàn)樵谧?cè)自定義屬性編輯器的時(shí)候使用的是 PropertyEditorRegistry 的 registerCustomEditor 方法,而這里使用的是 ConfigurableListableBeanFactory 的 addPropertyEditorRegistrar 方法。 我們不妨深入探索一下 ResourceEditorRegistrar 的內(nèi)部實(shí)現(xiàn),在 ResourceEditorRegistrar 中,我們最關(guān)心的方法是 registerCustomEditors .
/**
* Populate the given {@code registry} with the following resource editors:
* ResourceEditor, InputStreamEditor, InputSourceEditor, FileEditor, URLEditor,
* URIEditor, ClassEditor, ClassArrayEditor.
* <p>If this registrar has been configured with a {@link ResourcePatternResolver},
* a ResourceArrayPropertyEditor will be registered as well.
* @see org.springframework.core.io.ResourceEditor
* @see org.springframework.beans.propertyeditors.InputStreamEditor
* @see org.springframework.beans.propertyeditors.InputSourceEditor
* @see org.springframework.beans.propertyeditors.FileEditor
* @see org.springframework.beans.propertyeditors.URLEditor
* @see org.springframework.beans.propertyeditors.URIEditor
* @see org.springframework.beans.propertyeditors.ClassEditor
* @see org.springframework.beans.propertyeditors.ClassArrayEditor
* @see org.springframework.core.io.support.ResourceArrayPropertyEditor
*/
@Override
public void registerCustomEditors(PropertyEditorRegistry registry) {
ResourceEditor baseEditor = new ResourceEditor(this.resourceLoader, this.propertyResolver);
doRegisterEditor(registry, Resource.class, baseEditor);
doRegisterEditor(registry, ContextResource.class, baseEditor);
doRegisterEditor(registry, InputStream.class, new InputStreamEditor(baseEditor));
doRegisterEditor(registry, InputSource.class, new InputSourceEditor(baseEditor));
doRegisterEditor(registry, File.class, new FileEditor(baseEditor));
if (pathClass != null) {
doRegisterEditor(registry, pathClass, new PathEditor(baseEditor));
}
doRegisterEditor(registry, Reader.class, new ReaderEditor(baseEditor));
doRegisterEditor(registry, URL.class, new URLEditor(baseEditor));
ClassLoader classLoader = this.resourceLoader.getClassLoader();
doRegisterEditor(registry, URI.class, new URIEditor(classLoader));
doRegisterEditor(registry, Class.class, new ClassEditor(classLoader));
doRegisterEditor(registry, Class[].class, new ClassArrayEditor(classLoader));
if (this.resourceLoader instanceof ResourcePatternResolver) {
doRegisterEditor(registry, Resource[].class,
new ResourceArrayPropertyEditor((ResourcePatternResolver) this.resourceLoader, this.propertyResolver));
}
}
/**
* Override default editor, if possible (since that's what we really mean to do here);
* otherwise register as a custom editor.
*/
private void doRegisterEditor(PropertyEditorRegistry registry, Class<?> requiredType, PropertyEditor editor) {
if (registry instanceof PropertyEditorRegistrySupport) {
((PropertyEditorRegistrySupport) registry).overrideDefaultEditor(requiredType, editor);
}
else {
registry.registerCustomEditor(requiredType, editor);
}
}
在doRegisterEditor 方法中,可以看到自定義屬性編譯器中使用的關(guān)鍵代碼 registry.registerCustomEditor(requiredType, editor) ; 回過頭來看,其實(shí) ResourceEditorRegistrar 類其實(shí)無非就是注冊(cè)了一系列的常用類型的屬性編輯器。例如,代碼 doRegisterEditor(registry, Class.class, new ClassEditor(classLoader)) 實(shí)現(xiàn)的功能就是注冊(cè)Class類對(duì)應(yīng)的屬性編輯器。那么,注冊(cè)之后,一旦某個(gè)實(shí)體bean中存在一些Class類型的屬性,那么Spring會(huì)調(diào)用ClassEditor將配置中定義的String類型轉(zhuǎn)換為Class類型并進(jìn)行賦值。
分析到這里,依然有個(gè)疑問那就是 beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment())) 僅僅是注冊(cè)了 ResourceEditorRegistrar 實(shí)例,但是卻沒有調(diào)用 registerCustomEditors進(jìn)行注冊(cè) ,那么到底什么時(shí)候進(jìn)行這些屬性編輯器的注冊(cè)呢?進(jìn)一步我們查看ResourceEditorRegistrar#registerCustomEditors 的調(diào)用棧:

可以看到一個(gè)比較熟悉的方法在調(diào)用,那就是 AbstractBeanFactory#initBeanWrapper ,這是在bean 初始化時(shí)使用的一個(gè)方法,主要是將BeanDefinition 轉(zhuǎn)換為BeanWrapper 后用于對(duì)屬性的填充。到此邏輯已經(jīng)明了,在bean的初始化后,會(huì)調(diào)用 ResourceEditorRegistrar#registerCustomEditors 方法進(jìn)行批量通用屬性編輯器注冊(cè)。注冊(cè)后,在屬性填充時(shí),Spring便可以使用這些屬性編輯器進(jìn)行屬性的解析注入了。
既然提到了BeanWrapper, 這里需要強(qiáng)調(diào)下,Spring中用于封裝Bean的是BeanWrapper類型,而它又間接繼承了 PropertyEditorRegistry ,也就是我們上面自定義的屬性編輯器注冊(cè)時(shí)的方法參數(shù) PropertyEditorRegistry registry ,其實(shí)大部分情況下都是BeanWrapper,對(duì)于BeanWrapper 在 Spring 中的默認(rèn)實(shí)現(xiàn)為 BeanWrapperImpl , 而 BeanWrapperImpl 除了實(shí)現(xiàn) BeanWrapper 接口外還間接繼承了 PropertyEditorRegistrySupport ,在 PropertyEditorRegistrySupport 中有這樣一個(gè)方法:
/**
* Actually register the default editors for this registry instance.
*/
private void createDefaultEditors() {
this.defaultEditors = new HashMap<Class<?>, PropertyEditor>(64);
// Simple editors, without parameterization capabilities.
// The JDK does not contain a default editor for any of these target types.
this.defaultEditors.put(Charset.class, new CharsetEditor());
this.defaultEditors.put(Class.class, new ClassEditor());
this.defaultEditors.put(Class[].class, new ClassArrayEditor());
this.defaultEditors.put(Currency.class, new CurrencyEditor());
this.defaultEditors.put(File.class, new FileEditor());
this.defaultEditors.put(InputStream.class, new InputStreamEditor());
this.defaultEditors.put(InputSource.class, new InputSourceEditor());
this.defaultEditors.put(Locale.class, new LocaleEditor());
if (pathClass != null) {
this.defaultEditors.put(pathClass, new PathEditor());
}
this.defaultEditors.put(Pattern.class, new PatternEditor());
this.defaultEditors.put(Properties.class, new PropertiesEditor());
this.defaultEditors.put(Reader.class, new ReaderEditor());
this.defaultEditors.put(Resource[].class, new ResourceArrayPropertyEditor());
this.defaultEditors.put(TimeZone.class, new TimeZoneEditor());
this.defaultEditors.put(URI.class, new URIEditor());
this.defaultEditors.put(URL.class, new URLEditor());
this.defaultEditors.put(UUID.class, new UUIDEditor());
if (zoneIdClass != null) {
this.defaultEditors.put(zoneIdClass, new ZoneIdEditor());
}
// Default instances of collection editors.
// Can be overridden by registering custom instances of those as custom editors.
this.defaultEditors.put(Collection.class, new CustomCollectionEditor(Collection.class));
this.defaultEditors.put(Set.class, new CustomCollectionEditor(Set.class));
this.defaultEditors.put(SortedSet.class, new CustomCollectionEditor(SortedSet.class));
this.defaultEditors.put(List.class, new CustomCollectionEditor(List.class));
this.defaultEditors.put(SortedMap.class, new CustomMapEditor(SortedMap.class));
// Default editors for primitive arrays.
this.defaultEditors.put(byte[].class, new ByteArrayPropertyEditor());
this.defaultEditors.put(char[].class, new CharArrayPropertyEditor());
// The JDK does not contain a default editor for char!
this.defaultEditors.put(char.class, new CharacterEditor(false));
this.defaultEditors.put(Character.class, new CharacterEditor(true));
// Spring's CustomBooleanEditor accepts more flag values than the JDK's default editor.
this.defaultEditors.put(boolean.class, new CustomBooleanEditor(false));
this.defaultEditors.put(Boolean.class, new CustomBooleanEditor(true));
// The JDK does not contain default editors for number wrapper types!
// Override JDK primitive number editors with our own CustomNumberEditor.
this.defaultEditors.put(byte.class, new CustomNumberEditor(Byte.class, false));
this.defaultEditors.put(Byte.class, new CustomNumberEditor(Byte.class, true));
this.defaultEditors.put(short.class, new CustomNumberEditor(Short.class, false));
this.defaultEditors.put(Short.class, new CustomNumberEditor(Short.class, true));
this.defaultEditors.put(int.class, new CustomNumberEditor(Integer.class, false));
this.defaultEditors.put(Integer.class, new CustomNumberEditor(Integer.class, true));
this.defaultEditors.put(long.class, new CustomNumberEditor(Long.class, false));
this.defaultEditors.put(Long.class, new CustomNumberEditor(Long.class, true));
this.defaultEditors.put(float.class, new CustomNumberEditor(Float.class, false));
this.defaultEditors.put(Float.class, new CustomNumberEditor(Float.class, true));
this.defaultEditors.put(double.class, new CustomNumberEditor(Double.class, false));
this.defaultEditors.put(Double.class, new CustomNumberEditor(Double.class, true));
this.defaultEditors.put(BigDecimal.class, new CustomNumberEditor(BigDecimal.class, true));
this.defaultEditors.put(BigInteger.class, new CustomNumberEditor(BigInteger.class, true));
// Only register config value editors if explicitly requested.
if (this.configValueEditorsActive) {
StringArrayPropertyEditor sae = new StringArrayPropertyEditor();
this.defaultEditors.put(String[].class, sae);
this.defaultEditors.put(short[].class, sae);
this.defaultEditors.put(int[].class, sae);
this.defaultEditors.put(long[].class, sae);
}
}
通過這個(gè)方法我們已經(jīng)知道了在Spring中定義了一系列常用的屬性編輯器使我們可以方便的進(jìn)行配置。 如果我們定義的bean中有某個(gè)屬性不在上面的類型中,我們才需要進(jìn)行個(gè)性化的屬性編輯器的注冊(cè)。
添加 ApplicationContextAwareProcessor 處理器
? 繼續(xù)跟蹤 org.springframework.context.support.AbstractApplicationContext#prepareBeanFactory 方法的主線,接下來的 beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this)) 其實(shí)就是注冊(cè)個(gè)BeanPostProcessor,而真正的邏輯還是在 ApplicationContextAwareProcessor 中。
ApplicationContextAwareProcessor 實(shí)現(xiàn)了 BeanPostProcessor 接口,在這里需要講一下,在bean實(shí)例化的時(shí)候,也就是Spring 激活bean 的 init-method 前后,會(huì)調(diào)用BeanPostProcessor 的 postProcessBeforeInitialization 和 postProcessAfterInitialization 方法。同樣對(duì)于 ApplicationContextAwareProcessor 我們同樣關(guān)注于這兩個(gè)方法。
在 postProcessAfterInitialization 方法中并沒有做什么邏輯處理。
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
return bean;
}
那么,我們著重看下 postProcessBeforeInitialization
@Override
public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException {
AccessControlContext acc = null;
if (System.getSecurityManager() != null &&
(bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware ||
bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware ||
bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware)) {
acc = this.applicationContext.getBeanFactory().getAccessControlContext();
}
if (acc != null) {
AccessController.doPrivileged(new PrivilegedAction<Object>() {
@Override
public Object run() {
invokeAwareInterfaces(bean);
return null;
}
}, acc);
}
else {
invokeAwareInterfaces(bean);
}
return bean;
}
private void invokeAwareInterfaces(Object bean) {
if (bean instanceof Aware) {
if (bean instanceof EnvironmentAware) {
((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
}
if (bean instanceof EmbeddedValueResolverAware) {
((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);
}
if (bean instanceof ResourceLoaderAware) {
((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
}
if (bean instanceof ApplicationEventPublisherAware) {
((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
}
if (bean instanceof MessageSourceAware) {
((MessageSourceAware) bean).setMessageSource(this.applicationContext);
}
if (bean instanceof ApplicationContextAware) {
((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
}
}
}
postProcessBeforeInitialization 方法中調(diào)用了 invokeAwareInterfaces。 從invokeAwareInterfaces 方法中,我們或多或少已經(jīng)了解了Spring 的用意,實(shí)現(xiàn)這些Aware接口的bean在被初始化之后,可以取得一些對(duì)應(yīng)的資源。
設(shè)置忽略依賴
當(dāng) Spring 上一步將 ApplicationContextAwareProcessor注冊(cè)后,那么在 invokeAwareInterfaces 方法中間接調(diào)用的 Aware 類 已經(jīng)不是普通的bean了,如 MessageSourceAware 、ResourceLoaderAware 等,那么當(dāng)前需要在 Spring 做bean的依賴注入的時(shí)候忽略它們,而 ignoreDependencyInterface 的作用正是如此。
// 設(shè)置幾個(gè)忽略自動(dòng)裝配的接口
beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
注冊(cè)依賴
Spring 中有了忽略依賴的功能,必不可少的也會(huì)有注冊(cè)依賴的功能。
beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
beanFactory.registerResolvableDependency(ResourceLoader.class, this);
beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
beanFactory.registerResolvableDependency(ApplicationContext.class, this);
當(dāng)注冊(cè)了依賴解析后,例如當(dāng)注冊(cè)了對(duì)BeanFactory.class 的解析依賴后,當(dāng)bean的屬性注入的時(shí)候,一旦檢測(cè)到屬性為BeanFactory類型將會(huì)把beanFactory的實(shí)例注入進(jìn)去。
由于Spring容器啟動(dòng)這篇文章篇幅較長(zhǎng),本篇暫時(shí)介紹到這里,下一篇文章會(huì)接著分析BeanFactory的后處理、初始化非延遲加載單例、finishRefresh等方法的具體細(xì)節(jié)。
請(qǐng)務(wù)必點(diǎn)擊閱讀此文的下篇 《Spring容器基本實(shí)現(xiàn)之源碼分析-下篇 傳送門》 ,看完后必定會(huì)對(duì) Spring 的整體把控會(huì)有更深一步的理解。