摘要:后續(xù)的文章中,將更進(jìn)一步的帶領(lǐng)大家逐步深入地了解的的運(yùn)行流程用于從文件系統(tǒng)中加載指定的文件,來(lái)以此作為資源,下面是構(gòu)造函數(shù)初始化基類(lèi),主要是的初始化設(shè)置資源文件調(diào)用的方法,進(jìn)行容器的刷新是容器的核心方法,我們此文中僅僅探討前兩項(xiàng)內(nèi)容。
BeanDefinition資源定位
Spring第一步,資源來(lái)開(kāi)路。
鏈接:https://juejin.im/post/5d2945...
Spring資源的加載邏輯比較復(fù)雜,我們以相對(duì)簡(jiǎn)單的FileSystemXmlApplicationContext為例來(lái)講解BeanDefinition的定位過(guò)程。
后續(xù)的文章中,將更進(jìn)一步的帶領(lǐng)大家逐步深入地了解Spring的的運(yùn)行流程
FileSystemApplicationContextFileSystemXmlApplicationContext 用于從文件系統(tǒng)中加載指定的Xml文件,來(lái)以此作為Spring資源,下面是構(gòu)造函數(shù)
public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException { //初始化基類(lèi),主要是AbstractApplicationContext的初始化 super(parent); //設(shè)置資源(xml文件) setConfigLocations(configLocations); if (refresh) { //調(diào)用AbstractApplicationContext的refresh方法,進(jìn)行容器的刷新 refresh(); } }refresh
refresh是Spring容器的核心方法,我們此文中僅僅探討前兩項(xiàng)內(nèi)容。
public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. //準(zhǔn)備刷新容器,通知子類(lèi)刷新容器 prepareRefresh(); // Tell the subclass to refresh the internal bean factory. //獲取BeanFactory,實(shí)際是獲取子類(lèi)配置的BeanFactory ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); ..... //下面代碼與BeanDefinition資源的定位、載入、注冊(cè)關(guān)系不大,不在此處分析,后續(xù)文章中進(jìn)行分析prepareRefresh
protected void prepareRefresh() { this.startupDate = System.currentTimeMillis(); //獲取激活鎖,設(shè)置激活狀態(tài) synchronized (this.activeMonitor) { this.active = true; } if (logger.isInfoEnabled()) { logger.info("Refreshing " + this); } // Initialize any placeholder property sources in the context environment //初始化屬性源,交由子類(lèi)配置(FileSystemXmlApplicationContext沒(méi)有重寫(xiě)此方法 initPropertySources(); // Validate that all properties marked as required are resolvable // see ConfigurablePropertyResolver#setRequiredProperties //驗(yàn)證所有標(biāo)記為必須的屬性,此處沒(méi)有進(jìn)行任何必須的配置,所以驗(yàn)證通過(guò) getEnvironment().validateRequiredProperties(); }obtainFreshBeanFactory
obtainFreshBeanFactory實(shí)際是調(diào)用了子類(lèi)AbstractRefreshableApplicationContext的實(shí)現(xiàn),
@Override protected final void refreshBeanFactory() throws BeansException { //如果之前有BeanFactory了,就銷(xiāo)毀重新構(gòu)建一個(gè) if (hasBeanFactory()) { destroyBeans(); closeBeanFactory(); } try { //創(chuàng)建一個(gè)BeanFactory,默認(rèn)實(shí)現(xiàn)是DefaultListableBeanFactory DefaultListableBeanFactory beanFactory = createBeanFactory(); //設(shè)置id beanFactory.setSerializationId(getId()); //1.設(shè)置一些基本屬性 allowBeanDefinitionOverriding,allowCircularReferences // 是否允許beanDefinition重載,允許循環(huán)引用 //2.設(shè)置一個(gè)自動(dòng)注入候選者判斷器QualifierAnnotationAutowireCandidateResolver // 專(zhuān)用于@Querifiler @Value的條件判斷 customizeBeanFactory(beanFactory); //定位、加載、注冊(cè)beanDefinitiion,交由子類(lèi)實(shí)現(xiàn),因?yàn)椴煌臉I(yè)務(wù)場(chǎng)景下,資源的未知是不同的,所以父類(lèi)不能確定具體的資源加載形式,所以交由子類(lèi)實(shí)現(xiàn),對(duì)于xml來(lái)說(shuō)是交由子類(lèi)AbstractXmlApplicationContext實(shí)現(xiàn), loadBeanDefinitions(beanFactory); synchronized (this.beanFactoryMonitor) { this.beanFactory = beanFactory; } } catch (IOException ex) { throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex); } } //這里是子類(lèi)AbstractXmlApplicationContext實(shí)現(xiàn) @Override protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { // Create a new XmlBeanDefinitionReader for the given BeanFactory. //創(chuàng)建一個(gè)XmlBeanDefinitionReader,并初始化 //向XmlBeanDefinitionReader //設(shè)置一個(gè)BeanDefinitionRegistry //設(shè)置一個(gè)ResourceLoader //因?yàn)镈efaultListableBeanFactory不是一個(gè)ResoueceLoader,所以這里用了默認(rèn)值PathMatchingResourcePatternResolver //設(shè)置環(huán)境,用的默認(rèn)值StandardEnvironment //但是不要慌,下面的代碼中,就會(huì)使用FileSystemXmlApplicationContext來(lái)替換這兩個(gè)值 XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); // Configure the bean definition reader with this context"s // resource loading environment. //使用FileSystemXmlApplicationContext中的環(huán)境替換 beanDefinitionReader.setEnvironment(this.getEnvironment()); //使用FileSystemXmlApplicationContext來(lái)作為資源加載器 beanDefinitionReader.setResourceLoader(this); //設(shè)置一個(gè)實(shí)體解析器,用于獲取XML中的校驗(yàn)DTD文件,下篇文章中會(huì)使用到,這里是一個(gè)伏筆 beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); // Allow a subclass to provide custom initialization of the reader, // then proceed with actually loading the bean definitions. //設(shè)置驗(yàn)證類(lèi)型 initBeanDefinitionReader(beanDefinitionReader); //定位、加載、注冊(cè) loadBeanDefinitions(beanDefinitionReader); } // protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException { //我們使用的是String配置的資源,不會(huì)走這個(gè)加載 Resource[] configResources = getConfigResources(); if (configResources != null) { reader.loadBeanDefinitions(configResources); } //從此處進(jìn)入 String[] configLocations = getConfigLocations(); if (configLocations != null) { //使用XmlBeanDefinitionReader定位、加載、注冊(cè)指定的configLocations reader.loadBeanDefinitions(configLocations); } } //這里傳入的String[]類(lèi)型,所以調(diào)用的是XmlBeanDefinitionReader的父類(lèi)AbstractBeanDefinitionReader的方法 public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException { Assert.notNull(locations, "Location array must not be null"); //加載的BeanDefinition的個(gè)數(shù)統(tǒng)計(jì) int counter = 0; //迭代,加載所有的location for (String location : locations) { //加載并統(tǒng)計(jì)數(shù)量 counter += loadBeanDefinitions(location); } return counter; } public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException { return loadBeanDefinitions(location, null); } //加載流程的具體實(shí)現(xiàn) public int loadBeanDefinitions(String location, Set資源定位actualResources) throws BeanDefinitionStoreException { //獲取ResoruceLoader,實(shí)際就是上文中傳入的FileSystemXmlApplicaitonContext ResourceLoader resourceLoader = getResourceLoader(); if (resourceLoader == null) { throw new BeanDefinitionStoreException( "Cannot import bean definitions from location [" + location + "]: no ResourceLoader available"); } //FileSystemXmlApplicaitonContext實(shí)現(xiàn)了這個(gè)ResourcePatternResolver接口 if (resourceLoader instanceof ResourcePatternResolver) { // Resource pattern matching available. try { //這行很重要,這里就是資源定位和加載的核心代碼,這里是利用FileSystemXmlApplicaitonContext來(lái)進(jìn)行資源的定位和加載,具體分析見(jiàn)下文的資源定位 Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location); //資源的加載和BeanDefintiion的注冊(cè) int loadCount = loadBeanDefinitions(resources); if (actualResources != null) { for (Resource resource : resources) { actualResources.add(resource); } } if (logger.isDebugEnabled()) { logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]"); } return loadCount; } catch (IOException ex) { throw new BeanDefinitionStoreException( "Could not resolve bean definition resource pattern [" + location + "]", ex); } } else { // Can only load single resources by absolute URL. Resource resource = resourceLoader.getResource(location); int loadCount = loadBeanDefinitions(resource); if (actualResources != null) { actualResources.add(resource); } if (logger.isDebugEnabled()) { logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]"); } return loadCount; } }
下面是Spring真正加載資源的邏輯
//FileSystemXmlApplicationContext本身并沒(méi)有進(jìn)行資源的加載,而是調(diào)用了基類(lèi)AbstractApplicaiotnContext資源加載的方法,注意此處的方法名是 getResources , //內(nèi)部實(shí)際是調(diào)用自己內(nèi)部的resourcePatternResolver,這個(gè)resourcePatternResolver是在AbstractApplicationContext實(shí)例化是被創(chuàng)建的,是一個(gè)PathMatchingResourcePatternResolver //所以這里資源的加載是先交給PathMatchingResourcePatternResolver來(lái)解析 public Resource[] getResources(String locationPattern) throws IOException { return this.resourcePatternResolver.getResources(locationPattern); }PathMatchingResourcePatternResolver的解析
public Resource[] getResources(String locationPattern) throws IOException { Assert.notNull(locationPattern, "Location pattern must not be null"); //如果以 classpath*: 開(kāi)頭 if (locationPattern.startsWith(CLASSPATH_ALL_URL_PREFIX)) { // a class path resource (multiple resources for same name possible) //如果是Ant表達(dá)式,則進(jìn)入此 if (getPathMatcher().isPattern(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()))) { // a class path resource pattern return findPathMatchingResources(locationPattern); } else { //否則在classpath中尋找資源 // all class path resources with the given name return findAllClassPathResources(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length())); } } //如果不以classpath*:開(kāi)頭 else { //查看 : 后面的路徑 int prefixEnd = locationPattern.indexOf(":") + 1; //如果:后的路徑符合ant表達(dá)式 if (getPathMatcher().isPattern(locationPattern.substring(prefixEnd))) { // a file pattern return findPathMatchingResources(locationPattern); } else { //最后 : 后的表達(dá)式不是ant表達(dá)式的話(huà),就調(diào)用自己的ResourceLoader進(jìn)行資源的加載 //注意 PathMatchingResourcePatternResolver的構(gòu)造函數(shù)中,已經(jīng)把AbstractApplicationCotexnt作為了自己的資源加載器,所以此處調(diào)用的方法就是AbstractApplicationContext的getResource,注意這個(gè)方法的名稱(chēng),是getResource,不是getResources //因?yàn)锳bstractApplicationContext繼承了DefaultResourceLoader,所以此處調(diào)用的getResource,實(shí)際調(diào)用的DafaultResourceLoader的getResource方法, // a single resource with the given name return new Resource[] {getResourceLoader().getResource(locationPattern)}; } } }DefaultResourceLoader.getResource
public Resource getResource(String location) { Assert.notNull(location, "Location must not be null"); //如果 location 以 classpath: 開(kāi)頭,就返回一個(gè)ClassPathResouce if (location.startsWith(CLASSPATH_URL_PREFIX)) { return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader()); } else { try { //不以classpath: 開(kāi)頭的話(huà),就嘗試使用url來(lái)獲取資源,如果不拋出異常,就返回一個(gè)UrlResource資源 URL url = new URL(location); return new UrlResource(url); } catch (MalformedURLException ex) { //異常出現(xiàn),說(shuō)明url不能正確解析,只好調(diào)用 getResourceByPath 來(lái)加載資源 //注意 DefaultResourceLoader中已有g(shù)etResourceByPath的實(shí)現(xiàn),就是把location當(dāng)作一個(gè)ClassPathContextResource來(lái)解析,但是在此處并不是,因?yàn)镕ileSystemXmlApplicationContext重寫(xiě)了這個(gè)方法,所以getResourceByPath實(shí)際是調(diào)用的FileSystemXmlApplicationContext中的實(shí)現(xiàn), return getResourceByPath(location); } } }FileSystemXmlApplicationContext.getResourceByPath
//可以看出,把資源當(dāng)作一個(gè)FileSystemResource返回,至此,我們就找到了真正的資源位置,完成了資源的定位 protected Resource getResourceByPath(String path) { if (path != null && path.startsWith("/")) { path = path.substring(1); } return new FileSystemResource(path); }總結(jié)與回顧
我們可以發(fā)現(xiàn)Spring中對(duì)于資源的定位是比較復(fù)雜的,我大致梳理一下,大致邏輯如下:
使用PathMatchingResourcePatternResolver來(lái)解析Ant表達(dá)式路徑,成功則返回,失敗則向下
如果是classpath* 開(kāi)頭的資源 ,
符合Ant規(guī)則的按照Ant路徑解析
不符合Ant規(guī)則的,解析成ClasspathResource
不是classpath*開(kāi)頭的資源
如果 :后面的路徑符合Ant規(guī)則,按照Ant路徑解析
:后的路徑不符合Ant規(guī)則,調(diào)用傳入的ResouceLoader來(lái)解析(AbstractApplicaitonContext把這份工作交由DefaultResourceLoader來(lái)執(zhí)行)
使用DefaultResouceLoader加載資源
如果資源以 classpath: 開(kāi)頭,返回 ClassPathResource
不是 classpath: 開(kāi)頭
按照Url解析不出錯(cuò),返回UrlResource
解析Url出錯(cuò)了,調(diào)用getResourceByPath來(lái)解析(這個(gè)方法被FileSystemXmlApplicationContext重寫(xiě)了)
以上就是FileSystemXmlApplicationContext定位資源的基本流程。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/75329.html
摘要:前言以下源碼基于版本解析。實(shí)現(xiàn)源碼分析對(duì)于的實(shí)現(xiàn),總結(jié)來(lái)說(shuō)就是定位加載和注冊(cè)。定位就是需要定位配置文件的位置,加載就是將配置文件加載進(jìn)內(nèi)存注冊(cè)就是通過(guò)解析配置文件注冊(cè)。下面我們從其中的一種使用的方式一步一步的分析的實(shí)現(xiàn)源碼。 前言 以下源碼基于Spring 5.0.2版本解析。 什么是IOC容器? 容器,顧名思義可以用來(lái)容納一切事物。我們平常所說(shuō)的Spring IOC容器就是一個(gè)可以容...
摘要:使用別名時(shí),容器首先將別名元素所定義的別名注冊(cè)到容器中。調(diào)用的方法向容器注冊(cè)解析的通過(guò)對(duì)對(duì)象的解析和封裝返回一個(gè)通過(guò)這個(gè)來(lái)注冊(cè)對(duì)象當(dāng)調(diào)用向容器注冊(cè)解析的時(shí),真正完成注冊(cè)功能的是。 文章參考來(lái)自:https://www.cnblogs.com/ITtan... 文章代碼來(lái)自 spring-boot 1.4.1 Release版本 Spring IoC容器對(duì)Bean定義資源的載入是從ref...
摘要:對(duì)于開(kāi)發(fā)者來(lái)說(shuō),無(wú)疑是最常用也是最基礎(chǔ)的框架之一。概念上的東西還是要提一嘴的用容器來(lái)管理。和是容器的兩種表現(xiàn)形式。定義了簡(jiǎn)單容器的基本功能。抽象出一個(gè)資源類(lèi)來(lái)表示資源調(diào)用了忽略指定接口的自動(dòng)裝配功能委托解析資源。 對(duì)于Java開(kāi)發(fā)者來(lái)說(shuō),Spring無(wú)疑是最常用也是最基礎(chǔ)的框架之一。(此處省略1w字吹Spring)。相信很多同行跟我一樣,只是停留在會(huì)用的階段,比如用@Component...
摘要:從層層委托的依賴(lài)關(guān)系可以看出,的依賴(lài)注入給屬性賦值是層層委托的最終給了內(nèi)省機(jī)制,這是框架設(shè)計(jì)精妙處之一。當(dāng)然分享到你的朋友圈讓更多小伙伴看到也是被作者本人許可的若對(duì)技術(shù)內(nèi)容感興趣可以加入群交流高工架構(gòu)師群。 每篇一句 具備了技術(shù)深度,遇到問(wèn)題可以快速定位并從根本上解決。有了技術(shù)深度之后,學(xué)習(xí)其它技術(shù)可以更快,再深入其它技術(shù)也就不會(huì)害怕 相關(guān)閱讀 【小家Spring】聊聊Spring中的...
摘要:這個(gè)讀取器可以讀取注解標(biāo)注下的所有定義,并最終添加到的中。處理注解的配置類(lèi)讀取每一個(gè)配置類(lèi)中定義的,加入到容器中。 IOC的核心就是代碼入口就在AbstractApplictionContext public void refresh() throws BeansException, IllegalStateException { synchronized (t...
閱讀 3165·2023-04-25 18:54
閱讀 2665·2021-11-02 14:40
閱讀 3276·2021-09-23 11:58
閱讀 2490·2019-08-30 13:50
閱讀 1289·2019-08-29 12:46
閱讀 3177·2019-08-28 17:51
閱讀 734·2019-08-26 11:47
閱讀 954·2019-08-23 16:17