美文网首页Java 杂谈程序员技术干货
Spring-5.1.5源码解析【IOC】(一)

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

作者: haloSky_life | 来源:发表于2019-03-20 01:23 被阅读105次

不管是自己做项目,还是工作中,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中设置resourceLoader 为PathMatchingResourcePatternResolver

2.AbstractBeanDefinitionReader中设置environment 为StandardEnvironment

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

4.设置XmlBeanDefinitionReader的ResourceLoader为AbstractXmlApplicationContext

5.设置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中包含我们资源文件的磁盘路径,重头戏要来了

/**

这里给漏了,急速更新中

**/

接着再回到AbstractRefreshableApplicationContext:

接着把DefaultListableBeanFactory赋给了自己的beanFactory属性 (这个时候心里隐隐约约感觉接下来将会有大动作  超级赛亚人要出来了! ᕙ༼ ͝°益° ༽ᕗ  )

再回到这个最初的方法:

这个地方那就是返回我们上面创建的DefaultListableBeanFactory了  ( 看来我的感觉错了,暂时还没什么大动作 ==!)

ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); 到这里这个方法就算是结束了,做个总结:

1.new AbstractBeanFactory()

2.AbstractBeanFactory中设置parentBeanFactory为ApplicationContext

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

4.AbstractBeanDefinitionReader中设置resourceLoader 为PathMatchingResourcePatternResolver

5.AbstractBeanDefinitionReader中设置environment 为StandardEnvironment

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

7.设置XmlBeanDefinitionReader的ResourceLoader为AbstractXmlApplicationContext

8.设置XmlBeanDefinitionReader的EntityResolver为ResourceEntityResolver

9.设置AbstractRefreshableApplicationContext的beanFactory属性为DefaultListableBeanFactory

10.返回DefaultListableBeanFactory


3.prepareBeanFactory(beanFactory):

一个一个来,先从第一个开始 这里我们应该是没有给DefaultResourcceLoader设置过属性的(反正我印象中是没有 ==),那我们就直接看ClassUtils.getDefaultClassLoader()这个方法吧 这个方法很简单吧,先记起来这里是从当前线程中获取classLoader

   下一步 :beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));

先来说说这两个类是啥类:

StandardBeanExpressionResolver:BeanExpressionResolver 接口的标准实现, 使用Spring的表达式模块解析和评估Spring EL

SpelParserConfiguration : SpEL表达式解析器的配置对象

SpelExpressionParser : SpEL 分析器。实例是可重用和线程安全的。

其实这种我们在之前的步骤中也有类似的,只不过在处理这块没这么高大上而已,哈哈哈哈,这里我就不细说是哪个地方了,大家可以自己再从头对着源码看一遍  >_<

我们来写个Demo看看这到底是干嘛的吧,光说的话太不清晰了

有意思的事情就这么发生了

这个可能还不怎么明显,我们再来一种玩法

注意配置文件给换了哦~

我再把这个User类给贴上就知道啥意思了 == 

这下子清晰了吧 == !

我就不介绍太多了,给大家推荐两个地址吧,哈哈哈

http://www.cnblogs.com/longronglang/p/6180023.html      

https://docs.spring.io/spring/docs/4.0.x/spring-framework-reference/html/expressions.html

下一个:

beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));   //属性编辑器

自定义属性编辑器或注册表以应用于此工厂的bean。

beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this)):

ApplicationContextAwareProcessor:该类是BeanPostProcessor接口的实现它是用来处理和回调Aware接口的Bean,不知道大家有没有这样子使用过:

写一个接口继承ApplicationContextAware接口,重写setApplicationContext方法,然后某个实现类,就可以获取到ApplicationContext对象,具体怎么玩,我们还是来写个Demo吧

写个测试接口继承ApplicationContextAware接口 呐~就是这样了

我们来看看这个方法里头是怎么做的:

考虑到安全问题,所以直接是使用CopyOnWriteArrayList这个对象 (不得不说看Spring源码,真的是学到了很多东西)

CopyOnWriteArrayList这个类的介绍给大家推荐一个很不错的地方去看:http://ifeve.com/java-copy-on-write/

1.首先是从集合中删除掉这个对象,这里要说一点注意的就是后置处理器是按照注册的顺序提交的,所以添加的这个元素得是唯一的

2.判断是否是InstantiationAwareBeanPostProcessor接口的实例(InstantiationAwareBeanPostPressor接口可以返回代理对象,例如在创建Bean时,并不都是通过反射来为我们的bean来直接new一个对象的,很多时候都是通过cglib来生成一个代理类,这样做的好处就是更方便Spring来管理这些Bean,例如在Aop的时候,代理类就发挥了很大的作用,这里我们先不细讲,到后面自然就明白了),我们在这里穿的是一个ApplicationContextAwareProcessor对象,所以这里不管

3.判断是否是DestructionAwareBeanPostProcessor接口的实例,这里也不管,因为这里也是走false(eanPostProcessor的子接口,它添加了一个before-destruction回调,销毁bean后置处理器)

4.最后把穿入的beanPostProcessor添加进后置处理器集合中。

来简单看看这个BeanPostProcessor接口吧,看看实现它的作用在哪里.

BeanPostProcessor :

ApplicationContext会自动找到我们实现了BeanPostProcessor接口的所有类,然后把这些类都注册为后置处理器

先贴一张图 == (我写的这篇可能图片,代码贴的比较多,可能不符合很多人的阅读习惯,这里先说声不好意思了,因为我觉得贴图比较亲切 ==。)

直接来写个Demo看看这个实现了这个接口到底能干嘛吧 == 

这里我可是给user设置了name的 这里我可没有设置哟  =_= 呐,就是这样

beanFactory.ignoreDependencyInterface(EnvironmentAware.class);

beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);

beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);

beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);

beanFactory.ignoreDependencyInterface(MessageSourceAware.class);

beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);

这里大家应该不陌生,因为在看obtainFreshBeanFactory()该方法的源码时,也做过这种事情 == ,就是往AbstractAutowireCapableBeanFactory中的ignoreDependencyInterfaces Set集合中添加忽略依赖关系类型

beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);

beanFactory.registerResolvableDependency(ResourceLoader.class, this):

beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this):

beanFactory.registerResolvableDependency(ApplicationContext.class, this):

注册具有相应自动获取值的特殊依赖项类型,添加进Default'List'ableBeanFactory的resolvableDependencies集合中

先记下这里是干了这件事 来看看DefaultListableBeanFactory的体系吧

beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this)):

检测实现ApplicationListener接口的bean

接着往下:

老规矩一个方法一个个来

先从transformedBeanName(name);这个方法开始

判断我们传递的name是不是以“&”开头的,要是的需要去掉再返回 ,另外我们在配置Bean时,我们是可以配置这个bean的name属性的也就是这个Bean的别名,在canonicalName方法中,我么就是通过别名来找到这个bean的ID,下面就会说到为啥要获取到Bean 的ID

if (containsSingleton(beanName) || containsBeanDefinition(beanName)) {

return (!BeanFactoryUtils.isFactoryDereference(name) || isFactoryBean(name));

}

1.首先是在DefaultSingletonBeanRegistry中判断该name是否存在singletonObjects Map属性中(singletonObjects: 缓存对象的bean的名字:bean名称到bean实例)

2.containsBeanDefinition该方法是一个抽象方法,在子类DefaultListableBeanFactory中可以找到该方法实现,该判断是判断在DefaultListableBeanFactory的be'an'DefinitionMap属性中是否存在(beanDefinitionMap: beanName与BeanDefinition对象的映射)

BeanFactory parentBeanFactory = getParentBeanFactory();

return (parentBeanFactory !=null && parentBeanFactory.containsBean(originalBeanName(name)));

我们在看obtainFreshBeanFactory()方法的源码时,在实现中已经给AbstractBeanFactory的parentBeanFactory属性设置了值,而这个值就是一开始我们传入的ApplicationContext,但是ApplicationContext并没有赋值,所以它依然还是null,那么这个方法的返回就是false

总结: beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME) : 检测 loadTimeWeaver是否存在bean实例或BeanDefinition对象

下一个:

if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {

beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());

}

transformedBeanName这个方法上面已经说了,就不重复了,就直接看看下面是干嘛的吧

(containsSingleton(beanName) || containsBeanDefinition(beanName)) &&

(!BeanFactoryUtils.isFactoryDereference(name) || isFactoryBean(beanName))

这里也是直接走的false因为初始化时这个bean是既没有实例也没有BeanDefinition的,所以这里我们就快速走完吧,其他的也不细讲了,等源码看到一定程度后,再回过头来把这些看一下写一下,效果可能会更好一点(其实我压根没看过这里的代码),既然上面已经走完了,但是我们在条件里取反,所以来看看如果不存在的情况下是对这个Bean做了什么处理吧

beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());

直接贴实现,哈哈哈哈,我越来越爱直接贴了 这里显示判断是否存在该bean 的实例,不存在则添加,存在则报错,单例嘛

那我们再来看看这个添加的方法

1.该bean添加实例

2. 删除该Bean对应的对象工厂

3.删除该Bean的早期单例对象

4.添加进注册的Bean集合

这里我们来看看假如这里不是空的会发生什么:

synchronized (this.beanDefinitionMap) {

if (!this.beanDefinitionMap.containsKey(beanName)) {

       Set updatedSingletons =new LinkedHashSet<>(this.manualSingletonNames.size() +1);   

      updatedSingletons.addAll(this.manualSingletonNames);

      updatedSingletons.add(beanName);

      this.manualSingletonNames = updatedSingletons;

  }

}else {

   if (!this.beanDefinitionMap.containsKey(beanName)) {

            this.manualSingletonNames.add(beanName);                                        // 手动注册的单例程序的名称列表

     }

}

最后还有一个clearByTypeCache()

getEnvironment()这个就不用说了吧,不用想也知道这是什么飞机,我们最开始就new了一个StandardEnvironment == ,下面两个也不说了,其实都一样了(夜深了,该睡了 == )

到这里prepareBeanFactory方法就算是完了,我们来总结一下吧:

1.从当前Thread中获取类加载器并且设置进beanFactory

2.设置表达式的解析策略

3.设置属性编辑器

4.设置后置处理器

5.添加忽略自动连接的给定依赖接口

6.从依赖项类型映射到相应的对象

            BeanFactory -> DefaultListableBeanFactory

            ResourceLoader -> AbstractApplicationContext

            ApplicationEventPublisher -> AbstractApplicationContext

            ApplicationContext -> AbstractApplicationContext

7.检测实现ApplicationListener接口的bean

8.检测 loadTimeWeaver是否存在bean实例或BeanDefinition对象    

9.检测是否存在 environment/systemProperties/systemEnvironment  的Bean实例与BeanDefinition对象,不存在则添加

还有这么多没写 == 卧槽

感觉已经写的很多很罗嗦了,还是不要都写一块比较好,不过我会尽快把剩下的写完的,哈哈哈哈,感谢大家的阅览!(不早了,该睡了,明天还得上班呢,晚安)

相关文章

网友评论

    本文标题:Spring-5.1.5源码解析【IOC】(一)

    本文链接:https://www.haomeiwen.com/subject/wtlksqtx.html