經(jīng)過(guò)之前的分析丐一,我們已經(jīng)了解了Spring是如何解析和加載的bean的。但是在上一篇文章結(jié)束的時(shí)候說(shuō)到庸疾,一般我們寫(xiě)程序是不會(huì)用到BeanFactory來(lái)當(dāng)做Spring的容器的仓手,一般使用的是ApplicationContext作為Spring的容器睡互。這兩者之間有什么區(qū)別呢?
ApplicationContext和BeanFactory在Spring中都是用于加載bean的舰涌,但是ApplicationContext提供了許多擴(kuò)展功能猖任,換句話說(shuō),BeanFactory有的功能ApplicationContext全都有瓷耙,而Application有的功能朱躺,BeanFactory卻不一定有,所有在一般情況下我們寫(xiě)程序用到的都是ApplicationContext作為Spring的容器哺徊。
那么室琢,ApplicationContext比BeanFactory多出了哪些功能呢?這就是我們下面要關(guān)注的問(wèn)題落追。
首先盈滴,我們首先來(lái)看一看這兩個(gè)不同的類在加載配置文件上的寫(xiě)法的不同:
使用BeanFactory方式加載XML
BeanFactory bf = new XmlBeanFactory(new ClassPathResource("beanFactoryTest.xml"));
使用ApplicationContext方式加載XML
ApplicationContext bf = new ClassPathXmlApplicationContext("beanFactoryTest.xml");
與之前研究BeanFactory一樣,我們這次還是從ClassPathXmlApplication作為切入點(diǎn)轿钠,開(kāi)始對(duì)整體功能進(jìn)行分析
package org.springframework.context.support;
public class ClassPathXmlApplicationContext extends AbstractXmlApplicationContext {
public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
this(new String[] {configLocation}, true, null);
}
public ClassPathXmlApplicationContext(
String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
throws BeansException {
super(parent);
setConfigLocations(configLocations);
if (refresh) {
refresh();
}
}
}
設(shè)置路徑是必不可少的步驟巢钓,ClassPathXmlApplicationContext中可以將配置文件路徑以數(shù)組的方式傳入,ClassPathXmlApplicationContext可以對(duì)數(shù)組進(jìn)行解析并進(jìn)行加載疗垛。而對(duì)于解析及功能的實(shí)現(xiàn)都在refresh()中實(shí)現(xiàn)症汹。
設(shè)置配置路徑
在ClassPathXmlApplicationContext中支持多個(gè)配置文件以數(shù)組方式同時(shí)傳入:
public void setConfigLocations(@Nullable 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;
}
}
此函數(shù)主要用于解析給定的路徑數(shù)組,當(dāng)然贷腕,如果數(shù)組中包含特殊符號(hào)背镇,如${var}咬展,那么在resolvePath中會(huì)搜索匹配的系統(tǒng)變量并替換
擴(kuò)展功能
設(shè)置了路徑之后,便可以根據(jù)路徑做配置文件的解析以及各種功能的實(shí)現(xiàn)了瞒斩∑破牛可以說(shuō)refresh函數(shù)中幾乎包含了ApplicationContext中提供的全部功能,而且此函數(shù)中邏輯非常清楚明了胸囱,使我們很容易分析對(duì)應(yīng)的層次及邏輯祷舀。
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
//準(zhǔn)備刷新的上下文環(huán)境
prepareRefresh();
//初始化BeanFactory,并進(jìn)行XML文件讀取
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
//對(duì)BeanFactory進(jìn)行各種 功能填充
prepareBeanFactory(beanFactory);
try {
//子類覆蓋方法做額外的處理
postProcessBeanFactory(beanFactory);
//激活各種BeanFactory處理器
invokeBeanFactoryPostProcessors(beanFactory);
//注冊(cè)攔截Bean創(chuàng)建的Bean處理器烹笔,這里只是注冊(cè)裳扯,真正的調(diào)用是在getBean的時(shí)候
registerBeanPostProcessors(beanFactory);
//為上下文初始化Message源,即不同語(yǔ)言的消息體谤职,國(guó)際化處理
initMessageSource();
//初始化應(yīng)用消息廣播器饰豺,并放入"applicationEventMulticaster"bean中
initApplicationEventMulticaster();
//留給子類來(lái)初始化其他的bean
onRefresh();
//在所有注冊(cè)的bean中查找Listener bean,注冊(cè)到消息廣播中
registerListeners();
//初始化剩下的單實(shí)例(非惰性的)
finishBeanFactoryInitialization(beanFactory);
//完成刷新過(guò)程柬帕,通知生命周期處理器lifecycleProcessor刷新過(guò)程哟忍,同時(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();
}
}
}
下面概括一下ClassPathXmlApplicationContext初始化的步驟,并從中解釋一下它為我們提供的功能陷寝。
(1)初始化前的準(zhǔn)備工作锅很,例如對(duì)系統(tǒng)屬性或者環(huán)境變量進(jìn)行準(zhǔn)備及驗(yàn)證:在某種情況下項(xiàng)目的使用需要讀取某些系統(tǒng)變量,而這個(gè)變量的設(shè)置很可能會(huì)影響著系統(tǒng)的正確性凤跑,那么ClassPathXmlApplicationContext為我們提供的這個(gè)準(zhǔn)備函數(shù)就顯得非常必要爆安,它可以在Spring啟動(dòng)的時(shí)候提前對(duì)必須的變量進(jìn)行存在性驗(yàn)證。
(2)初始化BeanFactory仔引,并進(jìn)行XML文件讀热硬帧:之前提到過(guò)ClassPathXmlApplicationContext包含著B(niǎo)eanFactory所提供的一切特征,那么在這一步驟中將會(huì)復(fù)用BeanFactory中的配置文件讀取解析及其他功能咖耘,這一步之后翘簇,ClassPathXmlApplicationContext實(shí)際上就已經(jīng)包含了BeanFactory所提供的功能,也就是可以進(jìn)行Bean的提取等基礎(chǔ)操作了儿倒。
(3)對(duì)BeanFatory進(jìn)行各種功能填充:@Qualifer與@Autowired應(yīng)該是大家非常熟悉的注解版保,這兩個(gè)注解正是在這一步驟中增加的支持
(4)子類覆蓋方法做額外的處理:Spring之所以強(qiáng)大,為世人所推崇夫否,除了它功能上為大家提供了便利外朴爬,還有一方面就是它的完美架構(gòu)悍引,開(kāi)放式的架構(gòu)讓使用它的程序員很容易根據(jù)業(yè)務(wù)需要擴(kuò)展已經(jīng)存在的功能枉侧。這種開(kāi)放式的設(shè)計(jì)在Spring中隨處可見(jiàn)钳榨,例如在本例中就提供了一個(gè)空的函數(shù)實(shí)現(xiàn)postProcessBeanFactory來(lái)方便程序員在業(yè)務(wù)上做進(jìn)一步擴(kuò)展。
(5)激活各種BeanFatory處理器
(6)注冊(cè)攔截bean創(chuàng)建的bean處理器微谓,這里只是注冊(cè)森篷,真正的調(diào)用是在getBean的時(shí)候
(7)為上下文初始化Message源输钩,即對(duì)不同語(yǔ)言的消息體進(jìn)行國(guó)際化處理
(8)初始化應(yīng)用消息廣播器,并放入"applicationEventMulticaster"bean中
(9)留給子類來(lái)初始化其他的bean
(10)在所有注冊(cè)的bean中查找listener bean疾宏,注冊(cè)到消息廣播器中
(11)初始化剩下的單實(shí)例(非惰性的)
(12)完成刷新過(guò)程张足,通知聲明周期處理器lifecycleProcessor刷新過(guò)程,同時(shí)發(fā)出ContextRefreshEven通知?jiǎng)e人
環(huán)境準(zhǔn)備
protected void prepareRefresh() {
this.startupDate = System.currentTimeMillis();
this.closed.set(false);
this.active.set(true);
if (logger.isInfoEnabled()) {
logger.info("Refreshing " + this);
}
//留給子類覆蓋
initPropertySources();
//驗(yàn)證需要的屬性文件是否都已經(jīng)放入環(huán)境中
getEnvironment().validateRequiredProperties();
// Allow for the collection of early ApplicationEvents,
// to be published once the multicaster is available...
this.earlyApplicationEvents = new LinkedHashSet<>();
}
有人說(shuō)其實(shí)這個(gè)函數(shù)沒(méi)什么用坎藐,因?yàn)樽詈髢删浯a才是最為關(guān)鍵的,但是卻沒(méi)有什么邏輯處理哼绑,initPropertySources是空的岩馍,沒(méi)有任何邏輯,而getEnvironment().validateRequiredProperties也因?yàn)闆](méi)有需要驗(yàn)證的屬性而沒(méi)有做任何處理抖韩。其實(shí)這都是因?yàn)闆](méi)有徹底理解才會(huì)這么說(shuō)蛀恩,這個(gè)函數(shù)用好了作用還是很大的。那么茂浮,該怎么用呢双谆?我們先探索下各個(gè)函數(shù)的作用。
(1)initPropertySources:正符合Spring的開(kāi)放式結(jié)構(gòu)設(shè)計(jì)席揽,給用戶最大擴(kuò)展Spring的能力顽馋。用戶可以根據(jù)自身的需要重寫(xiě)initPropertySources方法,并在方法中進(jìn)行個(gè)性化的屬性處理及設(shè)置幌羞。
(2)validateRequiredProperties:則是對(duì)屬性進(jìn)行驗(yàn)證寸谜,那么如何驗(yàn)證呢?我們舉個(gè)融合兩句代碼的小例子來(lái)幫助大家理解属桦。
假如現(xiàn)在有這樣一個(gè)需求熊痴,工程在運(yùn)行過(guò)程中用到的摸個(gè)設(shè)置(例如VAR)是從系統(tǒng)環(huán)境變量中取得的,而如果用戶沒(méi)有在系統(tǒng)環(huán)境變量中配置這個(gè)參數(shù)聂宾,那么工程可能不會(huì)工作果善,這一要求可能會(huì)有各種各樣的解決辦法,當(dāng)然系谐,在Spring中可以這樣做巾陕,你可以直接修改Spring的源碼,例如修改ClassPathXmlApplicationContext蔚鸥。不過(guò)最好的辦法還是對(duì)源碼進(jìn)行擴(kuò)展惜论,我們可以自定義類:
public class MyClassPathXmlApplicationContext extends ClassPathXmlApplicationContext{
public MyClassPathXmlApplicationContext(String... configLocation){
super(configLocation);
}
protected void initPropertySources(){
getEnvironment().setRequiredProperties("VAR");
}
}
我們自定義了繼承自ClassPathXmlApplicationContext的MyClassPathXmlApplicationContext,并重寫(xiě)了initPropertySources方法止喷,在方法中添加了我們的個(gè)性化需求馆类,那么在驗(yàn)證的時(shí)候也就是程序走到getEnvironment().validateRequiredProperties()代碼的時(shí)候,如果系統(tǒng)并沒(méi)有檢測(cè)到對(duì)應(yīng)VAR的環(huán)境變量弹谁,那么將拋出異常乾巧。當(dāng)然我們還需要在使用的時(shí)候替換掉原有的ClassPathXmlApplicationContext:
public static void main(String[] args){
ApplicationContext bf = new ClassPathXmlApplicationContext("test/customag/test.xml");
User user = (User)bf.getBean("user");
}
加載BeanFactory
obtainFreshBeanFactory方法從字面理解是獲取BeanFactory句喜。之前說(shuō)過(guò),ApplicationContext是對(duì)BeanFactory在功能上的擴(kuò)展沟于,不但包含了BeanFactory的全部功能更在其基礎(chǔ)上添加了大量的擴(kuò)展應(yīng)用咳胃,那么obtainFreshBeanFactory正是實(shí)現(xiàn)BeanFactory的地方,也就是經(jīng)過(guò)這個(gè)函數(shù)后旷太,ApplicationContext正式擁有了BeanFactory的全部功能展懈。
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
refreshBeanFactory();
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (logger.isDebugEnabled()) {
logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
}
return beanFactory;
}
protected final void refreshBeanFactory() throws BeansException {
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
//創(chuàng)建DefualtListableBeanFactory
DefaultListableBeanFactory beanFactory = createBeanFactory();
//為了序列化指定id,如果需要的話供璧,讓這個(gè)BeanFactory從id反序列化到BeanFactory對(duì)象
beanFactory.setSerializationId(getId());
//定制beanFactory相關(guān)屬性存崖,包括是否允許覆蓋同名稱的不同定義的對(duì)象以及
//設(shè)置@Autowired和@Qualifier注解解析器QualifierAnnotationAutowiredCandidateResolver
customizeBeanFactory(beanFactory);
//初始化DocumentReader,并進(jìn)行XML文件讀取及解析
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
我們?cè)敿?xì)分析上面的每個(gè)步驟
(1)創(chuàng)建DefaultListableBeanFactory睡毒。
在介紹BeanFactory的時(shí)候来惧,不知道大家有沒(méi)有印象,聲明方式為:BeanFactory bf = new XmlBeanFactory("test.xml")演顾,其中的XmlBeanFactory繼承自DefaultListableBeanFactory,并提供了XmlBeanDefinitionReader類型的reader屬性供搀,也就是說(shuō)DefaultListableBeanFactory是容器的基礎(chǔ)。必須首先實(shí)例化钠至,那么在這里就是實(shí)例化DefaultListableBeanFactory的步驟葛虐。
(2)指定序列化ID
(3)定制BeanFactory
(4)加載BeanDefinition
(5)使用全局變量BeanFactory類實(shí)例
因?yàn)镈efaultListableBeanFactory類型的變量beanFactory是函數(shù)內(nèi)的局部變量,所以要使用全局變量來(lái)記錄結(jié)果棕洋。
定制BeanFactory
這里已經(jīng)開(kāi)始了對(duì)BeanFactory功能的擴(kuò)展挡闰,在基本容器的基礎(chǔ)上,增加了是否允許覆蓋是否允許循環(huán)依賴掰盘。
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
//如果屬性allowBeanDefinitionOverriding不為空摄悯,設(shè)置給beanFactory對(duì)象相應(yīng)屬性,
//此屬性含義:是否允許覆蓋同名稱的不同定義的對(duì)象
if (this.allowBeanDefinitionOverriding != null) {
beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
//如果屬性allowCircularReferences不為空愧捕,設(shè)置給beanFactory對(duì)象相應(yīng)屬性奢驯,
//此屬性含義:是否允許bean之間循環(huán)依賴
if (this.allowCircularReferences != null) {
beanFactory.setAllowCircularReferences(this.allowCircularReferences);
}
}
對(duì)于允許覆蓋和允許循環(huán)依賴的設(shè)置這里只是判斷了是否為空,如果不為空要進(jìn)行設(shè)置次绘,但是并沒(méi)有看到在哪里進(jìn)行設(shè)置瘪阁,究竟這個(gè)設(shè)置是在哪里進(jìn)行設(shè)置的呢?還是那句話邮偎,使用子類覆蓋方法管跺,例如:
public class MyClassPathXmlApplicationContext extends ClassPathXmlApplicationContext{
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory){
super.setAllowBeanDefinitionOverriding(false);
super.setAllowCircularReferences(false);
super.customizeBeanFactory(this.beanFactory);
}
}
設(shè)置完后相信大家已經(jīng)對(duì)于這兩個(gè)屬性的使用有所了解,或者可以看前面的文章進(jìn)行了解禾进。
加載BeanDefinition
在第一步中提到了將ClassPathXmlApplicationContext與XmlBeanFactory創(chuàng)建的對(duì)比豁跑,在實(shí)現(xiàn)配置文件的加載功能中除了我們?cè)诘谝徊揭呀?jīng)初始化的DefaultListableBeanFactory外,還需要XmlBeanDefinitionReader來(lái)讀取XML泻云,那么在這個(gè)步驟中首先要做的就是初始化XmlBeanDefinitionReader艇拍。
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
//為給定的beanFactory創(chuàng)建一個(gè)XmlBeanDefinitionReader
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
//對(duì)beanDefinitionReader進(jìn)行環(huán)境變量的設(shè)置
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
//對(duì)BeanDefinitionReader進(jìn)行設(shè)置狐蜕,可以覆蓋
initBeanDefinitionReader(beanDefinitionReader);
loadBeanDefinitions(beanDefinitionReader);
}
在初始化了DefaultListableBeanFactory和XmlBeanDefinitionReader后就可以配置文件的讀取了。
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)行配置文件的加載機(jī)制注冊(cè)卸夕,后面的代碼就又回到了前兩篇文章中所分析的流程了层释,所以這里就不再贅述了。同時(shí)快集,經(jīng)過(guò)此步驟之后贡羔,XmlBeanDefinitionReader所讀取的BeanDefinitionHolder就都注冊(cè)到了DefaultListableBeanFactory中了,此時(shí)的DefaultListableBeanFactory已經(jīng)包含了所有解析好的配置了碍讨。
功能擴(kuò)展
進(jìn)入函數(shù)prepareBeanFactory之前治力,Spring已經(jīng)完成了對(duì)配置的解析,而ApplicationContext在功能上的擴(kuò)展也由此展開(kāi)
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}的形式來(lái)調(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);
//設(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);
//添加一個(gè)ApplicationListenerDetector類型的BeanPostProcessor
beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));
//增加對(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ò)展覆获。
1.增加對(duì)SpEL語(yǔ)言的支持
2.增加對(duì)屬性編輯器的支持
3.增加對(duì)一些內(nèi)置類,比如EnvironmentAware瓢省、MessageSourceAware的信息注入弄息。
4.設(shè)置了依賴功能可忽略的接口。
5.注冊(cè)一些固定依賴的屬性
6.增加AspectJ的支持
7.將相關(guān)環(huán)境變量及屬性注冊(cè)以單利模式注冊(cè)勤婚。
下面我們來(lái)一一的看一下每個(gè)步驟的流程
增加SPEL語(yǔ)言的支持
Spring表達(dá)式語(yǔ)言全稱為"Spring Expression Language"摹量,縮寫(xiě)為"SpEL",類似于Struts2x中使用的OGNL表達(dá)式語(yǔ)言馒胆,能在運(yùn)行時(shí)構(gòu)建復(fù)雜表達(dá)式缨称、存取對(duì)象圖屬性、對(duì)象方法調(diào)用等祝迂,并且能與Spring功能完美整合睦尽,比如能用來(lái)配置bean定義。SpEL是單獨(dú)模塊型雳,只依賴與core模塊当凡,不依賴與其他模塊,可以單獨(dú)使用纠俭。
SpEL使用#{...}作為定界符沿量,所有在大括號(hào)中的字符都將被認(rèn)為是SpEL,使用格式如下:
<bean id="saxophone" value="com.xxx.xxx.Xxxx" />
<bean>
<property name="instument" value="#{saxophone}"/>
</bean>
相當(dāng)于:
<bean id="saxophone" value="com.xxx.xxx.Xxxx" />
<bean>
<property name="instrument" ref="saxophone">
</bean>
當(dāng)然冤荆,上面只是列舉了其中最簡(jiǎn)單的使用方式朴则,SpEL功能非常強(qiáng)大,使用好可以大大提高開(kāi)發(fā)效率匙赞,這里只是為了幫助我們理解源碼佛掖,所以不做過(guò)多的研究妖碉。
在源碼中通過(guò)代碼beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver())注冊(cè)語(yǔ)言解析器,就可以對(duì)SpEL進(jìn)行解析了芥被,那么在注冊(cè)解析器后Spring又是在什么時(shí)候調(diào)用這個(gè)解析器進(jìn)行解析呢欧宜?
之前我們說(shuō)過(guò)Spring在bean進(jìn)行初始化的時(shí)候會(huì)有屬性填充這一步,而在這步中Spring會(huì)調(diào)用AbstractAutowireCapableBeanFactory類applyPropertyValues函數(shù)來(lái)完成功能拴魄。就在這個(gè)函數(shù)中冗茸,會(huì)通過(guò)構(gòu)造BeanDefinitionValueResolver類型實(shí)例valueResolver來(lái)進(jìn)行屬性值的解析。同時(shí)匹中,也是在這個(gè)步驟中一般通過(guò)AbstractBeanFactory中的evaluateBeanDefinitionString方法去完成SpEL解析夏漱。
protected Object evaluateBeanDefinitionString(@Nullable String value, @Nullable BeanDefinition beanDefinition) {
if (this.beanExpressionResolver == null) {
return value;
}
Scope scope = null;
if (beanDefinition != null) {
String scopeName = beanDefinition.getScope();
if (scopeName != null) {
scope = getRegisteredScope(scopeName);
}
}
return this.beanExpressionResolver.evaluate(value, new BeanExpressionContext(this, scope));
}
當(dāng)調(diào)用這個(gè)方法時(shí)會(huì)判斷是否存在語(yǔ)言解析器,如果存在則調(diào)用語(yǔ)言解析器的方法進(jìn)行解析顶捷,解析的過(guò)程是在Spring的expression的包內(nèi)挂绰,這里不做過(guò)多解釋。我們通過(guò)查看對(duì)evaluateBeanDefinitionString方法的調(diào)用層次可以看出服赎,應(yīng)用語(yǔ)言解析器的調(diào)用主要是在解析依賴注入bean的時(shí)候葵蒂,以及在完成bean的初始化和屬性獲取后進(jìn)行屬性填充的時(shí)候。
增加屬性注冊(cè)編輯器
在Spring依賴注入的時(shí)候可以把普通屬性注入進(jìn)來(lái)重虑,但是像Date類型就無(wú)法被識(shí)別践付,例如:
public class UserManager{
private Date dataValue;
public Date getDataValue(){
return dataValue;
}
public void setDataValue(Date dataValue){
this.dataValue = dataValue;
}
public String toString(){
return "dataValue = " + dataValue;
}
}
上面代碼中,需要對(duì)日期型屬性進(jìn)行注入:
<bean id="userManager" class="com.test.UserManager">
<property name="dataValue">
<value>2018-06-30</value>
</property>
</bean>
測(cè)試代碼:
@Test
public void testDate(){
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
UserManager userManager = (UserManager)ctx.getBean("userManager");
System.out.println(userManager);
}
如果直接這樣使用缺厉,程序則會(huì)報(bào)異常永高,類型轉(zhuǎn)換不成功。因?yàn)樵赨serManager中的dataValue屬性是Date類型的提针,而在XML中配置的則是String類型的命爬,所以會(huì)報(bào)異常。
Spring針對(duì)這個(gè)問(wèn)題提供了兩種解決方法:
1.使用自定義屬性編輯器
使用自定義屬性編輯器关贵,通過(guò)繼承PropertyEditorSupport遇骑,重寫(xiě)setAsText方法,具體步驟如下:
(1)編寫(xiě)自定義的屬性編輯器:
public class DatePropertyEditor extends PropertyEditorSupport{
private String format = "yyyy-MM-dd";
public void setFormat(String format){
this.format = format;
}
public void setAsText(String arg0) throws IllegalArgumentException{
System.out.println("arg0: " + arg0);
SimpleDateFormat sdf = new SimpleDateFormat(format);
try{
Date d = sdf.parse(arg0);
this.setValue(d);
}catch(ParseException e){
e.printStackTrace();
}
}
}
(2)將自定義屬性編輯器注冊(cè)到Spring中:
<bean class="org.Springframework.beans.factory.config.CustomEditorConfigurer">
<property name="customEditors">
<map>
<entry key="java.util.Date">
<bean class="com.test.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)的類型。通過(guò)這樣的配置炭剪,當(dāng)Spring注入bean的屬性時(shí)一旦遇到了java.util.Date類型的屬性會(huì)自動(dòng)調(diào)用自定義的DatePropertyEditor解析器進(jìn)行解析练链,并用解析結(jié)果代替配置屬性進(jìn)行注入。
2.注冊(cè)Spring自帶的屬性編輯器CustomDateEditor
通過(guò)注冊(cè)Spring自帶的屬性編輯器CustomDateEditor奴拦,具體步驟如下:
(1)定義屬性編輯器
public class DatePropertyEditorRegistrar implements PropertyEditorRegistrar{
public void registerCustomEditors(PropertyEditorRegistry registry){
registry.registerCustomEditors(Date.class, new CustomDateEditor(
new SimpleDateFormat("yyyy-MM-dd"), true)
);
}
}
(2)注冊(cè)到Spring中
<bean class="org.Springframework.bean.factory.config.CustomEditorConfigurer">
<property name="propertyEditorRegistrars">
<list>
<bean class="com.test.DatePropertyEditorRegistrar" />
</list>
</property>
</bean>
通過(guò)在配置文件中將自定義的DatePropertyEditorRegistrar注冊(cè)進(jìn)入org.Springframework.beans.factory.config. CustomEditorConfigurer的propertyEditorRegistrars屬性中媒鼓,可以具有與方法1同樣的效果。
我們了解了自定義屬性編輯器的使用,但是绿鸣,這似乎與本篇文章中圍繞的核心代碼beanFactory.addPropertyEditorRegistrar( newResourceEditorRegistrar(this, getEnvironment()))并無(wú)聯(lián)系疚沐,因?yàn)樵谧?cè)自定義屬性編輯器的時(shí)候使用的是PropertyEditor Registry的registerCustomEditor方法,我們不妨深入探索一下ResourceEditorRegistrar的內(nèi)部實(shí)現(xiàn)潮模,在ResourceEditorRegistrar中亮蛔,我們最關(guān)心的方法是registerCustomEditors。
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));
doRegisterEditor(registry, Path.class, 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));
}
}
private void doRegisterEditor(PropertyEditorRegistry registry, Class<?> requiredType, PropertyEditor editor) {
if (registry instanceof PropertyEditorRegistrySupport) {
((PropertyEditorRegistrySupport) registry).overrideDefaultEditor(requiredType, editor);
}
else {
registry.registerCustomEditor(requiredType, editor);
}
}
在doRegisterEditor函數(shù)中擎厢,可以看到在之前提到的自定義屬性中使用的關(guān)鍵代碼:registry.registerCustomEditor(requiredType, editor)究流,回過(guò)頭來(lái)看ResourceEditorRegistrar類的registerCustomEditors方法的核心功能,其實(shí)無(wú)非是注冊(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è)疑問(wèn)炎疆,雖說(shuō)ResourceEditorRegistrar類的registerCustomEditors方法實(shí)現(xiàn)了批量注冊(cè)的功能,但是bean Factroy.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()))僅僅是注冊(cè)了ResourceEditor Registrar實(shí)例国裳,卻并沒(méi)有調(diào)用ResourceEditorRegistrar的registerCustomEditors方法進(jìn)行注冊(cè),那么到底是什么時(shí)候進(jìn)行注冊(cè)的呢全跨,進(jìn)一步查看ResourceEditorRegistrar的registerCustomEditors方法的調(diào)用層次結(jié)構(gòu)缝左,如圖:
發(fā)現(xiàn)在AbstractBeanFactory中的registerCustomEditors方法中被調(diào)用過(guò),繼續(xù)查看AbstractBeanFactory中的register CustomEditors方法的調(diào)用層次結(jié)構(gòu)浓若,如圖:
其中我們看到了一個(gè)熟悉的方法渺杉,就是initBeanWrapper方法,這是在bean初始化時(shí)使用的一個(gè)方法挪钓,之前已經(jīng)使用過(guò)大量的篇幅進(jìn)行講解是越,主要是將BeanDefinition轉(zhuǎn)換為BeanWrapper后用于對(duì)屬性的填充。至此碌上,邏輯已經(jīng)明了倚评,在bean的初始化后悔調(diào)用ResourceEditorRegistrar的registerCustomEditors方法進(jìn)行批量的通用屬性編輯器注冊(cè)。注冊(cè)后馏予,在屬性填充的環(huán)節(jié)便可以直接讓Spring使用這些編輯器進(jìn)行屬性的解析了天梧。
既然提到了BeanWrapper,這里有必要強(qiáng)調(diào)下霞丧,Spring用于封裝bean的是BeanWrapper類型呢岗,而它又間接繼承了Property EditorRegistry類型,也就是我們之間反復(fù)看到的方法參數(shù)PropertyEditorRegistry,其實(shí)大部分情況下都是BeanWrapper后豫,對(duì)于BeanWrapper在Spring中默認(rèn)實(shí)現(xiàn)是BeanWrapperImpl悉尾,而B(niǎo)eanWrapperImpl除了實(shí)現(xiàn)BeanWrapper接口外還繼承了PropertyEditorRegistrySupport,在PropertyEditorRegistrySupport中有這樣一個(gè)方法:
private void createDefaultEditors() {
this.defaultEditors = new HashMap<>(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());
this.defaultEditors.put(Path.class, 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());
this.defaultEditors.put(ZoneId.class, 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);
}
}
具體的調(diào)用方法我們就不去深究了挫酿,但是至少通過(guò)這個(gè)方法我們已經(jīng)知道了在Spring中定義了上面一系列常用的屬性編輯器使我們可以方便地進(jìn)行配置构眯。如果我們定義的bean的某個(gè)屬性的類型不在上面的常用配置中的話,才需要我們進(jìn)行個(gè)性化屬性編輯器的注冊(cè)饭豹。
3.添加ApplicationContextAwareProcessor處理器
了解了屬性編輯器的使用后鸵赖,接下來(lái)我們繼續(xù)回到AbstractApplicationContext的prepareBeanFactory方法的主線來(lái)進(jìn)行函數(shù)追蹤。對(duì)于beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this))其實(shí)主要目的就是注冊(cè)BeanPost Processor拄衰,而真正的邏輯還是在ApplicationContextAwareProcessor中它褪。
ApplicationContextAwareProcessor實(shí)現(xiàn)了BeanPostProcessor接口,我們回顧一下之前的內(nèi)容翘悉,在bean實(shí)例化的時(shí)候茫打,也就是Spring激活bean的init-method的前后,會(huì)調(diào)用BeanPostProcessor的postProcessorBeforeInitialization方法和postProcessorAfterInitialization方法妖混,同樣老赤,對(duì)于ApplicationContextAwareProcessor我們也關(guān)心這兩個(gè)方法。
對(duì)于postProcessAfterInitialization方法制市,在ApplicationContextAwareProcessor中并沒(méi)有做過(guò)多邏輯處理
public Object postProcessAfterInitialization(Object bean, String beanName) {
return bean;
}
那么我們重點(diǎn)看一下postProcessBeforeInitialization方法
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((PrivilegedAction<Object>) () -> {
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)的資源。
4.設(shè)置忽略依賴
當(dāng)Spring將ApplicationContextAwareProcessor注冊(cè)后误褪,那么在invokeAwareInterfaces方法中間接調(diào)用的Aware類已經(jīng)不是普通的bean了责鳍,如ResourceLoaderAware、ApplicationEventPublisherAware等兽间,那么當(dāng)然需要再Spring做bean的依賴注入的時(shí)候忽略它們历葛。而ignoreDependencyInterfaces的作用正是在此。
beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
5.注冊(cè)依賴
Spring中有忽略依賴的功能嘀略,當(dāng)然也必不可少的會(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)去
BeanFactory的后處理
BeanFactory作為Spring中容器功能的基礎(chǔ),用于存放所有已經(jīng)加載的bean逮壁,為了保證程序上的高可擴(kuò)展性孵坚,Spring針對(duì)BeanFactory做了大量的擴(kuò)展粮宛,比如我們熟知的PostProcessor等都是在這里實(shí)現(xiàn)的。
1.激活注冊(cè)的BeanFactoryPostProcessor
正式開(kāi)始介紹之前我么你先了解下BeanFactoryPostProcessor的用法卖宠。
BeanFactoryPostProcessor接口和BeanPostProcessor類似巍杈,可以對(duì)bean的定義(配置元數(shù)據(jù))進(jìn)行處理,也就是說(shuō)扛伍,SpringIoC容器允許BeanFactoryPostProcessor在容器實(shí)際實(shí)例化任何其他的bean之前讀取配置元數(shù)據(jù)筷畦,并有可能修改它。如果你愿意刺洒,你可以配置多個(gè)BeanFactoryPostProcessor鳖宾。你還可以通過(guò)設(shè)置"order"屬性來(lái)控制BeanFactoryPostProcessor的執(zhí)行次序(僅當(dāng)BeanFactoryPostProcessor實(shí)現(xiàn)了Ordered接口)。具體請(qǐng)參考BeanFactoryPostProcessor和Ordered接口的JavaDoc以獲取更詳細(xì)的信息逆航。
如果你想改變實(shí)際的bean實(shí)例(例如從配置元數(shù)據(jù)創(chuàng)建的對(duì)象)鼎文,那么你最好使用BeanPostProcessor。同樣的因俐,BeanFactoryPost Processor的作用域范圍是容器級(jí)的拇惋。它只和你所使用的容器有關(guān)。如果你在容器中定義一個(gè)BeanFactoryPostProcessor抹剩,它僅僅對(duì)此容器中的bean進(jìn)行后置處理撑帖。BeanFactoryPostProcessor不會(huì)對(duì)定義在另一個(gè)容器中的bean進(jìn)行后置處理。即使這兩個(gè)容器在同一個(gè)層次上澳眷。在Spring中存在對(duì)于BeanFactoryPostProcessor的典型應(yīng)用胡嘿,比如PropertyPlaceholderConfigurer。
有時(shí)候钳踊,閱讀Spring的Bean描述文件時(shí)灶平,我們也許會(huì)遇到類似如下的一些配置:
<bean id="message" class="distConfig.HelloMessage">
<property name="mes">
<value>${bean.message}</value>
</property>
</bean>
其中竟然出現(xiàn)了變量引用:${bean.message}。這就是Spring的分散配置箍土,可以在另外的配置文件中為bean.message指定值。如在bean.property配置如下定義:
bean.message=Hi,can you find me?
當(dāng)訪問(wèn)名為message的bean時(shí)罐监,mes屬性就會(huì)被設(shè)置為“Hi,can you find me?”吴藻,但Spring框架是怎么知道存在這樣的配置文件的呢?這就要靠PropertyPlaceholderConfigurer這個(gè)類的bean:
<bean id="mesHandler" class="org.Springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>config/bean.properties</value>
</list>
</property>
</bean>
在這個(gè)bean中指定了配置文件為config/bean.properties弓柱。到這里似乎找到了問(wèn)題的答案了沟堡,但是其實(shí)還有個(gè)問(wèn)題。這個(gè)"mesHandler"只不過(guò)是Spring框架管理的一個(gè)bean矢空,并沒(méi)有被別的bean或者對(duì)象引用航罗,Spring的beanFactory是怎么知道要從這個(gè)bean中獲取配置信息的呢?
查看層級(jí)結(jié)構(gòu)可以看出PropertyPlaceholderConfigurer這個(gè)類間接繼承了BeanFactoryPostProcessor接口屁药。這是一個(gè)特別的接口粥血,當(dāng)Spring加載任何實(shí)現(xiàn)了這個(gè)接口的bean的配置時(shí),都會(huì)在bean工廠載入所有bean的配置之后執(zhí)行PostProcessorBeanFactory方法。在PropertyPlaceholderConfigurer類中實(shí)現(xiàn)了postProcessorBeanFactory方法复亏。在mergePorperties趾娃、convertProperties、processProperties這3個(gè)方法缔御,分別得到配置抬闷,將得打的配置轉(zhuǎn)換為合適的類型,最后將配置內(nèi)容告知BeanFactory耕突。
正是通過(guò)實(shí)現(xiàn)BeanFactoryPostProcessor接口笤成,BeanFactory會(huì)在實(shí)例化任何bean之前獲得配置信息,從而能夠正確解析bean描述文件中的變量引用眷茁。
下面我們自定義一個(gè)實(shí)現(xiàn)了BeanFactoryPostProcessor接口的類來(lái)體驗(yàn)一下整個(gè)過(guò)程啼器。我們通過(guò)實(shí)現(xiàn)BeanFactoryPostProcessor接口备蚓,來(lái)去除潛在的“流氓”屬性值,例如bean定義下留下bollocks這樣的字眼:
<bean id="bfpp" class="com.Spring.test.ObscenityRemovingBeanFactoryPostProcessor">
<property name="obscenties">
<set>
<value>bollocks</value>
<value>winky<value>
<value>bum<value>
<value>Microsoft<value>
</set>
</property>
</bean>
<bean id="simpleBean" class="com.Spring.test.SimplePostProcessor">
<property name="connectionString" value="bollocks" />
<property name="password" value="imaginecup" />
<property name="username" value="Microsoft" />
</bean>
public class ObscenityRemovingBeanFactoryPostProcessor implements BeanFactoryPostProcessor{
private Set<String> obscenties;
public ObscenityRemovingBeanFactoryPostProcessor(){
this.obscenties = new HashSet<String>();
}
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException{
String[] beanNames = beanFactory.getBeanDefinitionNames();
for(String beanName:beanNames){
BeanDefinition bd = beanFactory.getBeanDefinition(beanName);
StringValueResolver valueResolver = new StringValueResolver(){
public String resolveStringValue(String strVal){
if(isObscene(StrVal)) return "******";
return strVal;
}
}
BeanDefinitionVisitor visitor = new BeanDefinitionVisitor(valueResolver);
visitor.visitBeanDefinition(bd);
}
}
public boolean isObscene(Object value){
String potentialObscenity = value.toString().toUpperCase();
return this.obscenties.contains(potentialObscenity);
}
public void setObscenties(Set<String> obscenties){
this.obscenties.clear();
for(String obscenty : obscenties){
this.obscenties.add(obscenty.toUpperCase());
}
}
}
執(zhí)行類
public class PropertyConfigurerDemo{
public static void main(String[] args){
ConfigurableListableBeanFactory bf = new XmlBeanFactory(new
ClassPathResource("/META-INF/BeanFactory.xml"));
BeanFactoryPostProcessor bfpp = (BeanFactoryPostProcessor)bf.getBean("bfpp");
bfpp.postProcessBeanFactory(bf);
System.out.println(bf.getBean("simpleBean"));
}
}
輸出結(jié)果
SimplePostProcessor{connectionString=******,username=******,password=imaginecup}
通過(guò)ObscenityRemovingBeanFactoryPostProcessor,Spring很好的實(shí)現(xiàn)了屏蔽掉obscenties定義的不應(yīng)該展示的屬性鸡捐。
了解了BeanFactoryPostProcessor的用法后便可以深入研究BeanFactoryPostProcessor的調(diào)用過(guò)程了。
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
//調(diào)用 PostProcessorRegistrationDelegate 的 invokeBeanFactoryPostProcessors方法
if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
}
}
繼續(xù)調(diào)用PostProcessorRegistrationDelegate的invokeBeanFactoryPostProcessors方法吗伤,我們繼續(xù)跟蹤代碼:
public static void invokeBeanFactoryPostProcessors(
ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
// Invoke BeanDefinitionRegistryPostProcessors first, if any.
Set<String> processedBeans = new HashSet<>();
//對(duì) BeanDefinitionRegistry 類型的處理
if (beanFactory instanceof BeanDefinitionRegistry) {
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
List<BeanFactoryPostProcessor> regularPostProcessors = new LinkedList<>();
List<BeanDefinitionRegistryPostProcessor> registryProcessors = new LinkedList<>();
for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
BeanDefinitionRegistryPostProcessor registryProcessor =
(BeanDefinitionRegistryPostProcessor) postProcessor;
//對(duì)于 BeanDefinitionRegistryPostProcessor 類型珠移,在 BeanFactoryPostProcessor的
//基礎(chǔ)上還有自己定義的方法,需要先調(diào)用
registryProcessor.postProcessBeanDefinitionRegistry(registry);
registryProcessors.add(registryProcessor);
}
else {
//記錄常規(guī) BeanFactoryPostProcessor
regularPostProcessors.add(postProcessor);
}
}
// Do not initialize FactoryBeans here: We need to leave all regular beans
// uninitialized to let the bean factory post-processors apply to them!
// Separate between BeanDefinitionRegistryPostProcessors that implement
// PriorityOrdered, Ordered, and the rest.
List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();
//對(duì)于配置中讀取的 BeanDefinitionRegistryPostProcessor 的處理
//首先處理 PriorityOrdered 接口的 BeanDefinitionRegistryPostProcessor
String[] postProcessorNames =
beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
currentRegistryProcessors.clear();
//接著處理 Ordered 接口的 BeanDefinitionRegistryPostProcessor
postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
currentRegistryProcessors.clear();
//最后再處理之前未注冊(cè)的 BeanDefinitionRegistryPostProcessor
boolean reiterate = true;
while (reiterate) {
reiterate = false;
postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
if (!processedBeans.contains(ppName)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
reiterate = true;
}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
currentRegistryProcessors.clear();
}
//激活所有的 BeanFactoryPostProcessor
invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);
invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);
}
else {
// Invoke factory processors registered with the context instance.
invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory);
}
//對(duì)于配置中讀取的 BeanFactoryPostProcessor 的處理
String[] postProcessorNames =
beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);
// 對(duì)后處理器進(jìn)行分類
List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
List<String> orderedPostProcessorNames = new ArrayList<>();
List<String> nonOrderedPostProcessorNames = new ArrayList<>();
for (String ppName : postProcessorNames) {
if (processedBeans.contains(ppName)) {
// skip - already processed in first phase above
}
else if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class));
}
else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
orderedPostProcessorNames.add(ppName);
}
else {
nonOrderedPostProcessorNames.add(ppName);
}
}
//按照優(yōu)先級(jí)排序
sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);
//按照order排序
List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<>();
for (String postProcessorName : orderedPostProcessorNames) {
orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
}
sortPostProcessors(orderedPostProcessors, beanFactory);
invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory);
//直接調(diào)用
List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<>();
for (String postProcessorName : nonOrderedPostProcessorNames) {
nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
}
invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);
//清空緩存
beanFactory.clearMetadataCache();
}
從上面的方法我們可以看到塘砸,對(duì)于BeanFactoryPostProcessor的處理主要分兩種情況進(jìn)行的节仿,一個(gè)是對(duì)于BeanDefinitionRegistry類的特殊處理,另一種是對(duì)普通的BeanFactoryPostProcessor進(jìn)行處理掉蔬。而對(duì)于每種情況都需要考慮硬編碼注入注冊(cè)的后處理器以及通過(guò)配置注入的后處理器廊宪。
對(duì)于BeanDefinitionRegistry類型的處理類的處理主要包括以下內(nèi)容:
(1)對(duì)于硬編碼注冊(cè)的后處理器的處理,主要是通過(guò)AbstractApplicationContext中的添加處理器方法addBeanFactoryPost Processor進(jìn)行添加
public void addBeanFactoryPostProcessor(BeanFactoryPostProcessor postProcessor) {
Assert.notNull(postProcessor, "BeanFactoryPostProcessor must not be null");
this.beanFactoryPostProcessors.add(postProcessor);
}
添加后的后處理器會(huì)存放在beanFactoryPostProcessors里女轿,而在處理BeanFactoryPostProcessor的時(shí)候會(huì)首先檢測(cè)beanFactory PostProcessors中是否有數(shù)據(jù)箭启。當(dāng)然,BeanDefinitionRegistryPostProcessor繼承自BeanFactoryPostProcessor蛉迹,不但有后者的全部特性傅寡,同時(shí)還有自己的個(gè)性化方法,也需要在此調(diào)用北救。所以荐操,這里需要從beanFactoryPostProcessor中挑出BeanDefinition RegistryPostProcessor的后處理器,并進(jìn)行其postProcessBeanDefinitionRegistry方法的激活珍策。
(2)記錄后處理器主要使用了三個(gè)List完成
i.regularPostProcessors:記錄通過(guò)硬編碼方式注冊(cè)的BeanFactoryPostProcessor類型的處理器
ii.registryProcessors:記錄BeanDefinitionRegistryPostProcessor類型的處理器
iii.currentRegistryProcessors:記錄當(dāng)前已經(jīng)注冊(cè)過(guò)的
BeanDefinitionRegistryPostProcessor類型的處理器托启。
(3)分別對(duì)實(shí)現(xiàn)了PriorityOrdered和Ordered接口的BeanDefinitionRegistryPostProcessor類型的處理器進(jìn)行排序
(4)調(diào)用激活方法對(duì)處理器進(jìn)行激活
(5)普通的后處理器的流程與BeanDefinitionRegistryPostProcessor類型的處理器流程大致相同,這里就不再贅述了攘宙。
這里需要強(qiáng)調(diào)的是屯耸,對(duì)于硬編碼方式手動(dòng)添加的后處理器是不需要做任何排序的拐迁,但是在配置文件中讀取的處理器,Spring并不保證讀取的順序肩民。所以唠亚,為了保證用戶的調(diào)用順序的要求,Spring對(duì)于后處理器的調(diào)用支持按照PriorityOrdered和Ordered的順序調(diào)用持痰。
2.注冊(cè)BeanPostProcessor
上文中提到了Bean提到了BeanFactoryPostProcessor的調(diào)用灶搜,現(xiàn)在我們來(lái)探索下BeanPostProcessor,但是這里并不是調(diào)用工窍,而是注冊(cè)割卖。真正的調(diào)用其實(shí)是在bean的實(shí)例化階段進(jìn)行的。這是一個(gè)很重要的步驟患雏,也是很多功能BeanFactory不支持的重要原因鹏溯。Spring中大部分功能都是通過(guò)后處理器的方式進(jìn)行擴(kuò)展的,這是Spring框架的一個(gè)特性淹仑,但是在BeanFactory中其實(shí)并沒(méi)有實(shí)現(xiàn)后處理器的自動(dòng)注冊(cè)丙挽,所以在調(diào)用的時(shí)候如果沒(méi)有進(jìn)行手動(dòng)注冊(cè)其實(shí)是不能使用的。但是在ApplicationContext中卻添加了自動(dòng)注冊(cè)功能匀借,如自定義這樣一個(gè)后處理器:
public class MyInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor{
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException{
System.out.println("=====");
return null;
}
}
在配置文件中添加配置:
<bean class="processors.MyInstantiationAwareBeanPostProcessor" />
那么使用BeanFactory方式進(jìn)行Spring的bean的加載是是不會(huì)有任何改變的颜阐,但是使用ApplicationContext方式獲取bean的時(shí)候會(huì)在獲取每個(gè)bean時(shí)打印出“====”,而這個(gè)特性就是在registerBeanPostProcessor方法中完成的吓肋。
我們繼續(xù)探索registerBeanPostProcessor的方法實(shí)現(xiàn)凳怨。
protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {
PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory, this);
}
public static void registerBeanPostProcessors(
ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) {
String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);
// BeanPostProcessorChecker 是一個(gè)普通的信息打印,可能會(huì)有些情況:
// 當(dāng)Spring的配置中的的后處理器還沒(méi)有被注冊(cè)就已經(jīng)開(kāi)始了bean的初始化時(shí)是鬼,
// 便會(huì)打印出BeanPostProcessorChecker中設(shè)定的信息
int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length;
beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount));
// 分別為實(shí)現(xiàn)了PriorityOrdered和Ordered接口的BeanPostProcessor保證順序
List<BeanPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
List<BeanPostProcessor> internalPostProcessors = new ArrayList<>();
List<String> orderedPostProcessorNames = new ArrayList<>();
List<String> nonOrderedPostProcessorNames = new ArrayList<>();
for (String ppName : postProcessorNames) {
if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
priorityOrderedPostProcessors.add(pp);
if (pp instanceof MergedBeanDefinitionPostProcessor) {
internalPostProcessors.add(pp);
}
}
else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
orderedPostProcessorNames.add(ppName);
}
else {
nonOrderedPostProcessorNames.add(ppName);
}
}
// 首先排序并注冊(cè)實(shí)現(xiàn)了PriorityOrdered接口的BeanPostProcessor
sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors);
// 其次排序并注冊(cè)實(shí)現(xiàn)了Ordered接口的BeanPostProcessor
List<BeanPostProcessor> orderedPostProcessors = new ArrayList<>();
for (String ppName : orderedPostProcessorNames) {
BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
orderedPostProcessors.add(pp);
if (pp instanceof MergedBeanDefinitionPostProcessor) {
internalPostProcessors.add(pp);
}
}
sortPostProcessors(orderedPostProcessors, beanFactory);
registerBeanPostProcessors(beanFactory, orderedPostProcessors);
// 然后注冊(cè)正常的BeanPostProcessor
List<BeanPostProcessor> nonOrderedPostProcessors = new ArrayList<>();
for (String ppName : nonOrderedPostProcessorNames) {
BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
nonOrderedPostProcessors.add(pp);
if (pp instanceof MergedBeanDefinitionPostProcessor) {
internalPostProcessors.add(pp);
}
}
registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors);
// 最后排序并注冊(cè)所有的實(shí)現(xiàn)了MergedBeanDefinitionPostProcessor接口的BeanPostProcessor
// 這里并沒(méi)有重復(fù)注冊(cè)
sortPostProcessors(internalPostProcessors, beanFactory);
registerBeanPostProcessors(beanFactory, internalPostProcessors);
// 添加ApplicationListener探測(cè)器
beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext));
}
配合源碼以及注釋肤舞,在registerBeanPostProcessor方法中所做的邏輯相信大家都已經(jīng)很清楚了,我們?cè)僮鲆幌驴偨Y(jié)均蜜。
首先我們會(huì)發(fā)現(xiàn)李剖,對(duì)于BeanPostProcessor和BeanFactoryPostProcessor的處理極為相似,但是似乎又有一些不一樣的地方囤耳。經(jīng)過(guò)反復(fù)的對(duì)比發(fā)現(xiàn)杖爽,對(duì)于BeanFactoryPostProcessor的處理要區(qū)分兩種情況,一種方式是通過(guò)硬編碼方式的處理紫皇,另一種是通過(guò)配置文件方式的處理。那么為什么在BeanPostProcessor的處理中只考慮了配置文件的方式而不考慮硬編碼的方式呢腋寨?提出這個(gè)問(wèn)題聪铺,還是因?yàn)榇蠹覜](méi)有完全理解兩者實(shí)現(xiàn)的功能。對(duì)于BeanFactoryPostProcessor的處理萄窜,不但要實(shí)現(xiàn)注冊(cè)功能铃剔,而且還要實(shí)現(xiàn)對(duì)后處理器的激活操作撒桨,所以需要載入配置中的定義,并進(jìn)行激活:而對(duì)于BeanPostProcessor并不需要馬上調(diào)用键兜,再說(shuō)凤类,硬編碼的方式實(shí)現(xiàn)的功能是將后處理器提取并調(diào)用,這里并不需要調(diào)用當(dāng)然不需要硬編碼的方式了普气,這里的功能只需要將配置文件的BeanPostProcessor提取出來(lái)并注冊(cè)進(jìn)入beanFactory就可以了谜疤。
對(duì)于beanFactory的注冊(cè),也不是直接注冊(cè)就可以的现诀。在Spring中支持對(duì)于BeanPostProcessor的排序夷磕,比如根據(jù)PriorityOrdered進(jìn)行排序、根據(jù)Ordered進(jìn)行排序或者無(wú)序仔沿,而Spring在BeanPostProcessor的激活順序的時(shí)候也會(huì)考慮對(duì)于順序的問(wèn)題而先進(jìn)行排序坐桩。
這里有個(gè)地方讓人很迷惑,對(duì)于internalPostProcessor中存儲(chǔ)后處理器也就是MergedBeanDefinitionPostProcessor類型的處理器封锉,好像在代碼中重復(fù)注冊(cè)了绵跷,是這樣嗎?其實(shí)并不是的成福,我們可以看看對(duì)于registerBeanPostProcessor方法的實(shí)現(xiàn)方式碾局。
private static void registerBeanPostProcessors(
ConfigurableListableBeanFactory beanFactory, List<BeanPostProcessor> postProcessors) {
for (BeanPostProcessor postProcessor : postProcessors) {
beanFactory.addBeanPostProcessor(postProcessor);
}
}
public void addBeanPostProcessor(BeanPostProcessor beanPostProcessor) {
Assert.notNull(beanPostProcessor, "BeanPostProcessor must not be null");
this.beanPostProcessors.remove(beanPostProcessor);
this.beanPostProcessors.add(beanPostProcessor);
if (beanPostProcessor instanceof InstantiationAwareBeanPostProcessor) {
this.hasInstantiationAwareBeanPostProcessors = true;
}
if (beanPostProcessor instanceof DestructionAwareBeanPostProcessor) {
this.hasDestructionAwareBeanPostProcessors = true;
}
}
可以看到,在registerBeanPostProcessors方法的實(shí)現(xiàn)中其實(shí)已經(jīng)確保了BeanPostProcessor的唯一性闷叉,個(gè)人猜想擦俐,之所以選擇在registerBeanPostProcessor中沒(méi)有進(jìn)行重復(fù)移除操作或許是為了保持分類的效果,是邏輯更為清晰吧握侧。
3.初始化消息資源
在進(jìn)行這段函數(shù)的解析之前蚯瞧,我們先來(lái)回顧Spring國(guó)際化的使用方法。
假設(shè)我們正在開(kāi)發(fā)一個(gè)支持多國(guó)語(yǔ)言的Web應(yīng)用程序品擎,要求系統(tǒng)能夠根據(jù)客戶端的系統(tǒng)的語(yǔ)言類型返回對(duì)應(yīng)的界面:英文的操作系統(tǒng)返回英文界埋合,而中文的操作系統(tǒng)則返回中文界面——這是典型的i18n國(guó)際化問(wèn)題。對(duì)于有國(guó)際化要求的應(yīng)用系統(tǒng)萄传,我們不能簡(jiǎn)單的采用硬編碼的方式編寫(xiě)用戶界面信息甚颂、報(bào)錯(cuò)信息等內(nèi)容,而必須為這些需要國(guó)際化的信息進(jìn)行特殊處理秀菱。簡(jiǎn)單來(lái)說(shuō)振诬,就是為每種語(yǔ)言提供一種相應(yīng)的資源文件,并以規(guī)范化命名的方式保存在特定的目錄中衍菱,由系統(tǒng)自動(dòng)根據(jù)客戶端語(yǔ)言選擇合適的資源文件赶么。
“國(guó)際化信息”也稱為“本地化信息”,一般需要兩個(gè)條件才可以確定一個(gè)特定類型的本地化信息脊串,它們分別是“語(yǔ)言類型”和“國(guó)家/地區(qū)的類型”辫呻。如中文本地化信息既有中國(guó)大陸地區(qū)的中文清钥,又有中國(guó)臺(tái)灣地區(qū)。中國(guó)香港地區(qū)的中文放闺,還有新加坡地區(qū)的中文祟昭。Java通過(guò)java.util.Locale類表示一個(gè)本地化對(duì)象,它允許通過(guò)語(yǔ)言參數(shù)和國(guó)家/地區(qū)參數(shù)創(chuàng)建一個(gè)確定的本地化對(duì)象怖侦。
java.util.Locale是表示語(yǔ)言和國(guó)家/地區(qū)信息的本地化類篡悟,它是創(chuàng)建國(guó)際化應(yīng)用的基礎(chǔ)。下面給出幾個(gè)創(chuàng)建本地化對(duì)象的實(shí)例:
//帶有語(yǔ)言和國(guó)家/地區(qū)信息的本地化對(duì)象
Locale locale1 = new Locale("zh", "CN");
//只有語(yǔ)言信息的本地化對(duì)象
Locale locale2 = new Locale("zh");
//等同于locale1
Locale locale3 = Locale.CHINA;
//等同于locale2
Locale locale4 = Locale.CHINESE础钠;
//獲取本地系統(tǒng)默認(rèn)的本地化對(duì)象
Locale locale5 = Locale.getDefault();
JDK的java.util包提供了幾個(gè)支持本地化的格式化操作工具類:NumberFormat恰力、DateFormat、MessageFormat旗吁,而在Spring中的國(guó)際化資源操作也無(wú)非是對(duì)于這些類的封裝操作踩萎,我們僅僅介紹下MessageFormat的用法以幫助大家回顧
//信息格式化串
String pattern1 = "{0}, 你好!你與{1}在工商銀行存入{2}元很钓。";
String pattern2 = "At {1, time, short} On {1, date, long}, {0} paid {2, number, currency}.";
//用于動(dòng)態(tài)替換占位符的參數(shù)
Object[] params = {"John", new GregorianCalendar().getTime(), 1.0E3};
//使用默認(rèn)本地化對(duì)象格式化信息
String msg1 = MessageFormat.format(pattern1, params);
//使用指定的本地化對(duì)象格式化的信息
MessageFormat mf = new MessageFormat(pattern2, Locale.US);
String msg2 = mf.format(params);
System.out.println(msg1);
System.out.println(msg2);
Spring定義了訪問(wèn)國(guó)際化信息的MessageSource接口香府,并提供了幾個(gè)易用的實(shí)現(xiàn)類。MessageSource分別被HierarchicalMessage Source和ApplicationContext接口擴(kuò)展码倦,這里我們主要看一下HierarchicalMessageSource接口的幾個(gè)實(shí)現(xiàn)類:
HierarchicalMessageSource接口最重要的兩個(gè)實(shí)現(xiàn)類是ResourceBundleMessageSource和ReloadableResourceBundleMessage Source企孩。它們基于Java的ResourceBundle基礎(chǔ)類實(shí)現(xiàn),允許僅通過(guò)資源名加載國(guó)際化資源袁稽。ReloadableResourceBundleMessage Source提供了定時(shí)刷新功能勿璃,允許在不重啟系統(tǒng)的情況下,更新資源信息推汽。StaticMessageSource主要用于程序測(cè)試补疑,它允許通過(guò)編程的方式提供國(guó)家化信息。而DelegatingMessageSource是為方便操作父MessageSource而提供的代理類歹撒。僅僅舉例ResourceBundleMessageSource的實(shí)現(xiàn)方式:
(1)定義資源文件
messages.properties(默認(rèn):英文)
test=test
messages_zh_CN.properties(簡(jiǎn)體中文)
test=測(cè)試
(2)定義配置文件
<bean id="messageSource" class="org.Springframework.context.support.ResourceBundleMessageSource">
<property name="basename">
<list>
<value>test/messages</value>
</list>
</property>
</bean>
其中莲组,這個(gè)Bean的ID必須命名為messageSource,否則會(huì)拋出NoSuchMessageException異常
(3)使用暖夭。通過(guò)ApplicationContext訪問(wèn)國(guó)際化信息锹杈。
String[] configs = {"applicationContext.xml"};
ApplicationContext ctx = new ClassPathXmlApplicationContext(configs);
//直接通過(guò)容器訪問(wèn)國(guó)際化信息
Object[] params = {"John", new GregorianCalendar.getTime()};
String str1 = ctx.getMessage("test", params, Locale.US);
String str2 = ctx.getMessage("test", params, Locale.CHINA);
System.out.println(str1);
System.out.println(str2);
了解了Spring國(guó)際化的使用后便可以進(jìn)行源碼的分析了。
在initMessageSource中的方法主要功能是提取配置中定義的messageSource迈着,并將其記錄在Spring的容器中竭望,也就是AbstractApplicationContext中,當(dāng)然裕菠,如果用戶未設(shè)置資源文件的話咬清,Spring中也提供了默認(rèn)的配置DelegatingMessageSource。
在initMessageSource中獲取自定義資源文件的方式為beanFactory.getBean(MESSAGE_SOURCE_BEAN_NAME, Message Source.class),在這里Spring使用了硬編碼的方式硬性規(guī)定了自定義資源的文件的ID必須為messageSource枫振,否則便會(huì)獲取不到自定義資源配置,這也就是為什么前面提到的Bean的id如果不為messageSource會(huì)拋出異常萤彩。下面來(lái)看一下代碼:
protected void initMessageSource() {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (beanFactory.containsLocalBean(MESSAGE_SOURCE_BEAN_NAME)) {
//如果在配置中已經(jīng)配置了messageSource粪滤,那么將messageSource提取并記錄在this.messageSource中
this.messageSource = beanFactory.getBean(MESSAGE_SOURCE_BEAN_NAME, MessageSource.class);
// Make MessageSource aware of parent MessageSource.
if (this.parent != null && this.messageSource instanceof HierarchicalMessageSource) {
HierarchicalMessageSource hms = (HierarchicalMessageSource) this.messageSource;
if (hms.getParentMessageSource() == null) {
// Only set parent context as parent MessageSource if no parent MessageSource
// registered already.
hms.setParentMessageSource(getInternalParentMessageSource());
}
}
if (logger.isDebugEnabled()) {
logger.debug("Using MessageSource [" + this.messageSource + "]");
}
}
else {
// 如果用戶沒(méi)有定義配置文件,那么使用臨時(shí)的DelegatingMessageSource以便于調(diào)用getMessage方法的返回
DelegatingMessageSource dms = new DelegatingMessageSource();
dms.setParentMessageSource(getInternalParentMessageSource());
this.messageSource = dms;
beanFactory.registerSingleton(MESSAGE_SOURCE_BEAN_NAME, this.messageSource);
if (logger.isDebugEnabled()) {
logger.debug("Unable to locate MessageSource with name '" + MESSAGE_SOURCE_BEAN_NAME +
"': using default [" + this.messageSource + "]");
}
}
}
通過(guò)讀取并將自定義資源文件配置記錄在容器中雀扶,那么就可以在獲取資源文件的時(shí)候直接使用了杖小,例如在AbstractApplicationContext中的獲取資源文件屬性的方法:
public String getMessage(String code, @Nullable Object[] args, Locale locale) throws NoSuchMessageException {
return getMessageSource().getMessage(code, args, locale);
}
其中的getMessageSource()方法正是獲取了之前定義的自定義資源配置
4.初始化ApplicationEventMulticaster
在講解Spring的時(shí)間傳播器之前,我們還是先來(lái)看一下Spring的時(shí)間監(jiān)聽(tīng)的簡(jiǎn)單用法愚墓。
(1)定義監(jiān)聽(tīng)時(shí)間
public class TestEvent extends ApplicationEvent{
public String msg;
public TestEvent(Object source){
super(source);
}
public TestEvent(Object source, String msg){
super(source);
this.msg = msg;
}
public void print(){
System.out.println(msg);
}
}
(2)定義監(jiān)聽(tīng)器
public class TestListener implements ApplicationListener{
public void onApplicationEvent(ApplicationEvent event){
if(event instancof TestEvent){
TestEvent testEvent = (TestEvent)event;
testEvent.print();
}
}
}
(3)添加配置文件
<bean id="testListener" class="com.test.event.TestListener" />
(4)測(cè)試
public class Test{
public static void main(String[] args){
ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
TestEvent event = new TestEvent("hello", "msg");
ctx.publishEvent(event);
}
}
當(dāng)程序運(yùn)行時(shí)予权,Spring會(huì)將發(fā)出的TestEvent事件轉(zhuǎn)給我們自定義的TestListener進(jìn)行進(jìn)一步處理。
或許很多人一下子會(huì)反應(yīng)這是設(shè)計(jì)模式中的觀察者模式浪册,這確實(shí)是個(gè)典型的應(yīng)用扫腺,可以在比較關(guān)心的事件結(jié)束后及時(shí)處理。那么我們看看ApplicationEventMulticaster是如何被初始化的村象,以確保功能的正確運(yùn)行笆环。
initApplicationEventMulticaster的方式比較簡(jiǎn)單,無(wú)非考慮兩種情況厚者。
i.如果用戶自定義了事件廣播器躁劣,那么使用用戶自定義的事件廣播器
ii.如果用戶沒(méi)有自定義事件廣播器,那么使用默認(rèn)的ApplicationEventMulticaster库菲。
protected void initApplicationEventMulticaster() {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
this.applicationEventMulticaster =
beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
if (logger.isDebugEnabled()) {
logger.debug("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
}
}
else {
this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
if (logger.isDebugEnabled()) {
logger.debug("Unable to locate ApplicationEventMulticaster with name '" +
APPLICATION_EVENT_MULTICASTER_BEAN_NAME +
"': using default [" + this.applicationEventMulticaster + "]");
}
}
}
按照之前介紹的順序及邏輯账忘,我們推斷,作為廣播器熙宇,一定是用于存放監(jiān)聽(tīng)器并在合適的時(shí)候調(diào)用監(jiān)聽(tīng)器鳖擒,那么我們不妨進(jìn)入默認(rèn)的廣播器實(shí)現(xiàn)SimpleApplicationEventMulticaster來(lái)一探究竟。
其中一段代碼使我們感興趣的奇颠。
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
Executor executor = getTaskExecutor();
if (executor != null) {
executor.execute(() -> invokeListener(listener, event));
}
else {
invokeListener(listener, event);
}
}
}
可以推斷败去,當(dāng)產(chǎn)生Spring事件的時(shí)候會(huì)默認(rèn)使用SimpleApplicationEventMulticaster的multicasterEvent來(lái)廣播事件,遍歷所有監(jiān)聽(tīng)器烈拒,并使用監(jiān)聽(tīng)器中的onApplicationEvent方法來(lái)進(jìn)行監(jiān)聽(tīng)器的處理圆裕。而對(duì)于每個(gè)監(jiān)聽(tīng)器來(lái)說(shuō)其實(shí)都可以獲取到產(chǎn)生的時(shí)間,但是是否進(jìn)行處理則由事件監(jiān)聽(tīng)器決定荆几。
5.注冊(cè)監(jiān)聽(tīng)器
之前介紹Spring的廣播器時(shí)反復(fù)提到了事件監(jiān)聽(tīng)器吓妆,那么在Spring注冊(cè)監(jiān)聽(tīng)器的時(shí)候又做了哪些邏輯操作呢?
protected void registerListeners() {
// 硬編碼注冊(cè)監(jiān)聽(tīng)器
for (ApplicationListener<?> listener : getApplicationListeners()) {
getApplicationEventMulticaster().addApplicationListener(listener);
}
// 配置文件注冊(cè)監(jiān)聽(tīng)器
String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
for (String listenerBeanName : listenerBeanNames) {
getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
}
// Publish early application events now that we finally have a multicaster...
Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
this.earlyApplicationEvents = null;
if (earlyEventsToProcess != null) {
for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
getApplicationEventMulticaster().multicastEvent(earlyEvent);
}
}
}
初始化非延遲加載單例
完成BeanFactory的初始化工作吨铸,其中包括ConversionService的設(shè)置行拢、配置凍結(jié)以及非延遲加載的bean的初始化工作。
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
// Initialize conversion service for this context.
if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
beanFactory.setConversionService(
beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
}
// Register a default embedded value resolver if no bean post-processor
// (such as a PropertyPlaceholderConfigurer bean) registered any before:
// at this point, primarily for resolution in annotation attribute values.
if (!beanFactory.hasEmbeddedValueResolver()) {
beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
}
// Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
for (String weaverAwareName : weaverAwareNames) {
getBean(weaverAwareName);
}
// Stop using the temporary ClassLoader for type matching.
beanFactory.setTempClassLoader(null);
// 凍結(jié)所有的bean定義诞吱,說(shuō)明注冊(cè)的bean定義將不被修改或任何進(jìn)一步的處理
beanFactory.freezeConfiguration();
// 初始化剩下的單實(shí)例(非惰性的)
beanFactory.preInstantiateSingletons();
}
首先我們來(lái)了解一下ConversionService類所提供的作用舟奠。
1.ConversionService的設(shè)置
之前我們提到過(guò)使用自定義類型轉(zhuǎn)換器從String轉(zhuǎn)換為Date的方式竭缝,那么,在Spring中還提供了另一種轉(zhuǎn)換方式:使用Converter沼瘫。同樣抬纸,我們使用一個(gè)簡(jiǎn)單的實(shí)例來(lái)了解下Converter的使用方式。
(1)定義轉(zhuǎn)換器
public class String2DateConverter implements Converter<String, Date>{
@Override
public Date convert(String arg0){
try{
return DateUtil.parseDate(arg0,
new String[]("yyyy-MM-dd HH:mm:ss"));
}catch(ParseException e){
return null;
}
}
}
(2)注冊(cè)
<bean id="conversionService" class="org.Springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<list>
<bean class="String2DateConverter" />
</list>
</property>
</bean>
(3)測(cè)試
這樣便可以使用Converter為我們提供的功能了耿戚,下面我們通過(guò)一個(gè)簡(jiǎn)便的方法來(lái)對(duì)此直接測(cè)試湿故。
public void testString2DateConvert(){
DefaultConversionService conversionService = new DefaultConversionService();
conversionService.addConverter(new String2DateConverter());
String dateStr = "2018-07-04 10:00:00";
Date date = conversionService.convert(dateStr, Date.class);
}
通過(guò)以上的功能我們看到了Converter以及ConversionService提供的便利功能,其中的配置就是在當(dāng)前函數(shù)中被初始化的膜蛔。
2.凍結(jié)配置
凍結(jié)所有的bean定義坛猪,說(shuō)明注冊(cè)的bean定義將不被修改或進(jìn)行任何進(jìn)一步的處理。
public void freezeConfiguration() {
this.configurationFrozen = true;
this.frozenBeanDefinitionNames = StringUtils.toStringArray(this.beanDefinitionNames);
}
3.初始化非延遲加載
ApplicationContext實(shí)現(xiàn)的默認(rèn)行為就是在啟動(dòng)時(shí)將所有單例bean提前進(jìn)行實(shí)例化皂股。提前實(shí)例意味著作初始化過(guò)程的一部分墅茉,ApplicationContext實(shí)例會(huì)創(chuàng)建并配置所有的單例bean。通常情況下這是一件好事屑墨,因?yàn)檫@樣在配置中的任何錯(cuò)誤就會(huì)即刻發(fā)現(xiàn)(否則的話可能要花幾個(gè)小時(shí)甚至幾天)躁锁。而這個(gè)實(shí)例化的過(guò)程就是在finishBeanFactoryInitialization中完成的。
public void preInstantiateSingletons() throws BeansException {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Pre-instantiating singletons in " + this);
}
// Iterate over a copy to allow for init methods which in turn register new bean definitions.
// While this may not be part of the regular factory bootstrap, it does otherwise work fine.
List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
// Trigger initialization of all non-lazy singleton beans...
for (String beanName : beanNames) {
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
if (isFactoryBean(beanName)) {
Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
if (bean instanceof FactoryBean) {
final FactoryBean<?> factory = (FactoryBean<?>) bean;
boolean isEagerInit;
if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>)
((SmartFactoryBean<?>) factory)::isEagerInit,
getAccessControlContext());
}
else {
isEagerInit = (factory instanceof SmartFactoryBean &&
((SmartFactoryBean<?>) factory).isEagerInit());
}
if (isEagerInit) {
getBean(beanName);
}
}
}
else {
getBean(beanName);
}
}
}
// Trigger post-initialization callback for all applicable beans...
for (String beanName : beanNames) {
Object singletonInstance = getSingleton(beanName);
if (singletonInstance instanceof SmartInitializingSingleton) {
final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
smartSingleton.afterSingletonsInstantiated();
return null;
}, getAccessControlContext());
}
else {
smartSingleton.afterSingletonsInstantiated();
}
}
}
}
finishRefresh
在Spring中還提供了Lifecycle接口卵史,Lifecycle中包含了start/stop方法战转,實(shí)現(xiàn)此接口后Spring會(huì)保證在啟動(dòng)的時(shí)候調(diào)用其start方法開(kāi)始生命周期,并在Spring關(guān)閉的時(shí)候調(diào)用stop方法來(lái)結(jié)束生命周期以躯,通常用來(lái)配置后臺(tái)程序槐秧,在啟動(dòng)后一直運(yùn)行(如對(duì)MQ進(jìn)行輪詢等)。而ApplicationContext的初始化最后正是保證了這一功能的實(shí)現(xiàn)忧设。
protected void finishRefresh() {
// Clear context-level resource caches (such as ASM metadata from scanning).
clearResourceCaches();
// Initialize lifecycle processor for this context.
initLifecycleProcessor();
// Propagate refresh to lifecycle processor first.
getLifecycleProcessor().onRefresh();
// Publish the final event.
publishEvent(new ContextRefreshedEvent(this));
// Participate in LiveBeansView MBean, if active.
LiveBeansView.registerApplicationContext(this);
}
1.initLifecycleProcess
當(dāng)ApplicationContext啟動(dòng)或停止時(shí)刁标,它會(huì)通過(guò)LifecycleProcessor來(lái)與所有聲明的bean的周期做狀態(tài)更新,而在LifecycleProcessor的使用前首先需要初始化址晕。
protected void initLifecycleProcessor() {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (beanFactory.containsLocalBean(LIFECYCLE_PROCESSOR_BEAN_NAME)) {
this.lifecycleProcessor =
beanFactory.getBean(LIFECYCLE_PROCESSOR_BEAN_NAME, LifecycleProcessor.class);
if (logger.isDebugEnabled()) {
logger.debug("Using LifecycleProcessor [" + this.lifecycleProcessor + "]");
}
}
else {
DefaultLifecycleProcessor defaultProcessor = new DefaultLifecycleProcessor();
defaultProcessor.setBeanFactory(beanFactory);
this.lifecycleProcessor = defaultProcessor;
beanFactory.registerSingleton(LIFECYCLE_PROCESSOR_BEAN_NAME, this.lifecycleProcessor);
if (logger.isDebugEnabled()) {
logger.debug("Unable to locate LifecycleProcessor with name '" +
LIFECYCLE_PROCESSOR_BEAN_NAME +
"': using default [" + this.lifecycleProcessor + "]");
}
}
}
2.onRefresh
啟動(dòng)所有實(shí)現(xiàn)了Lifecycle接口的bean膀懈。
public void onRefresh() {
startBeans(true);
this.running = true;
}
private void startBeans(boolean autoStartupOnly) {
Map<String, Lifecycle> lifecycleBeans = getLifecycleBeans();
Map<Integer, LifecycleGroup> phases = new HashMap<>();
lifecycleBeans.forEach((beanName, bean) -> {
if (!autoStartupOnly || (bean instanceof SmartLifecycle && ((SmartLifecycle) bean).isAutoStartup())) {
int phase = getPhase(bean);
LifecycleGroup group = phases.get(phase);
if (group == null) {
group = new LifecycleGroup(phase, this.timeoutPerShutdownPhase, lifecycleBeans, autoStartupOnly);
phases.put(phase, group);
}
group.add(beanName, bean);
}
});
if (!phases.isEmpty()) {
List<Integer> keys = new ArrayList<>(phases.keySet());
Collections.sort(keys);
for (Integer key : keys) {
phases.get(key).start();
}
}
}
3.publishEvent
當(dāng)完成ApplicationContext初始化的時(shí)候,要通過(guò)Spring中的時(shí)間發(fā)布機(jī)制來(lái)發(fā)出ContextRefreshEvent事件谨垃,以保證對(duì)應(yīng)的監(jiān)聽(tīng)器可以做進(jìn)一步的邏輯處理启搂。
protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
Assert.notNull(event, "Event must not be null");
if (logger.isTraceEnabled()) {
logger.trace("Publishing event in " + getDisplayName() + ": " + event);
}
// Decorate event as an ApplicationEvent if necessary
ApplicationEvent applicationEvent;
if (event instanceof ApplicationEvent) {
applicationEvent = (ApplicationEvent) event;
}
else {
applicationEvent = new PayloadApplicationEvent<>(this, event);
if (eventType == null) {
eventType = ((PayloadApplicationEvent) applicationEvent).getResolvableType();
}
}
// Multicast right now if possible - or lazily once the multicaster is initialized
if (this.earlyApplicationEvents != null) {
this.earlyApplicationEvents.add(applicationEvent);
}
else {
getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
}
// Publish event via parent context as well...
if (this.parent != null) {
if (this.parent instanceof AbstractApplicationContext) {
((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
}
else {
this.parent.publishEvent(event);
}
}
}
至此,ApplicationContext相比于BeanFactory擴(kuò)展的功能以及相關(guān)代碼我們就了解完了刘陶,下一篇文章我們將會(huì)看一看Spring中另一個(gè)重要的思想AOP(面向切面編程)是如何實(shí)現(xiàn)的胳赌。