日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

【Spring源码分析】Bean加载流程概览

發(fā)布時(shí)間:2024/7/5 javascript 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【Spring源码分析】Bean加载流程概览 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

代碼入口

之前寫文章都會(huì)啰啰嗦嗦一大堆再開始,進(jìn)入【Spring源碼分析】這個(gè)板塊就直接切入正題了。

很多朋友可能想看Spring源碼,但是不知道應(yīng)當(dāng)如何入手去看,這個(gè)可以理解:Java開發(fā)者通常從事的都是Java Web的工作,對(duì)于程序員來說,一個(gè)Web項(xiàng)目用到Spring,只是配置一下配置文件而已,Spring的加載過程相對(duì)是不太透明的,不太好去找加載的代碼入口。

下面有很簡單的一段代碼可以作為Spring代碼加載的入口:

1 ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml");2 ac.getBean(XXX.class);

ClassPathXmlApplicationContext用于加載CLASSPATH下的Spring配置文件,可以看到,第二行就已經(jīng)可以獲取到Bean的實(shí)例了,那么必然第一行就已經(jīng)完成了對(duì)所有Bean實(shí)例的加載,因此可以通過ClassPathXmlApplicationContext作為入口。為了后面便于代碼閱讀,先給出一下ClassPathXmlApplicationContext這個(gè)類的繼承關(guān)系:

大致的繼承關(guān)系是如上圖所示的,由于版面的關(guān)系,沒有繼續(xù)畫下去了,左下角的ApplicationContext應(yīng)當(dāng)還有一層繼承關(guān)系,比較關(guān)鍵的一點(diǎn)是它是BeanFactory的子接口。

最后聲明一下,本文使用的Spring版本為3.0.7,比較老,使用這個(gè)版本純粹是因?yàn)楣臼褂枚选?/p>

?

ClassPathXmlApplicationContext存儲(chǔ)內(nèi)容

為了更理解ApplicationContext,拿一個(gè)實(shí)例ClassPathXmlApplicationContext舉例,看一下里面存儲(chǔ)的內(nèi)容,加深對(duì)ApplicationContext的認(rèn)識(shí),以表格形式展現(xiàn):

對(duì)象名類 ?型作 ?用歸屬類
configResourcesResource[]配置文件資源對(duì)象數(shù)組ClassPathXmlApplicationContext
configLocationsString[]配置文件字符串?dāng)?shù)組,存儲(chǔ)配置文件路徑AbstractRefreshableConfigApplicationContext
beanFactoryDefaultListableBeanFactory上下文使用的Bean工廠AbstractRefreshableApplicationContext
beanFactoryMonitorObjectBean工廠使用的同步監(jiān)視器AbstractRefreshableApplicationContext
idString上下文使用的唯一Id,標(biāo)識(shí)此ApplicationContextAbstractApplicationContext
parentApplicationContext父級(jí)ApplicationContextAbstractApplicationContext
beanFactoryPostProcessorsList<BeanFactoryPostProcessor>存儲(chǔ)BeanFactoryPostProcessor接口,Spring提供的一個(gè)擴(kuò)展點(diǎn)AbstractApplicationContext
startupShutdownMonitorObjectrefresh方法和destory方法公用的一個(gè)監(jiān)視器,避免兩個(gè)方法同時(shí)執(zhí)行AbstractApplicationContext
shutdownHookThreadSpring提供的一個(gè)鉤子,JVM停止執(zhí)行時(shí)會(huì)運(yùn)行Thread里面的方法AbstractApplicationContext
resourcePatternResolverResourcePatternResolver上下文使用的資源格式解析器AbstractApplicationContext
lifecycleProcessorLifecycleProcessor用于管理Bean生命周期的生命周期處理器接口AbstractApplicationContext
messageSourceMessageSource用于實(shí)現(xiàn)國際化的一個(gè)接口AbstractApplicationContext
applicationEventMulticasterApplicationEventMulticasterSpring提供的事件管理機(jī)制中的事件多播器接口AbstractApplicationContext
applicationListenersSet<ApplicationListener>Spring提供的事件管理機(jī)制中的應(yīng)用監(jiān)聽器AbstractApplicationContext

?

ClassPathXmlApplicationContext構(gòu)造函數(shù)

看下ClassPathXmlApplicationContext的構(gòu)造函數(shù):

1 public ClassPathXmlApplicationContext(String configLocation) throws BeansException {2 this(new String[] {configLocation}, true, null);3 } 1 public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) 2 throws BeansException { 3 4 super(parent); 5 setConfigLocations(configLocations); 6 if (refresh) { 7 refresh(); 8 } 9 }

從第二段代碼看,總共就做了三件事:

  1、super(parent)

    沒什么太大的作用,設(shè)置一下父級(jí)ApplicationContext,這里是null

  2、setConfigLocations(configLocations)

    代碼就不貼了,一看就知道,里面做了兩件事情:

    (1)將指定的Spring配置文件的路徑存儲(chǔ)到本地

    (2)解析Spring配置文件路徑中的${PlaceHolder}占位符,替換為系統(tǒng)變量中PlaceHolder對(duì)應(yīng)的Value值,System本身就自帶一些系統(tǒng)變量比如class.path、os.name、user.dir等,也可以通過System.setProperty()方法設(shè)置自己需要的系統(tǒng)變量

  3、refresh()

    這個(gè)就是整個(gè)Spring Bean加載的核心了,它是ClassPathXmlApplicationContext的父類AbstractApplicationContext的一個(gè)方法,顧名思義,用于刷新整個(gè)Spring上下文信息,定義了整個(gè)Spring上下文加載的流程。

?

refresh方法

上面已經(jīng)說了,refresh()方法是整個(gè)Spring Bean加載的核心,因此看一下整個(gè)refresh()方法的定義:

1 public void refresh() throws BeansException, IllegalStateException {2 synchronized (this.startupShutdownMonitor) {3 // Prepare this context for refreshing.4 prepareRefresh();5 6 // Tell the subclass to refresh the internal bean factory.7 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();8 9 // Prepare the bean factory for use in this context. 10 prepareBeanFactory(beanFactory); 11 12 try { 13 // Allows post-processing of the bean factory in context subclasses. 14 postProcessBeanFactory(beanFactory); 15 16 // Invoke factory processors registered as beans in the context. 17 invokeBeanFactoryPostProcessors(beanFactory); 18 19 // Register bean processors that intercept bean creation. 20 registerBeanPostProcessors(beanFactory); 21 22 // Initialize message source for this context. 23 initMessageSource(); 24 25 // Initialize event multicaster for this context. 26 initApplicationEventMulticaster(); 27 28 // Initialize other special beans in specific context subclasses. 29 onRefresh(); 30 31 // Check for listener beans and register them. 32 registerListeners(); 33 34 // Instantiate all remaining (non-lazy-init) singletons. 35 finishBeanFactoryInitialization(beanFactory); 36 37 // Last step: publish corresponding event. 38 finishRefresh(); 39 } 40 41 catch (BeansException ex) { 42 // Destroy already created singletons to avoid dangling resources. 43 destroyBeans(); 44 45 // Reset 'active' flag. 46 cancelRefresh(ex); 47 48 // Propagate exception to caller. 49 throw ex; 50 } 51 } 52 }

每個(gè)子方法的功能之后一點(diǎn)一點(diǎn)再分析,首先refresh()方法有幾點(diǎn)是值得我們學(xué)習(xí)的:

  1、方法是加鎖的,這么做的原因是避免多線程同時(shí)刷新Spring上下文

  2、盡管加鎖可以看到是針對(duì)整個(gè)方法體的,但是沒有在方法前加synchronized關(guān)鍵字,而使用了對(duì)象鎖startUpShutdownMonitor,這樣做有兩個(gè)好處:

    (1)refresh()方法和close()方法都使用了startUpShutdownMonitor對(duì)象鎖加鎖,這就保證了在調(diào)用refresh()方法的時(shí)候無法調(diào)用close()方法,反之亦然,避免了沖突

    (2)另外一個(gè)好處不在這個(gè)方法中體現(xiàn),但是提一下,使用對(duì)象鎖可以減小了同步的范圍,只對(duì)不能并發(fā)的代碼塊進(jìn)行加鎖,提高了整體代碼運(yùn)行的效率

  3、方法里面使用了每個(gè)子方法定義了整個(gè)refresh()方法的流程,使得整個(gè)方法流程清晰易懂。這點(diǎn)是非常值得學(xué)習(xí)的,一個(gè)方法里面幾十行甚至上百行代碼寫在一起,在我看來會(huì)有三個(gè)顯著的問題:

    (1)擴(kuò)展性降低。反過來講,假使把流程定義為方法,子類可以繼承父類,可以根據(jù)需要重寫方法

    (2)代碼可讀性差。很簡單的道理,看代碼的人是愿意看一段500行的代碼,還是愿意看10段50行的代碼?

    (3)代碼可維護(hù)性差。這點(diǎn)和上面的類似但又有不同,可維護(hù)性差的意思是,一段幾百行的代碼,功能點(diǎn)不明確,不易后人修改,可能會(huì)導(dǎo)致“牽一發(fā)而動(dòng)全身”

?

prepareRefresh方法

下面挨個(gè)看refresh方法中的子方法,首先是prepareRefresh方法,看一下源碼:

1 /**2 * Prepare this context for refreshing, setting its startup date and3 * active flag.4 */5 protected void prepareRefresh() {6 this.startupDate = System.currentTimeMillis();7 synchronized (this.activeMonitor) {8 this.active = true;9 } 10 11 if (logger.isInfoEnabled()) { 12 logger.info("Refreshing " + this); 13 } 14 }

這個(gè)方法功能比較簡單,顧名思義,準(zhǔn)備刷新Spring上下文,其功能注釋上寫了:

1、設(shè)置一下刷新Spring上下文的開始時(shí)間

2、將active標(biāo)識(shí)位設(shè)置為true

另外可以注意一下12行這句日志,這句日志打印了真正加載Spring上下文的Java類。

?

obtainFreshBeanFactory方法

obtainFreshBeanFactory方法的作用是獲取刷新Spring上下文的Bean工廠,其代碼實(shí)現(xiàn)為:

1 protected ConfigurableListableBeanFactory obtainFreshBeanFactory() { 2 refreshBeanFactory(); 3 ConfigurableListableBeanFactory beanFactory = getBeanFactory(); 4 if (logger.isDebugEnabled()) { 5 logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory); 6 } 7 return beanFactory; 8 }

其核心是第二行的refreshBeanFactory方法,這是一個(gè)抽象方法,有AbstractRefreshableApplicationContext和GenericApplicationContext這兩個(gè)子類實(shí)現(xiàn)了這個(gè)方法,看一下上面ClassPathXmlApplicationContext的繼承關(guān)系圖即知,調(diào)用的應(yīng)當(dāng)是AbstractRefreshableApplicationContext中實(shí)現(xiàn)的refreshBeanFactory,其源碼為:

1 protected final void refreshBeanFactory() throws BeansException {2 if (hasBeanFactory()) {3 destroyBeans();4 closeBeanFactory();5 }6 try {7 DefaultListableBeanFactory beanFactory = createBeanFactory();8 beanFactory.setSerializationId(getId());9 customizeBeanFactory(beanFactory); 10 loadBeanDefinitions(beanFactory); 11 synchronized (this.beanFactoryMonitor) { 12 this.beanFactory = beanFactory; 13 } 14 } 15 catch (IOException ex) { 16 throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex); 17 } 18 }

這段代碼的核心是第7行,這行點(diǎn)出了DefaultListableBeanFactory這個(gè)類,這個(gè)類是構(gòu)造Bean的核心類,這個(gè)類的功能會(huì)在下一篇文章中詳細(xì)解讀,首先給出DefaultListableBeanFactory的繼承關(guān)系圖:

AbstractAutowireCapableBeanFactory這個(gè)類的繼承層次比較深,版面有限,就沒有繼續(xù)畫下去了,本圖基本上清楚地展示了DefaultListableBeanFactory的層次結(jié)構(gòu)。

為了更清晰地說明DefaultListableBeanFactory的作用,列舉一下DefaultListableBeanFactory中存儲(chǔ)的一些重要對(duì)象及對(duì)象中的內(nèi)容,DefaultListableBeanFactory基本就是操作這些對(duì)象,以表格形式說明:

?對(duì)象名類 ?型?作 ? ?用歸屬類
?aliasMapMap<String, String>存儲(chǔ)Bean名稱->Bean別名映射關(guān)系??SimpleAliasRegistry
singletonObjects?Map<String, Object>?存儲(chǔ)單例Bean名稱->單例Bean實(shí)現(xiàn)映射關(guān)系DefaultSingletonBeanRegistry?
?singletonFactories?Map<String, ObjectFactory>存儲(chǔ)Bean名稱->ObjectFactory實(shí)現(xiàn)映射關(guān)系?DefaultSingletonBeanRegistry?
earlySingletonObjects??Map<String, Object>存儲(chǔ)Bean名稱->預(yù)加載Bean實(shí)現(xiàn)映射關(guān)系???DefaultSingletonBeanRegistry?
registeredSingletons?Set<String>?存儲(chǔ)注冊(cè)過的Bean名?DefaultSingletonBeanRegistry?
singletonsCurrentlyInCreation?Set<String>存儲(chǔ)當(dāng)前正在創(chuàng)建的Bean名?? DefaultSingletonBeanRegistry??
?disposableBeans?Map<String, Object>

存儲(chǔ)Bean名稱->Disposable接口實(shí)現(xiàn)Bean實(shí)現(xiàn)映射關(guān)系 ?

? ?DefaultSingletonBeanRegistry???
?factoryBeanObjectCache?Map<String, Object>存儲(chǔ)Bean名稱->FactoryBean接口Bean實(shí)現(xiàn)映射關(guān)系FactoryBeanRegistrySupport?
propertyEditorRegistrars??Set<PropertyEditorRegistrar>存儲(chǔ)PropertyEditorRegistrar接口實(shí)現(xiàn)集合AbstractBeanFactory?
?embeddedValueResolversList<StringValueResolver>?存儲(chǔ)StringValueResolver(字符串解析器)接口實(shí)現(xiàn)列表AbstractBeanFactory?
beanPostProcessors?List<BeanPostProcessor>?存儲(chǔ)?BeanPostProcessor接口實(shí)現(xiàn)列表AbstractBeanFactory
mergedBeanDefinitions?Map<String, RootBeanDefinition>?存儲(chǔ)Bean名稱->合并過的根Bean定義映射關(guān)系?AbstractBeanFactory?
?alreadyCreatedSet<String>?存儲(chǔ)至少被創(chuàng)建過一次的Bean名集合??AbstractBeanFactory??
ignoredDependencyInterfaces?Set<Class>?存儲(chǔ)不自動(dòng)裝配的接口Class對(duì)象集合?AbstractAutowireCapableBeanFactory?
?resolvableDependenciesMap<Class, Object>?存儲(chǔ)修正過的依賴映射關(guān)系?DefaultListableBeanFactory?
beanDefinitionMap?Map<String, BeanDefinition>?存儲(chǔ)Bean名稱-->Bean定義映射關(guān)系?DefaultListableBeanFactory??
beanDefinitionNamesList<String>存儲(chǔ)Bean定義名稱列表??DefaultListableBeanFactory??
==================================================================================?

轉(zhuǎn)載于:https://www.cnblogs.com/moxiaowenxin/p/11169548.html

總結(jié)

以上是生活随笔為你收集整理的【Spring源码分析】Bean加载流程概览的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。