Spring-5.1.5源码解析【IOC】(一)

不管是自己做项目,还是工作中,Spring都是使用最多的框架。但是一直都没有好好的了解一下,本篇博客的目的就是为了深入分析Spring最核心的概念之一:IOC容器的实现原理

通常我们在获取Bean的时候,会这么去做:

//创建Spring容器

 ApplicationContext ctx=new ClassPathXmlApplicationContext("classpath*:beans.xml");  

//通过getBean()方法获取Bean实例  

Person person=(Person) ctx.getBean("person");  

我们来看看这个ClassPathXmlApplicationContext里面是怎么样的

下图是ClassPathXmlApplicationContext的继承体系:

下面我们就深入的来看看new ClassPathXmlApplicationContext()是做了什么:

可以发现,不管我们是怎么创建ClassPathXmlApplicationContext对象,它内部其实都是调用同一个构造器的,我们来看看他最后调用的那个构造器是怎么样的


一步步看,先看看super(parent);是在干什么:


写不下了,还是直接写出来吧

  1. super(parent):

结尾是在AbstractApplicationContext的构造方法中,来看看这个构造方法


 1.1 this():

                  首先是调用了上面的那个构造器,我们先来看看this.resourcePatternResolover是什么飞机

用于将位置模式(例如,Ant-style 的路径模式)解析为资源对象的策略接口(稍后我们就会看到),在这个类中还提到了 :此接口还建议对类路径中的所有匹配资源使用新的资源前缀“classpath*:”。注意,在这种情况下,资源位置应该是没有占位符的路径(例如“/beans.xml”);JAR文件或类目录可以包含多个同名文件。

也就是this的目的其实就是为了给这个resourcePatternResolver变量赋值,我们来看看具体是怎么做的:

方法上加了注释,这样看起来可能会更加方便一点.....

这里是直接new PathMatchingResourcePatternResolver(this); 话不多说继续往下点

解释一下这里:这里是把AbstractApplicationContext给设置进PathMatchingResourcePatternResolver中(AbstractAppplicationContext是DefaultResourceLoader的子类,而DefaultResourceLoader是ResourceLoader的实现类)

PathMatchingResourcePatternResolver :


一个ResourcePatternResolver 能够解析指定资源位置到一个或者多个匹配资源的路径,源路径可以是一个简单的路经,它具有到目标为Resource,或者可能包含特殊的 classpath*: 前缀和 / 或者内部Ant-style正则表达式

没有通配符:

在简单情况下,如果指定的位置路经不是以 “classpath*:”为前缀,并且不包含PathMatcher模式,这个解析器将通过getResource()调用基础ResourceLoader。例如 file:C:/context.xml,伪URL 例如: classpath:/context.xml,以及简单的不固定路经 /WEB-INF/context.xml

Ant-style模式:

/WEB-INF/*-context.xml

com/mycompany/*applicationContext.xml

file:C:/some/path/*-context.xml

classpath:com/mycompany/*applicationContext.xml


http://sishuok.com/forum/blogPost/list/0/2458.html#7106 大家可以看这篇博客了解更多

第一步是设置给parent这个成员变量,这里就不用说了,来看下Environment是个什么类吧

Environment :

表示当前应用程序运行环境的接口,为应用程序环境的两个关键方面建模 profiles 和 properties,与属性访问相关的方法通过PropertyResolver 超级接口。

profile :是要注册的命名的逻辑Bean定义组,只有当给定的配置文件处于活动状态时,才使用容器,可以分配bean到一个配置文件,无论是Xml定义的还是通过注解定义的 或者 @profile 注解,与配置文件相关的Environment对象的角色是确认哪些配置文件(如果有) 当前是getActiveProfiles ,如果没有则 getDefaultProfiles,这里大家应该都有用到过applicationContext-dev.xml,applicationContext-prod.xml 这种选择测试跟线上环境的方法,就是在配置文件中添加

Properties:几乎在所有应用程序中发挥重要作用,并且可能源于各种源: 属性文件,JVM系统属性,系统环境变量,JNDI,servlet上下文参数,特殊属性对象,Maps 等等.与属性相关的环境对象的角色是为用户提供方便的环境接口,用于配置属性源并且从中解析属性

ApplicationContext 内管理的bean可注册为 EnvironmentAware 或者 Environment 以便直接查询配置文件状态或解析属性。

然而在大多情况下,application级别的bean不需要与Environment交互,但是必须有 ${...} 属性 由属性占位符配置程序PropertySourcesPlaceholderConfigurer,其本身就是EnvironmentAware,从Spring 3.1开始,在使用时默认注册  <context:property-placeholder/>

环境对象的配置必须通过 ConfigurableEnvironment接口,返回来自所有AbstractApplicationContext子类 getEnvironment 方法

反正这个玩意看图就知道了


还有一些方法没写出来,懒得写了 https://jinnianshilongnian.iteye.com/blog/2000183 这个里面讲的比较详细

到这里,setSuper(parent);这个方法就算是完了。总结一下:

        1.在PathMatchingResourcePatternResolver中设置AbstractApplicationContext为resourceLoader

        2.AbstractApplicationContext设置ResourcePatternResolver

        3. AbstractApplicationContext 设置ApplicationContext

先不要管为啥要这样子设置,看到后面就知道了....




2. setConfigLocations(configLocations):

                先不着急说什么,先把前面几个方法给贴上,反正也没几个方法

String CONFIG_LOCATION_DELIMITERS = ",; \t\n";
resolverPath() 这个方法才是具体的方法


看到这个getEnvironment()应该可以联想到刚才看的吧
调用的是StandardEnvironment中的resolveRequiredPlaceholders方法,最后在AbstractEnvironment中找到,该类为StandardEnvironment的父类


继续往下点


再往下点


private PropertyPlaceholderHelper strictHelper;
这里是直接就给new了一个PropertyPlaceholderHelper对象



在这个地方我们是直接跳过来的,所以大家直接按照顺序看图就可以了,在之气那我们调用helper.replacePlaceholders(),传的参数是 text, this::getPropertyAsRawString,我们找到这个getPropertyAsRawString方法,这个方法是一个抽象类,那么我们直接看这个类(AbstractPropertyResolver)的子类,按照顺序我们找到子类,并且看它的实现

在我们通过AbstractEnvironmentt的resolveRequiredPlaceholders方法再进入到AbstractPropertyResolver中的过程这里再截图说明一下:

当时是直接新建new了一个StandardEnvironment过来的,可以往上看看回忆一下


这个地方就比较有意思了

将MutablePropertySources 设置进PropertySourcesPropertyResolver的propertySources属性


而且注意到没有,这个PropertySourcesPropertyResolver就是AbstractPropertyResolver 的子类,我们刚才就是在找这个王八蛋,接下来我们走下断点


接着往下走
这个propertySources不为null那是肯定的,因为我们看到在AbstaractEnvironment中已经给他么的new了一个MutablePropertySources了
然后我们再来看看为什么会有值,还特么可以迭代

这个地方咱们还得回到之前的地方看看

这里它给咱们new了一个StandardEnvironment对象,咱们点进去
因为隐式super,这里走父类,然后父类中刚好把这个MutablePropertySources传进去,传进去就是为了添加这两个属性
添加这两个属性就是为了获取系统环境变量跟属性


总结:

1.设置AbstractApplicationContext中的 ConfigurableEnvironment environment;

2.解析我们ClassPathXmlApplicationContext传入的资源中的占位符


3、refresh()

好了,终于到了最后的refresh方法了

截不全只能这样了。 == 

先从第一个方法开始看吧 ==!

1、prepareRefresh()

时间还长,方法还多,慢慢来,一个个来看

首先是设置AbstractApplicationContext的一些初始化变量

initPropertySources();                            在上下文环境中初始化任何占位符属性源,留给子类拓展的

这个方法主要是留给子类或者我们来自己拓展的,其实就是往AbstractPropertyResolver的 requiredProperties集合中添加元素,这个集合是用来存储初始化时,必须存在的环境变量

接下里我们就来手动实验一下这个方法该怎么来玩:

定义一个类实现ClassPathXmlApplication,然后重写AbstractApplicationContext的initPropertySources方法,最重要的调用父类构造方法可别把参数给漏了   ==, 然后我们在initPropertySources就调用的是setRequiredProperties中设置了一个 "HALOSKY_HOME" (这里就不再说为什么要getEnvironment了,前面应该说了两遍吧)

然后我们贴上测试图:

这个地方原理就是这样,在initPropertySources 方法中通过AbstractPropertyResolver的setRequiredProperties方法往requiredProperties集合中添加必不可缺的环境变量
我们也可以通过添加系统环境变量的方式来做,因为在上面的截图我们可以很清楚的发现,其实在启动时是会把系统中的环境变量给读取进来的


看吧,没问题~
这里就不用说了吧...看到后面就能引出来 ==


2. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory()

在Spring源码深度解析这本书中是这么介绍以下这几个类的:

    1. BeanFactory: 定义获取bean及bean的各种属性

    2. HierarchicalBeanFactory: 继承BeanFactory,也就是BeanFactory定义的功能的基础上增加了对                              parentFactory的支持。

    3. ListableBeanFactory: 根据各种条件获取bean的配置清单

    4. ConfigurableBeanFactory: 根据配置Factory的各种方法

    5. ConfigurableListableBeanFactory: beanFactroy配置清单,指定忽略类型以及接口等。

    6. DefaultListableBeanFactory:综合上面的所有功能,主要是对bean注册后的处理.

XmlBeanFactory对DefaultListableBeanFactory类进行了拓展,主要是用以从Xml文档中读取beanDefinition,对于注册以及获取Bean都是使用从父类DefaultListableBeanFactory继承的方法去实现,而唯独与父类不同的个性化实现就是增加了XmlBeanDefinitionReader类型的reader属性,在XmlBeanFactory中主要使用reader属性对资源文件进行读取和注册。

obtainFreshBeanFactory中调了两个方法 regreshBeanFactory跟getBeanFactory两个方法,两个方法都为抽象方法,实现类为子类AbstractRefreshableApplicationContext
一步步来,先来看看createBeanFactory这里面是在干什么
就不说这里是直接new了一个DefaultListableBeanFactory这种一看就能明白的话了 ==!

先看看这个getInternalParentBeanFactory()这个方法是在搞什么飞机

这个getParent是AbstractApplicationContext中的方法,还记得我们在第一步super(parent);这个方法的时候吗,在这一步的最后的时候我们就走到了这个Abstract Application的构造方法中,而这个构造方法的处理是两步,第一步是this(),第二步是setParent(parent),这个方法就是把ClassPathXmlApplicationContext中的参数给传给了AbstractApplicationContext,但是有个问题就是这个parent是null,而且通过类的继承体系我们可以发现这里最终是直接getParent的,因为getParent就是获取一个ApplicationContext,而ConfigurableApplicationContext是继承ApplicationContext,所以这里是返回getParent的

接着我们再来看看new DefaultListableBeanFactory的时候干了啥

又特么是super ==!
也是两方法

小结:

new DefaultListableBeanFactory:

    1.new AbstractBeanFactory()

    2.AbstractBeanFactory中设置parentBeanFactory为ApplicationContext

    3.AbstractAutowireCapableBeanFactory的忽略依赖关系接口集合属性中添加了 BeanNameAware,BeanFactoryAware,BeanClassLoaderAware

继续往下看

这里的话其实是设置BeanFactory的一些事件属性(允许BeanDefinition重写,允许循环引用)

然后到了最后也是obtainFreshBeanFactory方法最重要的一步了----->loadBeanDefinitions方法,该方法的实现在子类AbstractXmlApplicationContext中,因为这是个抽象方法

我们先进这个new XmlBeanDefinitionReader这个里头看看

接着往下点
第一个if走的一定是false,因为我们传过来的是一个DefaultListableBeanFactory,DefaultListableBeanFactory并不是ResourceLoader的实例,第二个if也是走else 因为DefaultListableBeanFactory也不是EnvironmentCapble的实例  ==!

这里先说一下EnvironmentCapable这个类(注释翻译):

接口,指示包含并公开Environment引用的组件。

所有Spring应用程序上下文都支持EnvironmentCapable, 并且该接口主要用于在框架方法中执行instanceof检查

 这些框架方法接受可能是或可能不是应用程序上下文实例的BeanFactory实例,以便与环境交互(如果确实可用)。

ApplicationContext扩展了环境功能,因此公开了 getenvironment()方法;ConfigurableApplicationContext重新定义了getEnvironment方法 并缩小签名以返回一个ConfigurableEnvironment,其效果是环境对象是“只读”的,直到从ConfigurableApplicationContext访问它为止,此时也可以对其进行配置。

到这里new XmlBeanDefinitionReader(beanFactory); 这个过程就结束了

总结:

1.AbstractBeanDefinitionReader中设置registry属性为DefaultListableBeanFactory

2.AbstractBeanDefinitionReader中设置resourceLoader 为PathMatchingResourcePatternResolver

3.AbstractBeanDefinitionReader中设置environment 为StandardEnvironment

4.设置XmlBeanDefinitionReader的Environment属性为AbstractApplicationContext中的StandardEnvironment (在我们之前super(parent)这一步的时候给new的)

5.设置XmlBeanDefinitionReader的ResourceLoader为AbstractXmlApplicationContext

6.设置XmlBeanDefinitionReader的EntityResolver为ResourceEntityResolver

    ResourceEntityResolver: EntityResolver实现,尝试通过ResourceLoader(通常是相对于ApplicationContext的资源库)解析实体引用(如果适用),扩展DelegatingEntityResolver以同时提供DTD和XSD查找。允许使用标准XML实体将XML片段包含到应用程序上下文定义中,例如将大型XML文件拆分为多个模块。include路径可以像往常一样相对于应用程序上下文的资源库,而不是相对于JVM工作目录(XML解析器的默认值)。

注意:除了相对路径之外,在当前系统根目录(即jvm工作目录)中指定文件的每个URL也将相对于应用程序上下文进行解释。


第二步是把AbstractXmlApplicationContext作为自己的resourceLoader属性

EntityResolver:对于解析一个xml,sax 首先会读取该xml文档上的声明,根据声明去寻找相应的dtd定义,以便对文档的进行验证,默认的寻找规则,(即:通过网络,实现上就是声明DTD的地址URI地址来下载DTD声明),并进行认证,下载的过程是一个漫长的过程,而且当网络不可用时,这里会报错,就是应为相应的dtd没找到,详细的用法跟说明大家可以看看这个博客:https://www.cnblogs.com/ghgyj/p/4027796.html,我就不再去演示了


initBeanDefinitionReader(beanDefinitionReader):

loadBeanDefinitions(beanDefinitionReader):

使用给定的XmlBeanDefinitionReader加载bean定义。bean工厂的生命周期由refreshBeanFactory方法处理;因此,此方法只需加载/注册bean定义。

这里我们主要是看reader.loadBeanDefinitions(configLocations)这里,首先这个getConfigLocations(),我们在第二步setConfigLocations()的时候,其实就是把我们在new ClassPathXmlApplicationContext时所传的资源付给了AbstractRefreshableConfigApplicationContext的this.configLocations,所以这里只是把我们之前给设置进来的资源给拿出来而已 == 

回忆贴上 ==!

流程开始:

我先把图全给贴上,然后咱们慢慢分析这个流程

1. getResourceLoader(): 这一步在new XmlBeanDefinitionReader的时候,就已经把resourceLoader给赋值为PathMatchingResourcePatternResolver这个对象,在上面就总结就可以看得到

2. 因为PathMatchingResourcePatternResolver它实现了ResourcePatternResolver接口,所以这里我们直接看getResources(location)这个方法

getPathMatcher()可以获取一个AntPathMatcher对象

PathMatcher : 实现Ant-style的路径模式

映射使用以下规则匹配URL:

?:匹配一个字符

* :匹配零个或多个字符

** : 匹配路径中的零个或多个目录

spring:[a-z]+}: 将regexp {[a-z]+} 作为名为“spring”的路径变量匹配

这里就是在判断我们路经中是否有多个资源具有相同的名称  例如:classpath*:applicationContext.xml;classpath*:applicationContext-beans.xml

这里我们就先看这个findAllClassPathResources这个方法:

先来看看这个getClassLoader()方法,这个getResourceLoader也是获取PathMatchingResourcePatternResolver类中的ResourceLoader,在第一步super(parent); 这里我们已经为PathMatchingResourcePatternResolver设置好了resourceLoader

这个地方我们打个断点往下看,这样可以看得更清晰一点:

这个地方就是为了获取我们资源文件的磁盘路经

那么在findAllClassPathResources这个方法的目的就是返回我们资源文件的磁盘路经   ψ(*`ー´)ψ

现在回到我们AbstractBeanDefinitionReader中

最有意思的来了.... ┗( ▔, ▔ )┛

我们接着下一个loadBeanDefinitions(resources)这个方法往下看

还是loadBeanDefinitions方法。Resource中包含我们资源文件的磁盘路径,重头戏要来了
从xml配置文件中加载BeanDefinition


当前正在加载的XML bean定义资源

1.获取当前线程中的EncodedResource集合

2.如果添加失败,则认为是该encodedResource在循环加载

3.获取资源文件InputStream对象

doLoadBeanDefinitions才是真正实现的方法

doLoadDocument : 使用配置的DocumentLoader实际加载指定的文档。

getValidationModeForResource:指示应自动检测验证模式,isNamespaceAware():XML解析器是否应该感知XML名称空间,getEntity:返回要使用的EntityResolver,如果没有指定,则构建默认的解析器;this.errorHandler :简单的ErrorHandler实现,使用给定的通用日志记录器实例记录警告,并重新抛出错误以停止XML转换。

这个地方我觉得有必要提一下getEntityResolver这个方法:

这个地方在看obtainFreshBeanFactory 方法的时候吗,有给这个entityResolver属性赋值过,所以 this.entityResolver = ResourceEntityResolver 对象,因此也是直接返回这个对象


Spring的默认DocumentLoader实现。只需使用标准的jaxp配置的XML解析器加载 Document 。如果您想更改用于加载文档的 DocumentBuilder,那么一种策略是在启动JVM时定义相应的Java系统属性。例如,要使用Oracle DocumentBuilder,可以这样启动应用程序:java -Djavax.xml.parsers.DocumentBuilderFactory=oracle.xml.jaxp.JXDocumentBuilderFactory MyMainClass(存翻译注释)


需要注意的是我们在调用过来是validationMode所传的值为默认值:VALIDATION_AUTO(指示应自动检测验证模式)


创建一个DocumentBuilder,此bean定义阅读器将使用它解析XML文档。可以在子类中重写,添加构建器的进一步初始化

接着来看下一个方法

int count = registerBeanDefinitions(doc, resource);

BeanDefinitionDocumentReader:用于解析包含Spring bean定义的XML文档,由XmlBeanDefinitionReader用于实际解析DOM文档。


DefaultBeanDefinitionDocumentReader :  BeanDefinitionDocumentReader接口的默认实现, 该接口根据“Spring -beans”DTD和XSD格式(Spring的默认XML bean定义格式)读取bean定义。所需XML文档的结构、元素和属性名都硬编码在这个类中。(当然,如果需要生成这种格式,可以运行转换)。不需要是XML文档的根元素:这个类将解析XML文件中的所有bean定义元素,而不管实际的根元素是什么。


int countBefore = getRegistry().getBeanDefinitionCount() : 获取bean定义对象Map的大小

getRegistry其实就是获取之前我们给XmlBeanDefinitionReader设置的DefaultListableBeanFactory,是在AbstractXmlApplicationContext类的loadBeanDefinitions方法中new XmlBeanDefinitionReader的时候给赋值了

documentReader.registerBeanDefinitions(doc, createReaderContext(resource)):

先来看看这个createReaderContext方法是做了什么吧:

先看看这个createDefaultNamespaceHandlerResolver是做什么,返回什么吧
getResourceLoader()看方法名就知道是在干什么,这里是直接走true,因为我们在之前已经给这个属性赋值过了,值为PathMatchingResourcePatternResolver 对象,刚刚就把图给贴出来

DefaultNamespaceHandlerResolver:NamespaceHandlerResolver接口的默认实现。根据映射文件中包含的映射将名称空间uri解析为实现类。

String handlerMappingsLocation  = "META-INF/spring.handlers";  (没啥意思的方法我就不贴图了吧 ==),handlerMappingsLocation =PathMatchingResourcePatternResolver.getClassLoader(); 


这里就需要记一下了,因为后面会有地方用到 (感觉像是在说废话)

                ReaderContext (设置属性):

                        1.resource = Resource

                        2.problemReporter = FailFastProblemReporter

                        3. eventListener   =  EmptyReaderEventListener

                        4. sourceExtractor  = NullSourceExtractor

                XmlReaderContext (设置属性):

                        1. reader  =  XmlBeanDefinitionReader

                        2. namespaceHandlerResolver =  DefaultNamespaceHandlerResolver

                createReaderContext() : 创建XmlReaderContext对象,接下来我们回到registerBeanDefinitions方法:

this.delegate = createDelegate(getReaderContext(), root, parent):

初始化默认的懒加载初始化、自动装配、依赖项检查设置、init-method、destroy-method和merge设置,通过返回到给定的父元素来支持嵌套的“bean”元素用例,以防默认值没有在本地显式设置。

1.创建一个与所提供的相关联的新的BeanDefinitionParserDelegate

初始化默认的懒加载初始化、自动装配、依赖项检查设置、init-method、destroy-method和merge设置。通过返回到给定的父元素来支持嵌套的“bean”元素用例,以防默认值没有在本地显式设置。

this.defaults = DocumentDefaultsDefinition对象 (在标准的Spring XML bean定义文档中,简单的JavaBean包含在<beans>级别指定的默认值:default-lazy-init,default-autowire,etc)

populateDefaults : 

首先是通过我们Xml文件的根节点(<beans>),然后再来判断它的一些属性有没有配置值,如果没有则赋予默认值
这里还有最后这一步,直接点进去看一下吧
sourceExtractor = NullSourceExtractor,这里我们在之前创建XmlReaderContext对象时,已经为父类ReaderContext给赋值过了
这个方法只返回null  ( ̄ェ ̄;)

到这里populateDefaults这个方法就算是结束了,来个很有仪式感的总结吧: 设置Xml资源文件根节点某些属性默认的值

DocumentDefaultsDefinition :这个使我们传入populateDefaults方法中的this.defaults,该DocumentDefaultsDefinition 是不可变的,所以接下来的设置是有必要给记录下来的

1.DocumentDefaultsDefinition 设置lazyInit为false

2.DocumentDefaultsDefinition 设置merge为false

3.DocumentDefaultsDefinition 设置autowire为no

4.DocumentDefaultsDefinition  设置source 为null


this.readerContext.fireDefaultsRegistered(this.defaults):

调用XmlReaderContext的方法,在父类ReaderContext中找到实现


this.evenListener = EmptyReaderEventListener

先打开这个类来看看是咋样的吧

.........

    这个地方我可以确定我没有搞错,因为这个地方反正一定是从创建XmlReaderContext的时候给赋值的

我要证明自己没有搞错.....
看来事实就是这样

那这里就啥都没干....然后返回delegate....那 initDefaults方法就走完了......┓(;´_`)┏.....


开始下一步吧


namespaceUri 等于空 或者 http://www.springframework.org/schema/beans

接下来是获取beans的profile属性,并且从环境中去对比是否是spring.profiles.active对于的资源文件,这个大家应该都有用过这种方式,我简单说下吧


首先在AbstractEnvironment中找到该实现,这个地方我们主要是看这个isProfileActive这个方法

1.校验文件不为空并且不以 ! 开头

2.获取一个设置好的profile资源集合,下图中有实现方式

3.校验是否存在activeProfiles集合中,或者设置好的profile资源集合为空,校验currentActiveProfiles为空 并且 defaultProfiles集合中存在该profile(doGetDefaultProfiles方法的原理与doGetActiveProfiles一样)


如果存在于当前环境中,就把他添加进activeProfiles集合中

下面我们就写个Demo来玩玩这个profile :


在beans加个profile的属性,值就为dev吧,随便搞个值就行了


还记得我们在校验profile的时候就会判断是否为空,不为空的话就会在环境中找spring.profiles.active和spring.profiles.default 如果找到并且等于这个profile属性的值的话,如果没找到的话就直接return了

既然是在环境中找,那么我们往环境中加上这个映射不就可以了吗

没毛病

好了,再回到我们的doRegisterBeanDefinitions方法中接着往下看:

preProcessXml(root):


在开始处理bean定义之前,允许通过处理任何自定义元素类型来扩展XML。对于任何其他定制的XML预处理,此方法都是一个自然的扩展点。默认实现为空。例如,子类可以覆盖此方法,将定制元素转换为标准的Spring bean定义。实现者可以通过相应的访问器访问解析器的bean定义阅读器和底层XML资源。

parseBeanDefinitions(root, this.delegate):


这里我们主要来看这个parseDefaultElement方法,反正它本来就是进这里,哈哈哈


哈哈哈,一个一个来,这里可不能漏

1.import


解析import

一开始是判断resource属性是否为空,大家应该也都用过import,我们通常会使用import来分割规划我们的applicationContext资源文件,而resource就是指向这些分割的文件的,那当然是不能为空的(虽然很废话,但还是得说)

location = getReaderContext().getEnvironment().resolveRequiredPlaceholders(location): 从AbstractBeanDefinitionReader中获取environment,我们在看obtainFreshBeanFactory该步骤的源码中,AbstractBeanDefinitionReader的environment给设置为了StandardEnvironment对象,还有就是resolveRequiredPlaceholders这个方法我们在看setConfigLocations的源码时也说过,这个地方就是在解析我们传入的占位符(${JAVA_HOME}),大家还记得我写的那个demo吗。new ClassPathXmlApplicationContext("classpath*:applicationContext.xml;${JAVA_HOME}");

absoluteLocation = ResourcePatternUtils.isUrl(location) || ResourceUtils.toURI(location).isAbsolute() : 校验该资源路径是否是相对路径或者绝对路径

1.返回给定的资源位置是否是URL:一个特殊的“类路径”或“类路径*”伪URL或一个标准URL。(不等于null并且以classpath*:开头)

2.直接使用URL对象的isAbsolute方法来判断是否是一个绝对路径


然后又按照之前的流程走一遍,哈哈哈哈,就不用说了吧

Resource[] actResArray = actualResources.toArray(new Resource[0])

getReaderContext().fireImportProcessed(location, actResArray, extractSource(ele))

现在来看最后这个地方是在处理啥:

this.eventListener = EmptyReaderEventListener(在new XmlReaderContext时赋的值 )


之前也看过这个类,里头不做任何处理

2.alias

首先也不用说了,那就是判断alias标签的两个必须要填的属性是否为空,这个大家肯定是经常都会使用到的,就算现在没有使用过,以前肯定也是使用过的,直接进方法看实现吧

getReaderContext().getRegistry().registerAlias(name, alias):

这种图真美~

首先我们知道这里获取的reader是我们的XmlBeanDefinitionReader对象,然后我们调用了它的getRegistry方法,然后就走到了AbstractBeanDefinitionReader中,然后获取它的registry属性,这里我还是要多嘴一句,还是我们在看obtainFreshBeanFactory()方法的源码时给设置的属性,属性值为DefaultListableBeanFactory,实现方法是在SimpleAliasRegistry类中


checkForAliasCircle(name, alias):检查给定名称是否已经指向另一个方向的给定别名作为别名

this.aliasMap.put(alias, name);  添加进alias与 id 的映射

getValue是获取到Id,因为我们一个bean可以有多个别名,所以得以别名为key,id为value

字数太多,没办法发布 ==,后面的写在第二篇,感谢浏览哟~~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,905评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,140评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,791评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,483评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,476评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,516评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,905评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,560评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,778评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,557评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,635评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,338评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,925评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,898评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,142评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,818评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,347评论 2 342

推荐阅读更多精彩内容