不管是自己做项目,还是工作中,Spring都是使用最多的框架。但是一直都没有好好的了解一下,本篇博客的目的就是为了深入分析Spring最核心的概念之一:IOC容器的实现原理
通常我们在获取Bean的时候,会这么去做:
//创建Spring容器
ApplicationContext ctx=new ClassPathXmlApplicationContext("classpath*:beans.xml");
//通过getBean()方法获取Bean实例
Person person=(Person) ctx.getBean("person");
我们来看看这个ClassPathXmlApplicationContext里面是怎么样的
下图是ClassPathXmlApplicationContext的继承体系:

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


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

1. super(parent):
结尾是在AbstractApplicationContext的构造方法中,来看看这个构造方法

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

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

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

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


第一步是设置给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 方法
反正这个玩意看图就知道了

到这里,setSuper(parent);这个方法就算是完了。总结一下:
1.在PathMatchingResourcePatternResolver中设置AbstractApplicationContext为resourceLoader
2.AbstractApplicationContext设置ResourcePatternResolver
3. AbstractApplicationContext 设置ApplicationContext
先不要管为啥要这样子设置,看到后面就知道了....
2. setConfigLocations(configLocations):
先不着急说什么,先把前面几个方法给贴上,反正也没几个方法













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


将MutablePropertySources 设置进PropertySourcesPropertyResolver的propertySources属性




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






总结:
1.设置AbstractApplicationContext中的 ConfigurableEnvironment environment;
2.解析我们ClassPathXmlApplicationContext传入的资源中的占位符
3、refresh()
好了,终于到了最后的refresh方法了



先从第一个方法开始看吧 ==!
1、prepareRefresh()

首先是设置AbstractApplicationContext的一些初始化变量
initPropertySources(); 在上下文环境中初始化任何占位符属性源,留给子类拓展的
这个方法主要是留给子类或者我们来自己拓展的,其实就是往AbstractPropertyResolver的 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属性对资源文件进行读取和注册。



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

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




小结:
new DefaultListableBeanFactory:
1.new AbstractBeanFactory()
2.AbstractBeanFactory中设置parentBeanFactory为ApplicationContext
3.AbstractAutowireCapableBeanFactory的忽略依赖关系接口集合属性中添加了 BeanNameAware,BeanFactoryAware,BeanClassLoaderAware
继续往下看

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

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


这里先说一下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也将相对于应用程序上下文进行解释。


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

initBeanDefinitionReader(beanDefinitionReader):


loadBeanDefinitions(beanDefinitionReader):

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

流程开始:




1. getResourceLoader(): 这一步在new XmlBeanDefinitionReader的时候,就已经把resourceLoader给赋值为PathMatchingResourcePatternResolver这个对象,在上面就总结就可以看得到
2. 因为PathMatchingResourcePatternResolver它实现了ResourcePatternResolver接口,所以这里我们直接看getResources(location)这个方法

PathMatcher : 实现Ant-style的路径模式
映射使用以下规则匹配URL:
?:匹配一个字符
* :匹配零个或多个字符
** : 匹配路径中的零个或多个目录
spring:[a-z]+}: 将regexp {[a-z]+} 作为名为“spring”的路径变量匹配

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



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

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

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

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

/**
这里给漏了,急速更新中
**/

接着再回到AbstractRefreshableApplicationContext:

再回到这个最初的方法:


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):



下一步 :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())); //属性编辑器



beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this)):
ApplicationContextAwareProcessor:该类是BeanPostProcessor接口的实现它是用来处理和回调Aware接口的Bean,不知道大家有没有这样子使用过:
写一个接口继承ApplicationContextAware接口,重写setApplicationContext方法,然后某个实现类,就可以获取到ApplicationContext对象,具体怎么玩,我们还是来写个Demo吧




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


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看看这个实现了这个接口到底能干嘛吧 ==



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集合中



beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this)):
检测实现ApplicationListener接口的bean
接着往下:

老规矩一个方法一个个来

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


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());
}

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


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

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()


到这里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对象,不存在则添加

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