applicationContext文件加載和bean注冊(cè)流程
? Spring對(duì)于從事Java開發(fā)的boy來(lái)說(shuō),再熟悉不過了,對(duì)于我們這個(gè)牛逼的框架的介紹就不在這里復(fù)述了,Spring這個(gè)大雜燴,怎么去使用怎么去配置,各種百度谷歌都能查到很多大牛教程,但是,當(dāng)我們按著教程一步步的把spring的開發(fā)框架搭建起來(lái)的時(shí)候,有沒有一種想搞明白spring的沖動(dòng),萬(wàn)事開頭難,就要從開頭開始,而我認(rèn)為spring開頭就是如何加載配置文件,并初始化配置文件里面的bean當(dāng)然也包括了我們用注解Service、Component等注解注解的bean,spring在容器啟動(dòng)的時(shí)候就要去加載這些內(nèi)容,然后統(tǒng)一管理這些bean(統(tǒng)一管理的是他們的bean definition),這也就是spring的一個(gè)重要概念bean的容器。
成都創(chuàng)新互聯(lián)公司專業(yè)為企業(yè)提供鳳慶網(wǎng)站建設(shè)、鳳慶做網(wǎng)站、鳳慶網(wǎng)站設(shè)計(jì)、鳳慶網(wǎng)站制作等企業(yè)網(wǎng)站建設(shè)、網(wǎng)頁(yè)設(shè)計(jì)與制作、鳳慶企業(yè)網(wǎng)站模板建站服務(wù),十余年鳳慶做網(wǎng)站經(jīng)驗(yàn),不只是建網(wǎng)站,更提供有價(jià)值的思路和整體網(wǎng)絡(luò)服務(wù)。
? applicationContext.xml到底是如何加載的呢?我把他簡(jiǎn)化成以下流程,當(dāng)然了每個(gè)環(huán)節(jié)里Spring的實(shí)現(xiàn)都是錯(cuò)綜復(fù)雜的,也是很佩服寫Spring的大神。
Spring初始化
? 當(dāng)我們初學(xué)Spring的教程的時(shí)候,教程里面肯定會(huì)有這樣的一步操作,就是新建一個(gè)applicationContext.xml文件,當(dāng)然了這是Spring里必須要有的一個(gè)文件,在這個(gè)文件里面我們可以進(jìn)行bean的配置等等工作,讓Spring來(lái)管理我們的Bean。然后,這個(gè)文件放在哪里也是個(gè)比較講究的事情,可能對(duì)于初學(xué)者來(lái)說(shuō)可額能會(huì)往WEB-INF文件夾一放就了事了,確實(shí)這樣是可以的,因?yàn)镾pring默認(rèn)的位置就是這個(gè),但是我們一般不這么做,一般會(huì)把這個(gè)文件放在resource里面,那這樣子做的話,你就要指定位置,讓Spring知道你這個(gè)文件的位置,這就有了下面一段代碼,我們的Spring項(xiàng)目都會(huì)在web.xml配置這樣的代碼:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
那問題來(lái)了,當(dāng)項(xiàng)目啟動(dòng)的時(shí)候,spring是怎么去初始化應(yīng)用的上下文的呢?答案就在類ContextLoader.java里面。當(dāng)Tomcat啟動(dòng)時(shí)候會(huì)調(diào)用該類里面的一個(gè)方法public WebApplicationContext initWebApplicationContext(ServletContext servletContext),這個(gè)方法主要完成,根據(jù)我們?cè)趙eb.xml里面配置的contextConfigLocation初始化spring的web的應(yīng)用上下文。具體看下改方法的實(shí)現(xiàn)(非完整代碼,PS:由于太長(zhǎng)了):
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
......
this.context = createWebApplicationContext(servletContext);//主要代碼,創(chuàng)建web應(yīng)用上下文
......
configureAndRefreshWebApplicationContext(cwac, servletContext);//配置參數(shù)并調(diào)用初始化方法
......
}
在這個(gè)方法里面有兩句重要代碼,第一句createWebApplicationContext(servletContext),這個(gè)會(huì)根據(jù)你配置的contextClass創(chuàng)建一個(gè)WebApplicationContext對(duì)象,但是我們一般不會(huì)配置這個(gè)參數(shù),所以Spring默認(rèn)會(huì)創(chuàng)建一個(gè)XMLWebApplicationContext對(duì)象,而這個(gè)就是后續(xù)操作的的重要對(duì)象,然后接下來(lái)一句重要代碼configureAndRefreshWebApplicationContext(cwac, servletContext)這個(gè)就會(huì)去讀取我們?cè)趙eb.xml里面配置的參數(shù)并set到變量里頭去,這樣Spring就能找到我們項(xiàng)目的applicationContext.xml文件了,到底如何找到下面會(huì)講。接下來(lái)我們來(lái)看下configureAndRefreshWebApplicationContext方法的實(shí)現(xiàn)如下:
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
// The application context id is still set to its original default value
// -> assign a more useful id based on available information
String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);
if (idParam != null) {
wac.setId(idParam);
}
else {
// Generate default id...
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
ObjectUtils.getDisplayString(sc.getContextPath()));
}
}
wac.setServletContext(sc);
String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
if (configLocationParam != null) {
wac.setConfigLocation(configLocationParam);
}
// The wac environment's #initPropertySources will be called in any case when the context
// is refreshed; do it eagerly here to ensure servlet property sources are in place for
// use in any post-processing or initialization that occurs below prior to #refresh
ConfigurableEnvironment env = wac.getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment) env).initPropertySources(sc, null);
}
customizeContext(sc, wac);
wac.refresh();
}
在這個(gè)方法中我們只要關(guān)注兩個(gè)地方,第一個(gè):
String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
if (configLocationParam != null) {
wac.setConfigLocation(configLocationParam);
}
這塊代碼塊就是,講我們配置在web.xml里面的參數(shù)set到我們的變量中去。第二個(gè)地方就是:
wac.refresh();
調(diào)用這個(gè)執(zhí)行后續(xù)的加載文件操作等后續(xù)操作。
Spring是如何找到applicationContext.xml文件
? 其實(shí),從refresh到Spring里去查找配置文件路徑之間,有很多步驟,這些也都要花點(diǎn)時(shí)間去理解的,在這里不展開講,我們只要知道,XmlWebApplicationContext會(huì)委托給XmlBeanDefinitionReader類去解析配置文件,在XmlWebApplicationContext類里面有個(gè)方法loadBeanDefinitions如下:
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {
String[] configLocations = getConfigLocations();
if (configLocations != null) {
for (String configLocation : configLocations) {
reader.loadBeanDefinitions(configLocation);
}
}
}
該方法就是將一個(gè)個(gè)的配置文件委托給XmlBeanDefinitionReader去解析配置文件,但是解析之前有句代碼String[] configLocations = getConfigLocations();這個(gè)就是查找我們的配置的文件的方法,
protected String[] getConfigLocations() {
return (this.configLocations != null ? this.configLocations : getDefaultConfigLocations());
}
實(shí)現(xiàn)很簡(jiǎn)單,就是我們有配置該位置地址就會(huì)去讀我們配置的路徑,否則就會(huì)去讀默認(rèn)的配置文件路徑,這就是開篇說(shuō)到的要是沒配置路徑也能讀取到配置文件,前提就是要跟Spring默認(rèn)定義好的文件路徑及文件名保持一致才行。getDefaultConfigLocations函數(shù)的實(shí)現(xiàn)也很簡(jiǎn)單:
/** Default config location for the root context */
public static final String DEFAULT_CONFIG_LOCATION = "/WEB-INF/applicationContext.xml";
/** Default prefix for building a config location for a namespace */
public static final String DEFAULT_CONFIG_LOCATION_PREFIX = "/WEB-INF/";
/** Default suffix for building a config location for a namespace */
public static final String DEFAULT_CONFIG_LOCATION_SUFFIX = ".xml";
protected String[] getDefaultConfigLocations() {
if (getNamespace() != null) {
return new String[] {DEFAULT_CONFIG_LOCATION_PREFIX + getNamespace() + DEFAULT_CONFIG_LOCATION_SUFFIX};
}
else {
return new String[] {DEFAULT_CONFIG_LOCATION};
}
}
如果配置了namespace就會(huì)去找這個(gè)名字的xml配置文件,如果沒有配置就去找默認(rèn)的配置文件。所以不管如何,這個(gè)配置文件是必須在spring項(xiàng)目中的。至此,配置文件基本將完,接下來(lái)就是重頭戲了,就是解析xml以及xml里面的節(jié)點(diǎn),并注冊(cè)到spring的bean容器中去。
將xml文件轉(zhuǎn)成Document處理對(duì)象
如何將xml轉(zhuǎn)成Document對(duì)象,這個(gè)也是很復(fù)雜的操作,首先將resource讀取InputStream流,在將InputStream流包裝成InputSource對(duì)象,在處理成Document對(duì)象,直接上代碼:
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
Assert.notNull(encodedResource, "EncodedResource must not be null");
if (logger.isInfoEnabled()) {
logger.info("Loading XML bean definitions from " + encodedResource.getResource());
}
Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
if (currentResources == null) {
currentResources = new HashSet<EncodedResource>(4);
this.resourcesCurrentlyBeingLoaded.set(currentResources);
}
if (!currentResources.add(encodedResource)) {
throw new BeanDefinitionStoreException(
"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
}
try {
InputStream inputStream = encodedResource.getResource().getInputStream();//獲取流
try {
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
finally {
inputStream.close();
}
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"IOException parsing XML document from " + encodedResource.getResource(), ex);
}
finally {
currentResources.remove(encodedResource);
if (currentResources.isEmpty()) {
this.resourcesCurrentlyBeingLoaded.remove();
}
}
}
接下來(lái)又到doLoadBeanDefinitions(inputSource, encodedResource.getResource());方法去了,該方法就是生成Doucument對(duì)象的,然后就是解析具體的節(jié)點(diǎn)了,部分源碼如下:
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
Document doc = doLoadDocument(inputSource, resource);//這就是解析成Document對(duì)象的操作
return registerBeanDefinitions(doc, resource);
......
}
解析Document不展開講了,不是本篇的重點(diǎn),重點(diǎn)是下面的,spring如何解析xml文件的bean及注解的bean然后注冊(cè)到容器中去,registerBeanDefinitions(doc, resource)是下面的重點(diǎn)。
解析Document里面的節(jié)點(diǎn)
XmlBeanDfinitionReader本身又不是直接取解析document的,他是委托給了DefaultBeanDefinitionDocumentReader類去實(shí)現(xiàn),源代碼中,會(huì)去創(chuàng)建DefaultBeanDefinitionDocumentReader對(duì)象實(shí)例,然后調(diào)用實(shí)例的注冊(cè)方法,代碼如下:
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
int countBefore = getRegistry().getBeanDefinitionCount();
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}
首先,我們必須知道,spring的xml文件里面有兩種類型的節(jié)點(diǎn),一種是默認(rèn)節(jié)點(diǎn),相對(duì)于默認(rèn)節(jié)點(diǎn)之外的節(jié)點(diǎn)統(tǒng)稱自定義節(jié)點(diǎn),這可以從源碼里面知道,而默認(rèn)節(jié)點(diǎn)有以下幾個(gè):beans、import、alias、bean這幾個(gè)節(jié)點(diǎn)是默認(rèn)節(jié)點(diǎn),而相對(duì)于這幾個(gè)節(jié)點(diǎn)之外的都是默認(rèn)節(jié)點(diǎn),applicationContext里面有幾個(gè)自定義節(jié)點(diǎn),如下:property-placeholder、property-override、annotation-config、component-scan、load-time-weaver、spring-configured、mbean-export、mbean-server,這里面常見的有component-scan等,為什么spring要分成默認(rèn)和自定義節(jié)點(diǎn)呢,是因?yàn)樽远x節(jié)點(diǎn)都有特定的業(yè)務(wù),比如component-scan,他是去掃描程序包,加載用注解定義的bean,例如開發(fā)中的service等bean,所以這些自定義節(jié)點(diǎn)都配備了解析器,這些解析器預(yù)先初始化好的,解析到什么節(jié)點(diǎn)就去獲取相應(yīng)的解析器去處理相應(yīng)的業(yè)務(wù),自定義節(jié)點(diǎn)解析器配置如下:
@Override
public void init() {
registerBeanDefinitionParser("property-placeholder", new PropertyPlaceholderBeanDefinitionParser());
registerBeanDefinitionParser("property-override", new PropertyOverrideBeanDefinitionParser());
registerBeanDefinitionParser("annotation-config", new AnnotationConfigBeanDefinitionParser());
registerBeanDefinitionParser("component-scan", new ComponentScanBeanDefinitionParser());
registerBeanDefinitionParser("load-time-weaver", new LoadTimeWeaverBeanDefinitionParser());
registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
registerBeanDefinitionParser("mbean-export", new MBeanExportBeanDefinitionParser());
registerBeanDefinitionParser("mbean-server", new MBeanServerBeanDefinitionParser());
}
從以上源碼分析,我們可以得到一個(gè)推論:
我們自己可以自定義xml的節(jié)點(diǎn),spring可以去解析我們自定義的xml節(jié)點(diǎn)。
其實(shí)這個(gè)推論明顯成立,我們可以看到spring里面到處都是這種自定義的節(jié)點(diǎn)的。
這里又引申出一個(gè)問題:spring怎么去區(qū)分默認(rèn)節(jié)點(diǎn)和自定義節(jié)點(diǎn)的呢?答案是通過節(jié)點(diǎn)的namespaceUri屬性去判斷,namespaceUri是什么東東?我們來(lái)看下,默認(rèn)節(jié)點(diǎn)的namespaceUri是怎么樣的,源碼是這樣定義的:
public static final String BEANS_NAMESPACE_URI = "http://www.springframework.org/schema/beans";
是不是很熟悉,這貨就是我們配置文件里面的beans根節(jié)點(diǎn)會(huì)寫的東西,如下:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
</beans>
但是問題又來(lái)了,子節(jié)點(diǎn)上我們根本沒配置這貨,但是也能讀取到,以下是個(gè)人推論:
子節(jié)點(diǎn)會(huì)繼承父節(jié)點(diǎn)的屬性,這就說(shuō)的通,子節(jié)點(diǎn)即使沒配置那一堆東西也能判斷為默認(rèn)節(jié)點(diǎn)。
接下來(lái),就是解析Document的元素,從root元素開始解析,這時(shí)候spring是創(chuàng)建了一個(gè)解析類的代理類,所有的比較和解析操作都有該類完成,我們來(lái)看下spring的源碼實(shí)現(xiàn):
protected void doRegisterBeanDefinitions(Element root) {
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = createDelegate(getReaderContext(), root, parent);
if (this.delegate.isDefaultNamespace(root)) {
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
if (StringUtils.hasText(profileSpec)) {
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
if (logger.isInfoEnabled()) {
logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec +
"] not matching: " + getReaderContext().getResource());
}
return;
}
}
}
preProcessXml(root);
parseBeanDefinitions(root, this.delegate);
postProcessXml(root);
this.delegate = parent;
}
解析節(jié)點(diǎn)的過程是個(gè)遞歸的過程,每次都要記錄節(jié)點(diǎn)的父節(jié)點(diǎn),首先會(huì)創(chuàng)建一個(gè)delegate對(duì)象,然后再去解析節(jié)點(diǎn),調(diào)用parseBeanDefinitions(root, this.delegate);這個(gè)方法進(jìn)行解析操作;
繼續(xù)來(lái)看下parseBeanDefinitions(root, this.delegate);的實(shí)現(xiàn):
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
if (delegate.isDefaultNamespace(root)) {
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
if (delegate.isDefaultNamespace(ele)) {
parseDefaultElement(ele, delegate);
}
else {
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}
很簡(jiǎn)單,可以很清晰的看出,解析是分默認(rèn)節(jié)點(diǎn)和自定義節(jié)點(diǎn)分開解析的,而自定義的節(jié)點(diǎn)的解析其實(shí)就是找到對(duì)應(yīng)的解析器各自處理對(duì)應(yīng)的業(yè)務(wù),如component-scan會(huì)找到ComponentScanBeanDefinitionParser類來(lái)處理對(duì)應(yīng)的掃描包注冊(cè)bean的操作,而默認(rèn)的節(jié)點(diǎn)的處理有如下幾種,代碼如下:
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
//處理import
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
//處理alias
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
//處理bean
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
//處理beans
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recurse
doRegisterBeanDefinitions(ele);
}
}
import的處理相對(duì)其他幾種比較復(fù)雜點(diǎn),但最終還是處理變成其他3種的處理,而beans的處理就重新遞歸上面提到的方法,最重要的是bean的處理,bean的處理其實(shí)就是下面要講的內(nèi)容,解析bean并注冊(cè)bean definition的過程。
注冊(cè)bean
終于到了最后一個(gè)內(nèi)容了,也是最重要的一個(gè)內(nèi)容,上面講的所有都是為了這個(gè)而服務(wù)的,讀取配置文件也是為了加載bean,然后注冊(cè)到spring的容器里面,讓spring統(tǒng)一管理我們定義的bean。大家都很明白,spring的bean的容器,但是如果沒有去看源碼的話,是不是都認(rèn)為spring,是把每個(gè)實(shí)例對(duì)象注冊(cè)到容器里面然后統(tǒng)一管理的?其實(shí),spring其實(shí)不是這樣的做的,spring注冊(cè)的bean最終是個(gè)bean的定義,即BeanDefinition這個(gè)實(shí)例,并不是一個(gè)個(gè)類的具體實(shí)例。我們可以簡(jiǎn)單理解這些注冊(cè)的bean definition是為了方便后續(xù)的實(shí)例化bean進(jìn)行的一步準(zhǔn)備操作。所謂的注冊(cè),其實(shí)就是把各種這些實(shí)例用一個(gè)Map來(lái)管理,所以,spring的bean的容器的底層存儲(chǔ)其實(shí)是用Map來(lái)實(shí)現(xiàn)的(這個(gè)之前面試被問過)。接下來(lái),看看源碼的實(shí)現(xiàn):
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
//這個(gè)是對(duì)bean definition進(jìn)行修改如果有必要,如配置了代理的bean等
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// Register the final decorated instance.
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
// Send registration event.
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
從源碼里可以看出,bean的解析類代理會(huì)去解析ele元素,并返回一個(gè)BeanDefinitionHolder的實(shí)例,而這個(gè)BeanDefinitionHolder我們可以簡(jiǎn)單理解為BeanDefinition對(duì)象的持有對(duì)象。然后,通過調(diào)用BeanDefinitionReaderUtils工具類去執(zhí)行具體的注冊(cè)操作。繼續(xù)看BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry())這個(gè)的實(shí)現(xiàn)如下:
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
// Register bean definition under primary name.
String beanName = definitionHolder.getBeanName();
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
// Register aliases for bean name, if any.
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
registry.registerAlias(beanName, alias);
}
}
}
從上面代碼中,spring注冊(cè)bean其實(shí)注冊(cè)的是BeanDfinition,注冊(cè)bean其實(shí)就是綁定bean的name和BeanDfinition的關(guān)系。那么,我們繼續(xù)看看bean的具體注冊(cè)過程,代碼如下:
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
Assert.hasText(beanName, "Bean name must not be empty");
Assert.notNull(beanDefinition, "BeanDefinition must not be null");
if (beanDefinition instanceof AbstractBeanDefinition) {
try {
((AbstractBeanDefinition) beanDefinition).validate();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Validation of bean definition failed", ex);
}
}
BeanDefinition oldBeanDefinition;
oldBeanDefinition = this.beanDefinitionMap.get(beanName);
if (oldBeanDefinition != null) {
if (!isAllowBeanDefinitionOverriding()) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
"': There is already [" + oldBeanDefinition + "] bound.");
}
else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {
// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
if (this.logger.isWarnEnabled()) {
this.logger.warn("Overriding user-defined bean definition for bean '" + beanName +
"' with a framework-generated bean definition: replacing [" +
oldBeanDefinition + "] with [" + beanDefinition + "]");
}
}
else if (!beanDefinition.equals(oldBeanDefinition)) {
if (this.logger.isInfoEnabled()) {
this.logger.info("Overriding bean definition for bean '" + beanName +
"' with a different definition: replacing [" + oldBeanDefinition +
"] with [" + beanDefinition + "]");
}
}
else {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Overriding bean definition for bean '" + beanName +
"' with an equivalent definition: replacing [" + oldBeanDefinition +
"] with [" + beanDefinition + "]");
}
}
this.beanDefinitionMap.put(beanName, beanDefinition);
}
else {
if (hasBeanCreationStarted()) {
// Cannot modify startup-time collection elements anymore (for stable iteration)
synchronized (this.beanDefinitionMap) {
this.beanDefinitionMap.put(beanName, beanDefinition);
List<String> updatedDefinitions = new ArrayList<String>(this.beanDefinitionNames.size() + 1);
updatedDefinitions.addAll(this.beanDefinitionNames);
updatedDefinitions.add(beanName);
this.beanDefinitionNames = updatedDefinitions;
if (this.manualSingletonNames.contains(beanName)) {
Set<String> updatedSingletons = new LinkedHashSet<String>(this.manualSingletonNames);
updatedSingletons.remove(beanName);
this.manualSingletonNames = updatedSingletons;
}
}
}
else {
// Still in startup registration phase
this.beanDefinitionMap.put(beanName, beanDefinition);
this.beanDefinitionNames.add(beanName);
this.manualSingletonNames.remove(beanName);
}
this.frozenBeanDefinitionNames = null;
}
if (oldBeanDefinition != null || containsSingleton(beanName)) {
resetBeanDefinition(beanName);
}
}
這段代碼還是比較容易理解的,首先先判斷容器里面有沒這個(gè)bean,沒有的話判斷是否在創(chuàng)建過程,如果不是直接將該bean注冊(cè)到容器里并設(shè)置其他信息。簡(jiǎn)單的說(shuō),其實(shí)就是將一個(gè)個(gè)的bean的定義跟bean的名稱綁定起來(lái),存放到map里面。至此,spring加載applicationContext.xml的大致流程已經(jīng)說(shuō)清楚了,不過這里面涉及很多比較細(xì)又難懂的類并沒有體現(xiàn)出來(lái),最終要的是搞清楚spring加載配置文件的過程和注冊(cè)bean的過程。要想深入,可以繼續(xù)研讀源碼。
覺得不錯(cuò)請(qǐng)點(diǎn)贊支持,歡迎留言或進(jìn)我的個(gè)人群855801563領(lǐng)取【架構(gòu)資料專題目合集90期】、【BATJTMD大廠JAVA面試真題1000+】,本群專用于學(xué)習(xí)交流技術(shù)、分享面試機(jī)會(huì),拒絕廣告,我也會(huì)在群內(nèi)不定期答題、探討。
網(wǎng)頁(yè)名稱:Spring源碼解析-applicationContext.xml加載和bean的注冊(cè)
轉(zhuǎn)載源于:http://www.rwnh.cn/article24/gpocce.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供定制開發(fā)、搜索引擎優(yōu)化、響應(yīng)式網(wǎng)站、微信公眾號(hào)、動(dòng)態(tài)網(wǎng)站、企業(yè)建站
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)