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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

有没有code能改xml内容_Spring源码解析-applicationContext.xml加载和bean的注册

發布時間:2023/12/2 javascript 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 有没有code能改xml内容_Spring源码解析-applicationContext.xml加载和bean的注册 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

applicationContext文件加載和bean注冊流程

? Spring對于從事Java開發的boy來說,再熟悉不過了,對于我們這個牛逼的框架的介紹就不在這里復述了,Spring這個大雜燴,怎么去使用怎么去配置,各種百度谷歌都能查到很多大牛教程,但是,當我們按著教程一步步的把spring的開發框架搭建起來的時候,有沒有一種想搞明白spring的沖動,萬事開頭難,就要從開頭開始,而我認為spring開頭就是如何加載配置文件,并初始化配置文件里面的bean當然也包括了我們用注解Service、Component等注解注解的bean,spring在容器啟動的時候就要去加載這些內容,然后統一管理這些bean(統一管理的是他們的bean definition),這也就是spring的一個重要概念bean的容器。

? applicationContext.xml到底是如何加載的呢?我把他簡化成以下流程,當然了每個環節里Spring的實現都是錯綜復雜的,也是很佩服寫Spring的大神。

Spring初始化

? 當我們初學Spring的教程的時候,教程里面肯定會有這樣的一步操作,就是新建一個applicationContext.xml文件,當然了這是Spring里必須要有的一個文件,在這個文件里面我們可以進行bean的配置等等工作,讓Spring來管理我們的Bean。然后,這個文件放在哪里也是個比較講究的事情,可能對于初學者來說可額能會往WEB-INF文件夾一放就了事了,確實這樣是可以的,因為Spring默認的位置就是這個,但是我們一般不這么做,一般會把這個文件放在resource里面,那這樣子做的話,你就要指定位置,讓Spring知道你這個文件的位置,這就有了下面一段代碼,我們的Spring項目都會在web.xml配置這樣的代碼:

contextConfigLocationclasspath:applicationContext.xml

那問題來了,當項目啟動的時候,spring是怎么去初始化應用的上下文的呢?答案就在類ContextLoader.java里面。當Tomcat啟動時候會調用該類里面的一個方法public WebApplicationContext initWebApplicationContext(ServletContext servletContext),這個方法主要完成,根據我們在web.xml里面配置的contextConfigLocation初始化spring的web的應用上下文。具體看下改方法的實現(非完整代碼,PS:由于太長了):

public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {...... this.context = createWebApplicationContext(servletContext);//主要代碼,創建web應用上下文 ...... configureAndRefreshWebApplicationContext(cwac, servletContext);//配置參數并調用初始化方法 ...... }

在這個方法里面有兩句重要代碼,第一句createWebApplicationContext(servletContext),這個會根據你配置的contextClass創建一個WebApplicationContext對象,但是我們一般不會配置這個參數,所以Spring默認會創建一個XMLWebApplicationContext對象,而這個就是后續操作的的重要對象,然后接下來一句重要代碼configureAndRefreshWebApplicationContext(cwac, servletContext)這個就會去讀取我們在web.xml里面配置的參數并set到變量里頭去,這樣Spring就能找到我們項目的applicationContext.xml文件了,到底如何找到下面會講。接下來我們來看下configureAndRefreshWebApplicationContext方法的實現如下:

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 informationString 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 #refreshConfigurableEnvironment env = wac.getEnvironment();if (env instanceof ConfigurableWebEnvironment) {((ConfigurableWebEnvironment) env).initPropertySources(sc, null);}customizeContext(sc, wac);wac.refresh();}

在這個方法中我們只要關注兩個地方,第一個:

String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);if (configLocationParam != null) {wac.setConfigLocation(configLocationParam);}

這塊代碼塊就是,講我們配置在web.xml里面的參數set到我們的變量中去。第二個地方就是:

wac.refresh();

調用這個執行后續的加載文件操作等后續操作。

Spring是如何找到applicationContext.xml文件

? 其實,從refresh到Spring里去查找配置文件路徑之間,有很多步驟,這些也都要花點時間去理解的,在這里不展開講,我們只要知道,XmlWebApplicationContext會委托給XmlBeanDefinitionReader類去解析配置文件,在XmlWebApplicationContext類里面有個方法loadBeanDefinitions如下:

protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {String[] configLocations = getConfigLocations();if (configLocations != null) {for (String configLocation : configLocations) {reader.loadBeanDefinitions(configLocation);}}}

歡迎工作一到五年的Java工程師朋友們加入Java程序員開發: 721575865

群內提供免費的Java架構學習資料(里面有高可用、高并發、高性能及分布式、Jvm性能調優、Spring源碼,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多個知識點的架構資料)合理利用自己每一分每一秒的時間來學習提升自己,不要再用"沒有時間“來掩飾自己思想上的懶惰!趁年輕,使勁拼,給未來的自己一個交代!

該方法就是將一個個的配置文件委托給XmlBeanDefinitionReader去解析配置文件,但是解析之前有句代碼String[] configLocations = getConfigLocations();這個就是查找我們的配置的文件的方法,

protected String[] getConfigLocations() {return (this.configLocations != null ? this.configLocations : getDefaultConfigLocations());}

實現很簡單,就是我們有配置該位置地址就會去讀我們配置的路徑,否則就會去讀默認的配置文件路徑,這就是開篇說到的要是沒配置路徑也能讀取到配置文件,前提就是要跟Spring默認定義好的文件路徑及文件名保持一致才行。getDefaultConfigLocations函數的實現也很簡單:

/** 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就會去找這個名字的xml配置文件,如果沒有配置就去找默認的配置文件。所以不管如何,這個配置文件是必須在spring項目中的。至此,配置文件基本將完,接下來就是重頭戲了,就是解析xml以及xml里面的節點,并注冊到spring的bean容器中去。

將xml文件轉成Document處理對象

如何將xml轉成Document對象,這個也是很復雜的操作,首先將resource讀取InputStream流,在將InputStream流包裝成InputSource對象,在處理成Document對象,直接上代碼:

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 currentResources = this.resourcesCurrentlyBeingLoaded.get();if (currentResources == null) {currentResources = new HashSet(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();}}}

接下來又到doLoadBeanDefinitions(inputSource, encodedResource.getResource());方法去了,該方法就是生成Doucument對象的,然后就是解析具體的節點了,部分源碼如下:

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)throws BeanDefinitionStoreException {Document doc = doLoadDocument(inputSource, resource);//這就是解析成Document對象的操作return registerBeanDefinitions(doc, resource);......}

解析Document不展開講了,不是本篇的重點,重點是下面的,spring如何解析xml文件的bean及注解的bean然后注冊到容器中去,registerBeanDefinitions(doc, resource)是下面的重點。

解析Document里面的節點

XmlBeanDfinitionReader本身又不是直接取解析document的,他是委托給了DefaultBeanDefinitionDocumentReader類去實現,源代碼中,會去創建DefaultBeanDefinitionDocumentReader對象實例,然后調用實例的注冊方法,代碼如下:

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文件里面有兩種類型的節點,一種是默認節點,相對于默認節點之外的節點統稱自定義節點,這可以從源碼里面知道,而默認節點有以下幾個:beans、import、alias、bean這幾個節點是默認節點,而相對于這幾個節點之外的都是默認節點,applicationContext里面有幾個自定義節點,如下:property-placeholder、property-override、annotation-config、component-scan、load-time-weaver、spring-configured、mbean-export、mbean-server,這里面常見的有component-scan等,為什么spring要分成默認和自定義節點呢,是因為自定義節點都有特定的業務,比如component-scan,他是去掃描程序包,加載用注解定義的bean,例如開發中的service等bean,所以這些自定義節點都配備了解析器,這些解析器預先初始化好的,解析到什么節點就去獲取相應的解析器去處理相應的業務,自定義節點解析器配置如下:

@Overridepublic void init() {registerBeanDefinitionParser("property-placeholder

總結

以上是生活随笔為你收集整理的有没有code能改xml内容_Spring源码解析-applicationContext.xml加载和bean的注册的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。