请答复:咱们为什么要学习源码?1、知其然知其所以然
2、站在伟人的肩膀上,进步本人的编码程度
3、应酬面试
1.1 Spring 源码浏览小技巧
1、类档次藏得太深,不要一个类一个类的去看,遇到办法该进就大胆的进
2、更不要一行一行的去看,看外围点,有些办法并不重要,不要跟它纠缠
3、看不懂的先不看,依据语义和返回值能晓得这个办法达到了啥目标即可
4、只看外围接口(上面标注了重点的中央)和外围代码,有些中央兴许你应用 spring 以来都没触发过
5、debug 跟步走,源码中给大家标注好了,见到”===>“就进去
进去之前,下一行打个断点,不便疾速回到岔路口
进去之前,能够先点办法看源码,再 debug 跟进
6、广度优先,而非深度优先。先沿着主流程走,理解大略,再细化某些办法
7、认命。spring 里多少万行的代码,一部书都写不完。只能学关键点
浏览源码目标
加深了解 spring 的 bean 加载过程
面试吹牛 x
江湖传说,spring 的类关系是这样的……
1.2 IoC 初始化流程与继承关系
引言
在看源码之前须要把握 Spring 的继承关系和初始化
1) IoC 容器初始化流程
指标:
1、IoC 容器初始化过程中到底都做了哪些事件(宏观指标)
2、IoC 容器初始化是如何实例化 Bean 的(划重点,最终目标)
// 没有 Spring 之前咱们是这样的
User user=new User();
user.xxx();
// 有了 Spring 之后咱们是这样的
<bean id="userService" class="com.spring.test.impl.UserServiceImpl">
User user= context.getBean("xxx");
user.xxx();
IoC 流程简化图:
tips:
上面的流转记不住没有关系
在分析源码的整个过程中,咱们始终会拿着这个图和源码对照
初始化:
1、容器环境的初始化
2、Bean 工厂的初始化(IoC 容器启动首先会销毁旧工厂、旧 Bean、创立新的工厂)
读取与定义
读取:通过 BeanDefinitonReader 读取咱们我的项目中的配置(application.xml)
定义:通过解析 xml 文件内容,将外面的 Bean 解析成 BeanDefinition(未实例化、未初始化)
实例化与销毁
Bean 实例化、初始化(注入)
销毁缓存等
扩大点
事件与多播、后置处理器
简单的流程关键点:
重点总结:
1、工厂初始化过程
2、解析 xml 到 BeanDefinition,放到 map
3、调用后置处理器
4、从 map 取出进行实例化(ctor.newInstance)
5、实例化后放到一级缓存(工厂)
2) 容器与工厂继承关系
tips:
别缓和,上面的继承记不住没有关系
关注色彩标注的几个就能够
指标:简略了解 ioC 容器继承关系
继承关系了解:
1、ClassPathXmlApplicationContext最终还是到了 ApplicationContext 接口,同样的,咱们也能够应用绿色彩的 FileSystemXmlApplicationContext 和 AnnotationConfigApplicationContext 这两个类实现容器初始化的工作
2、FileSystemXmlApplicationContext 的构造函数须要一个 xml 配置文件在零碎中的门路,其余和 ClassPathXmlApplicationContext 基本上一样
3、AnnotationConfigApplicationContext 的构造函数扫描 classpath 中相干注解的类,主流程一样
课程中咱们以最经典的 classpathXml 为例。
Bean 工厂继承关系
指标:
ApplicationContext 和 BeanFactory 啥关系?
BeanFactory 和 FactoryBean 呢?
总结:
别胆怯,下面的继承关系不必刻意去记住它
其实接触到的就最上面这个!
1.3 开始搭建测试项目
四步:
1、新建测试 module 我的项目
首先咱们在 Spring 源码我的项目中新增一个测试项目,点击 New -> Module… 创立一个 Gradle 的 Java 我的项目
2、详细信息
3、设置 gradle
4、欠缺信息
在 build.gradle 中增加对 Spring 源码的依赖:
compile(project(':spring-context'))
spring-context 会主动将 spring-core、spring-beans、spring-aop、spring-expression 这几个根底 jar 包带进来。
接着,咱们须要在我的项目中创立一个 bean 和配置文件(application.xml)及启动文件(Main.java)
接口如下:
package com.spring.test.service;
public interface UserService {public String getName();
}
实现类
package com.spring.test.impl;
import com.spring.test.service.UserService;
public class UserServiceImpl implements UserService {
@Override
public String getName() {return "Hello World";}
}
Main 代码如下
public class Test {public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("classpath*:application.xml");
UserService userService = context.getBean(UserService.class);
System.out.println(userService);
// 这句将输入: hello world
System.out.println(userService.getName());
}
}
配置文件 application.xml(在 resources 中)配置如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userService" class="com.spring.test.impl.UserServiceImpl"/>
</beans>
运行
输入如下
[email protected]
Hello World
1.4 工厂的构建
引言:接下来,咱们就正式解说 Spring ioC 容器的源码
咱们的目标:看一下 ioC 如何帮咱们生成对象的
生命周期
1)ApplicationContext 入口
参考 IocTest.java
测试代码:spring 反对多种 bean 定义形式,为不便大家了解构造,以 xml 为案例,前面的解析流程统一
ApplicationContext context = new ClassPathXmlApplicationContext("classpath*:${xmlName}.xml");
//(c)从容器中取出 Bean 的实例,call:AbstractApplicationContext.getBean(java.lang.Class<T>)
// 工厂模式(simple)
UserService userService = (UserService) context.getBean("userServiceBeanId");
// 这句将输入: hello world
System.out.println(userService.getName());
进入到 ClassPathXmlApplicationContext 的有参结构器
org.springframework.context.support.ClassPathXmlApplicationContext#ClassPathXmlApplicationContext(java.lang.String[], boolean, org.springframework.context.ApplicationContext)
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
throws BeansException {
// 继承结构图
//1、返回一个 classloader
//2、返回一个解析器
super(parent);
// 1、获取环境(零碎环境、jvm 环境)// 2、设置 Placeholder 占位符解析器
// 2、将 xml 的门路解析完存储到数组
setConfigLocations(configLocations);
// 默认为 true
if (refresh) {
// 外围办法(模板)refresh();}
}
重点步骤解析(断点跟踪解说)
super 办法做了哪些事件
1、super 办法:通过点查看父容器与子容器概念
2、super 办法:调用到顶端,一共 5 层,每一层都要与讲义中的【ioC 与 Bean 工厂类关系继承】进行对照
3、super 办法:在什么中央初始化的类加载器和解析器
setConfigLocations 办法做了哪些事件:1、如何返回的零碎环境和 jvm 环境
2、门路的解析
3、设置占位符解析器
进入外围办法 refresh
2)预刷新
prepareRefresh()【筹备刷新】
// synchronized 块锁(monitorenter --monitorexit),不然 refresh() 还没完结,又来个启动或销毁容器的操作
synchronized (this.startupShutdownMonitor) {
//1、【筹备刷新】【Did four things】prepareRefresh();
......。略
解说重点(断点跟踪、类继承关系、架构图解说)
prepareRefresh 干了哪些事件
//1、记录启动工夫 / 设置开始标记
//2、子类属性扩大(模板办法)//3、校验 xml 配置文件
//4、初始化晚期公布的应用程序事件对象(不重要,仅仅是创立 setg 对象)
3)创立 bean 工厂【重点】
【取得新的 bean 工厂】obtainFreshBeanFactory()
最终目标就是解析 xml,注册 bean 定义
关键步骤
//1、敞开旧的 BeanFactory
//2、创立新的 BeanFactory(DefaluListbaleBeanFactory)//3、解析 xml/ 加载 Bean 定义、注册 Bean 定义到 beanFactory(未初始化)
//4、返回全新的工厂
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
4)bean 工厂前置操作
【筹备 bean 工厂】prepareBeanFactory(beanFactory);
//1、设置 BeanFactory 的类加载器
//2、设置 BeanFactory 的表达式解析器
//3、设置 BeanFactory 的属性编辑器
//4、智能注册
tips
以后代码逻辑简略、且非核心
5)bean 工厂后置操作
【后置处理器 Bean 工厂】postProcessBeanFactory(beanFactory) 空办法
tips:子类实现
空办法,跳过
6)工厂后置处理器【重点】
【调用 bean 工厂后置处理器】invokeBeanFactoryPostProcessors(beanFactory);
// 调用程序一:bean 定义注册后置处理器
// 调用程序二:bean 工厂后置处理器
PostProcessorRegistrationDelegate 类里有具体注解
tips
invoke 办法近 200 行
关注两类后置处理器的办法执行步骤和程序
7)bean 后置处理器
【注册 bean 后置处理器】registerBeanPostProcessors(beanFactory)
//6、【注册 bean 后置处理器】只是注册,然而不会反射调用
// 性能:找出所有实现 BeanPostProcessor 接口的类, 分类、排序、注册
registerBeanPostProcessors(beanFactory);
// 外围:查看重要的 3 步;最终目标都是实现 bean 后置处理器的注册
// 第一步:implement PriorityOrdered
// 第二步:implement Ordered.
// 第三步:Register all internal BeanPostProcessors.
8)国际化
【初始化音讯源】国际化问题 i18n initMessageSource();
tips:
就加了个 bean 进去,非核心步骤,跳过
9)初始化事件播送器
【初始化应用程序事件多路播送】initApplicationEventMulticaster();
tips:
须要解说观察者设计模式
重点:就放了个 bean 进去,到上面的 listener 再联调。
10)刷新
【刷新】 onRefresh();
空的,交给子类实现:默认状况下不执行任何操作
// 具体的子类能够在这里初始化一些非凡的 Bean(在初始化 singleton beans 之前)onRefresh();
不重要,跳过
11)注册监听器【重点】
【注册所有监听器】registerListeners();
测试代码参考:MulticastTest
// 获取所有实现了 ApplicationListener,而后进行注册
//1、汇合 applicationListeners 查找
//2、bean 工厂找到实现 ApplicationListener 接口的 bean
//3、this.earlyApplicationEvents;
tips:
须要解说观察者设计模式
重点:演示多播和容器公布
12)实现 bean 工厂【重点】
【实现 bean 工厂初始化操作】finishBeanFactoryInitialization(beanFactory);
//【实现 bean 工厂初始化操作】负责初始化所有的 singleton beans
// 此处开始调用 Bean 的前置处理器和后置处理器
finishBeanFactoryInitialization(beanFactory);
解说重点(断点跟踪、类继承关系、架构图解说)
//1、设置辅助器:例如:解析器、转换器、类装载器
//2、实例化
//3、填充
//4、调用前置、后置处理器
// 外围代码在 getBean() , 上面独自解说
13)实现刷新
【实现刷新】
protected void finishRefresh() {
// 1、革除上下文级资源缓存
clearResourceCaches();
// 2、LifecycleProcessor 接口初始化
// ps:当 ApplicationContext 启动或进行时,它会通过 LifecycleProcessor 来与所有申明的 bean 的周期做状态更新
// 而在 LifecycleProcessor 的应用前首先须要初始化
initLifecycleProcessor();
// 3、启动所有实现了 LifecycleProcessor 接口的 bean
//DefaultLifecycleProcessor, 默认实现
getLifecycleProcessor().onRefresh();
// 4、公布上下文刷新结束事件到相应的监听器
//ps:当实现容器初始化的时候,// 要通过 Spring 中的事件公布机制来收回 ContextRefreshedEvent 事件,以保障对应的监听器能够做进一步的逻辑解决
publishEvent(new ContextRefreshedEvent(this));
// 5、把以后容器注册到到 MBeanServer,用于 jmx 应用
LiveBeansView.registerApplicationContext(this);
}
tips:
非核心步骤
2 singleton bean 创立【重点】
上面拎进去,重点讲 getBean 办法。
参考代码:
先看没有循环依赖的状况,一般单例 bean 的初始化 SinigleTest.java
前面再讲循环依赖
1)调用入口
大家都晓得是 getBean()办法,然而这个办法要留神,有很多调用机会
如果你把断点打在了这里,再点进去 getBean,你将会间接从 singleton 汇合中拿到一个实例化好的 bean
无奈看到它的实例化过程。
能够 debug 试一下。会发现间接从 getSingleTon 返回了 bean,这不是咱们想要的模样……
思考一下,为什么呢?
回顾 1.4 中的第 12 大节,在 bean 工厂实现后,会对 singleton 的 bean 实现初始化,那么真正的初始化应该产生在那里!
那就须要找到:DefaultListableBeanFactory 的第 809 行,那里的 getBean
也能够从 1.4 的第 12 大节的入口跟进去。断点打在这里试试:
这也是咱们在下面留下的尾巴。
本大节咱们从这里持续……
2)主流程
小 tip:先搞革除 3 级缓存的事
对于 bean 的三级缓存:DefaultSingletonBeanRegistry 代码
/**
* 一级缓存:单例(对象)池,这外面的对象都是确保初始化实现,能够被失常应用的
* 它可能来自 3 级,或者 2 级
*/
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/**
* 三级缓存:单例工厂池,这外面不是 bean 自身,是它的一个工厂,将来调 getObject 来获取真正的 bean
* 一旦获取,就从这里删掉,进入 2 级(产生闭环的话)或 1 级(没有闭环)*/
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
/**
* 二级缓存:晚期(对象)单例池,这外面都是半成品,只是有人用它提前从 3 级 get 进去,把援用裸露进来
* 它外面的属性可能是 null,所以叫晚期对象,early!半成品
* 将来在 getBean 付完属性后,会调 addSingleton 清掉 2 级,正式进入 1 级
*/
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
传统叫三级缓存里拿 bean,其实就是仨 map
严格意义上,只有 single 一级缓存,其余俩基本算不上是缓存
他们只是在生成 bean 的过程中,暂存过 bean 的半成品。
就那么称说,不用较真
主流程图很重要!前面的 debug 会带着这张图走
getBean:入口
doGetBean:调 getSingleton 查一下缓存看看有没有,有就返回,没有给 singleton 一个 lambda 表达式,函数式编程里调上面的 createBean 拿到新的 bean,而后革除 3 级缓存,放入 1 级缓存
createBean:调这里。一堆查看后,进入上面
doCreateBean:真正创立 bean 的中央:调构造函数初始化 - 放入 3 级缓存 - 解析属性赋值 - bean 后置处理器
3)getSingleton
在 DefaultSingletonBeanRegistry 里,有三个,作用齐全不一样
// 啥也没干,调上面传了个 true
public Object getSingleton(String beanName)
// 从 1 级缓存拿,1 级没有再看状况
// 前面的参数如果 true,就应用 3 级升 2 级返回,否则间接返回 null
protected Object getSingleton(String beanName, boolean allowEarlyReference)
// 1 级没有,通过给的 factory 创立并放入 1 级里,革除 2、3
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory)
4)bean 实例化
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBeanInstance
5)放入三级缓存
循环依赖和 aop
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#addSingletonFactory
4)注入属性
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean
真正给 bean 设置属性的中央!
7)bean 前后置
还记得下面咱们自定义的 Bean 后置处理器吗
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean
前、后置的调用,在这里
具体见下图第 3 步,很多,理解即可,须要时查一下在相干中央扩大
8)小结
伪代码,无循环依赖时,生成 bean 流程一览
getBean("A"){doGetBean("A"){a = getSingleton("A"){a = singletonObjects(); // 查 1 级缓存,null
if("创立过 3 级缓存"){ // 不成立
// 疏忽
}
return a;
}; // null
if(a == null){a = getSingleton("A" , ObjectFactory of){a = of.getObject() -> { //lambda 表达式
createBean("A"){doCreateBean("A"){createBeanInstance("A"); // A 实例化
addSingletonFactory("A"); // A 放入 3 级缓存
populateBean("A"); // A 注入属性
initializeBean("A"); // A 后置处理器
} //end doCreateBean("A")
} //end crateBean("A")
} // end lambda A
addSingleton("A" , a) // 革除 2、3 级,放入 1 级
} // end getSingleton("A",factory)
} // end if(a == null)
return a;
} //end doGetBean("A")
}//end getBean("A")
3 Spring 的循环依赖
引言
在下面,咱们分析了 bean 实例化的整个过程
也就是咱们的 Bean 他是独自存在的,和其余 Bean 没有交加和援用
而咱们在业务开发中,必定会有多个 Bean 互相援用的状况
也就是所谓的循环依赖
3.1 什么是循环依赖
简略回顾下
艰深的讲就是 N 个 Bean 相互援用对方,最终造成 闭环。
我的项目代码介绍如下(测试类入口:CircleTest.java)
配置文件
<!-- 循环依赖 BeanA 依赖 BeanB -->
<bean id="userServiceImplA" class="com.spring.test.impl.UserServiceImplA">
<property name="userServiceImplB" ref="userServiceImplB"/>
</bean>
<!-- 循环依赖 BeanB 依赖 BeanA -->
<bean id="userServiceImplB" class="com.spring.test.impl.UserServiceImplB">
<property name="userServiceImplA" ref="userServiceImplA"/>
</bean>
userServiceImplA 代码如下
public class UserServiceImplA implements UserService {
private UserServiceImplB userServiceImplB;
public void setUserServiceImplB(UserServiceImplB userServiceImplB) {this.userServiceImplB = userServiceImplB;}
@Override
public String getName() {
return "在 UserServiceImplA 的 Bean 中" +
"userServiceImplB 注入胜利 >>>>>>>>>"+userServiceImplB;
}
}
userServiceImplB 代码如下
// 实现类
public class UserServiceImplB implements UserService {
private UserServiceImplA userServiceImplA;
public void setUserServiceImplA(UserServiceImplA userServiceImplA) {this.userServiceImplA = userServiceImplA;}
@Override
public String getName() {
return "在 UserServiceImplB 的 Bean 中" +
"userServiceImplA 注入胜利 >>>>>>>>>"+userServiceImplA;
}
入口 Main
ApplicationContext context = new ClassPathXmlApplicationContext("classpath*:application.xml");
UserService userService = context.getBean("userServiceImplA",UserService.class);
System.out.println(userService.getName());
输入如下
3.2 Spring 如何解决循环依赖
如果无奈解决循环依赖
1、Bean 无奈胜利注入,导致业务无奈进行
2、产生死循环(一种假如情景)
1)三级缓存变动过程
指标:
只有明确三级缓存变动过程,能力晓得是如何解决循环依赖的
略去其余步骤,只看缓存变动
变动过程 3 -1: 如下图:
步骤:
A:… – 走到 doCreateBean: 初始化 – 进 3 级缓存 – 注入属性,发现须要 B
B:… – 走到 doCreateBean: 初始化 – 进 3 级缓存
1、BeanA 经验 gdcd 四个办法,走到 doCreatebean 里在实例化后、注入前放到三级缓存
2、放到三级缓存后;BeanA 在正式的注入的时候,发现有循环依赖,反复上【1】的步骤
3、最终:BeanA 和 BeanB 都放到了三级缓存
变动过程 3 -2: 如下图:
步骤:
1、BeanB 放到三级缓存后,这个时候 BeanB 要开始注入了;
于是,BeanB 找到了循环依赖 BeanA 后,再从头执行 A 的 getBean 和 doGetBean 办法;
此处在 getSingleton 外面(这货第一次是必经的,但第二次来行为不一样了)将 BeanA 设置到了二级缓存,并且把 BeanA 从三级缓存移除走了
2、BeanB 如愿以偿的拿到了 A,注入,此时,实现了注入过程;始终到 DefaultSingletonBeanRegistry#addSingleton 办法后;BeanB 从三级缓存间接进入一级缓存,实现它的使命
3、目前,一级缓存有 BeanB(外面的 BeanA 属性还是空)、二级缓存有 BeanA 三级缓存为空
成果如下
走到这一步,B 外面有 A,它已实现。
然而很可怜,A 外面的 B 还是 null,咱们第三步会持续实现这个设置
思考一下:
如果不必三级,咱们间接用 2 级也能实现,然而 3 级咱们说它是一个 Factory,外面能够在创立的前后嵌入咱们的代码,和前后置处理器,Aop 之类的操作就产生在这里
而 2 级寄存的是 bean 实例,没这么多扩大的可能性,如果仅仅用于 bean 循环创立,倒是能够
总结:
1、如果不调用后置,返回的 bean 和三级缓存一样
2、如果调用后置,返回的就是代理对象
3、这就是三级缓存设计的奇妙之处!!!!Map<String, ObjectFactory<?>>
变动过程 3 -3: 如下图:
步骤:
此时, BeanB 外面曾经注入了 BeanA,它本人实现并进入了一级缓存
要留神,它的实现是被动的后果,也就是 A 须要它,长期先腾出工夫创立了它
接下来,BeanA 还要持续本人的流程,而后 populateBean 办法将 BeanB 注入到本人里
最初,BeanA 进一级缓存,删除之前的二级
整个流程实现!
功败垂成:单方互相持有对方成果如下:
2)三级缓存解决方案总结
简化版
序列图
三级缓存解决循环依赖过程(回顾)
1、BeanA 通过 gdcd 办法、放入到 3 级缓存、如果有循环依赖 BeanB,反复执行 gdcd 办法
2、直到发现了它也须要 A,而 A 后面经验了一次 get 操作,将 3 级缓存的 BeanA 放到 2 级缓存
3、而后 2 级缓存的 A 注入进 BeanB, BeanB 完事进一级缓存,此时 BeanB 持有 BeanA
3、接下来,持续实现 BeanA 剩下的操作,取 BeanB 填充进 BeanA,将 BeanA 放到一级缓存,实现!
伪代码,循环依赖流程一览,都是关键步骤,不能再简化了
倡议粘贴到 vscode 等编辑器里查看,因为……它层级太 tmd 深了!
getBean("A"){doGetBean("A"){a = getSingleton("A"){a = singletonObjects(); // 查 1 级缓存,null
if("创立过 3 级缓存"){ // 不成立
// 疏忽
}
return a;
}; // A 第一次,null
if(a == null){a = getSingleton("A" , ObjectFactory of){a = of.getObject() -> { //lambda 表达式
createBean("A"){doCreateBean("A"){createBeanInstance("A"); // A 实例化
addSingletonFactory("A"); // A 放入 3 级缓存
populateBean("A"){
//A 须要 B,进入 B 的 getBean
b = getBean("B"){doGetBean("B"){b = getSingleton("B"); // B 第一次,null
if(b == null){b = getSingleton("B", ObjectFactory of){b = of.getObject() -> {createBean("B"){doCreateBean("B"){createBeanInstance("B"); // B 实例化
addSingletonFactory("B"); // B 放入 3 级缓存
populateBean("B"){
//B 须要 A,2 次进入 A 的 getBean
a = getBean("A"){doGetBean("A"){a = getSingleton("A"){a = singletonObjects(); // 查 1 级缓存,null
if("创立过 3 级缓存"){ // 成立!a = singletonFactory.getObject("A"); // 取 3 级缓存,生成 a
earlySingletonObjects.put("A", a); // 放入 2 级缓存
singletonFactories.remove("A"); // 移除 3 级缓存
return a;
}
}; // A 第二次,不是 null,然而半成品,还待在 2 级缓存里
} // end doGetBean("A")
} // end getBean("A")
} // end populate B
initializeBean("B",b); // B 后置处理器
} // end doCreateBean B
} // end createBean B
} // end lambda B
// B 创立实现,并且是残缺的,尽管它外面的 A 还是半成品,但不影响它进入 1 级
addSingleton("B",b) ; // 革除 3 级缓存,进入 1 级
); // end getSingleton("B",factory)
} // end if(b==null);
return b;
} // end doGetBean("B")
} // end getBean("B")
} // end populateBean("A")
initializeBean("A"); // A 后置处理器
} //end doCreateBean("A")
} //end crateBean("A")
} // end lambda A
addSingleton("A" , a) // 革除 2、3 级,放入 1 级
} // end getSingleton("A",factory)
} // end if(a == null)
return a;
} //end doGetBean("A")
}//end getBean("A")
总结
能够发现,通过 spring 的三级缓存完满解决了循环依赖
Spring 解决机制很聪慧;它先扫描一遍 Bean,先放到一个容器(3 级缓存待命)
此时也不晓得是否存在循环依赖,先放到三级缓存再说
等到设置属性的时候,取对应的属性 bean 去(此时才发现有了循环依赖),在放到第二个容器(2 级缓存,半成品)
持续,而后从二级缓存拿出进行填充(注入)
填充结束,将本人放到一级缓存(这个 bean 是被动创立进去的,因为他人须要它,后果它先实现了)
而后一直循环外层,解决最原始要创立的那个 bean
为什么设计三级?二级缓存是否解决循环依赖?
能够解决。别说 2 级,1 级都行
尽管二级缓存能解决循环依赖,然而 aop 时会可能会引发问题,三级是一个 factory,在外面装备了对应的后置处理器,其中就有咱们的 aop(前面会讲到),如果有人要用它,会在调用 factory 的 getObject 时失效,生成代理 bean 而不是原始 bean。
如果不这么做,间接创立原始对象注入,可能引发 aop 生效。
所以 spring 的 3 级各有意义:
1 级:最终成品
2 级:半成品
3 级:工厂,备用
在下面的办法 getEarlyBeanReference(提前裸露的援用)
回顾下
AbstractAutowireCapableBeanFactory.getEarlyBeanReference
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
// 循环所有 Bean 后置处理器
for (BeanPostProcessor bp : getBeanPostProcessors()) {if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
// 重点:开始创立 AOP 代理
exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
}
}
}
总结下:
1、如果不调用后置处理器,返回的 Bean 和三级缓存一样, 都是实例化、一般的 Bean
2、如果调用后置,返回的就是代理对象,不是一般的 Bean 了
其实;这就是三级缓存设计的奇妙之处
那为什么要 2 级呢?不能间接放入 1 级吗?
不能!
A-B- A 中,第二次 A 的时候,A 还是个半成品,不能放入 1 级
以下面为例,A 在进入 2 级缓存的时候,它外面的 B 还是个 null!
如果放入 1 级,被其余应用的中央取走,会引发问题,比方空指针
4 IoC 用到的那些设计模式
引言:Spring 中应用了大量的设计模式(面试)
4.1 工厂
工厂模式(Factory Pattern)提供了一种创建对象的最佳形式。
工厂模式(Factory Pattern)分为三种
1、简略工厂
2、工厂办法
3、形象工厂
1. 简略工厂模式
ApplicationContext context =
new ClassPathXmlApplicationContext("classpath*:application.xml");\
UserService userService = context.getBean(UserService.class);
简略工厂模式对对象创立治理形式最为简略,因为其仅仅简略的对不同类对象的创立进行了一层简略的封装
定义接口 IPhone
public interface Phone {void make();
}
实现类
public class IPhone implements Phone {public IPhone() {this.make();
}
@Override
public void make() {
// TODO Auto-generated method stub
System.out.println("生产苹果手机!");
}
}
实现类
public class MiPhone implements Phone {public MiPhone() {this.make();
}
@Override
public void make() {
// TODO Auto-generated method stub
System.out.println("生产小米手机!");
}
}
定义工厂类并且测试
public class PhoneFactory {public Phone makePhone(String phoneType) {if (phoneType.equalsIgnoreCase("MiPhone")) {return new MiPhone();
} else if (phoneType.equalsIgnoreCase("iPhone")) {return new IPhone();
}
return null;
}
// 测试简略工厂
public static void main(String[] arg) {PhoneFactory factory = new PhoneFactory();
Phone miPhone = factory.makePhone("MiPhone");
IPhone iPhone = (IPhone) factory.makePhone("iPhone");
}
}
4.2 模板
模板模式 (Template Pattern): 基于抽象类的,外围是封装算法
Spring 外围办法 refresh 就是典型的模板办法
org.springframework.context.support.AbstractApplicationContext#refresh
模板设计模式—
模板办法定义了一个算法的步骤,并容许子类为一个或多个步骤提供具体实现
// 模板模式
public abstract class TemplatePattern {protected abstract void step1();
protected abstract void step2();
protected abstract void step3();
protected abstract void step4();
// 模板办法
public final void refresh() {// 此处也可退出以后类的一个办法实现,例如 init()
step1();
step2();
step3();
step4();}
}
定义子类
// 模板模式
public class SubTemplatePattern extends TemplatePattern {
@Override
public void step1() {System.out.println(">>>>>>>>>>>>>>1");
}
@Override
public void step2() {System.out.println(">>>>>>>>>>>>>>2");
}
@Override
public void step3() {System.out.println(">>>>>>>>>>>>>>3");
}
@Override
public void step4() {System.out.println(">>>>>>>>>>>>>>4");
}
// 测试
public static void main(String[] args) {TemplatePattern tp = new SubTemplatePattern();
tp.refresh();}
}
输入
4.3 观察者
什么是观察者模式
观察者模式(Observer Pattern):当对象间存在一对多关系时,则应用观察者模式(Observer Pattern)。比方,当一个对象被批改时,则会主动告诉依赖它的对象。
Spring 的事件机制就是具体的观察者模式的实现
spring 中的多播与事件
AbstractApplicationContext#initApplicationEventMulticaster
AbstractApplicationContext#registerListeners
观察者模式有哪些角色?
事件 ApplicationEvent
是所有事件对象的父类,继承 JDK 的 EventObject
事件监听 ApplicationListener
,也就是 观察者对象,继承自 JDK 的 EventListener
, 能够监听到事件;该类中只有一个办法 onApplicationEvent
。当监听的事件产生后该办法会被执行。
事件公布ApplicationContext
,实现事件的公布。
(公布事件)
========================or=================================
Spring 中的多播
事件公布 ApplicationEventMulticaster
,用于事件监听器的注册和事件的播送。
自定义一个事件 MessageSourceEvent 并且实现 ApplicationEvent 接口
// 在 Spring 中应用事件监听机制(事件、监听、公布)
// 定义事件
// 执行程序
//1、进入到事件源的有参数结构器
//2、公布事件
//3、进入到监听器类 ---one
//4、进入到事件源的办法
//5、进入到监听器类 ---two
//6、进入到事件源的办法
public class MessageSourceEvent extends ApplicationEvent {public MessageSourceEvent(Object source) {super(source);
System.out.println("进入到事件源的有参数结构器");
}
public void print() {System.out.println("进入到事件源的办法");
}
}
有了事件之后还须要自定义一个监听用来接管监听到事件,自定义 ApplicationContextListener 监听 须要交给 Spring 容器治理,实现 ApplicationListener 接口并且重写 onApplicationEvent 办法,
监听一
// 在 Spring 中应用事件监听机制(事件、监听、公布)
// 监听类,在 spring 配置文件中, 注册事件类和监听类
public class ApplicationContextListener implements ApplicationListener {
@Override
public void onApplicationEvent(ApplicationEvent event) {if (event instanceof MessageSourceEvent) {System.out.println("进入到监听器类 ---one");
MessageSourceEvent myEvent = (MessageSourceEvent) event;
myEvent.print();}
}
}
监听二
// 在 Spring 中应用事件监听机制(事件、监听、公布)
// 监听类,在 spring 配置文件中, 注册事件类和监听类
public class ApplicationContextListenerTwo implements ApplicationListener {
@Override
public void onApplicationEvent(ApplicationEvent event) {if(event instanceof MessageSourceEvent){System.out.println("进入到监听器类 ---two");
MessageSourceEvent myEvent=(MessageSourceEvent)event;
myEvent.print();}
}
}
公布事件
// 在 Spring 中应用事件监听机制(事件、监听、公布)
// 该类实现 ApplicationContextAware 接口, 失去 ApplicationContext 对象
// 应用该对象的 publishEvent 办法公布事件
public class ApplicationContextListenerPubisher implements ApplicationContextAware {
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {this.applicationContext = applicationContext;}
public void publishEvent(ApplicationEvent event) {System.out.println("公布事件");
applicationContext.publishEvent(event);
}
}
配置文件
<!-- Spirng 中的事件 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> -->
<!--<bean id="messageSourceEvent" class="com.spring.test.pattern.observer.MessageSourceEvent" />-->
<bean id="applicationContextListener" class="com.spring.test.pattern.observer.ApplicationContextListener"/>
<bean id="applicationContextListenerTwo" class="com.spring.test.pattern.observer.ApplicationContextListenerTwo"/>
<bean id="applicationContextListenerPubisher" class="com.spring.test.pattern.observer.ApplicationContextListenerPubisher"/>
<!-- Spirng 中的事件 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> -->
测试
// 总结:应用 bean 工厂公布和应用多播器成果是一样的
public class Test {public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("classpath*:application.xml");
//*************** 应用 spring 的多播器公布 **********************
ApplicationEventMulticaster applicationEventMulticaster = (ApplicationEventMulticaster) context.getBean("applicationEventMulticaster");
applicationEventMulticaster.multicastEvent(new MessageSourceEvent("测试..."));
//*************** 应用 BeanFactory 的 publishEvent 公布 *********************
// ApplicationContextListenerPubisher myPubisher = (ApplicationContextListenerPubisher)
//context.getBean("applicationContextListenerPubisher");
//myPubisher.publishEvent(new MessageSourceEvent("测试..."));
}
}
多播公布
工厂公布
总结:
1、spring 的事件驱动模型应用的是 观察者模式
2、通过 ApplicationEvent 抽象类和 ApplicationListener 接口, 能够实现事件处理
3、ApplicationEventMulticaster 事件播送器实现了监听器的注册, 个别不须要咱们实现, 只须要显示的调用 applicationcontext.publisherEvent 办法即可
4、应用 bean 工厂公布和应用多播器成果是一样的
如果本文对您有帮忙,欢送
关注
和点赞
`,您的反对是我保持创作的能源。转载请注明出处!