回答:
我们为什么要学习源码?
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>
运行
输出如下
com.spring.test.impl.UserServiceImpl@2aa5fe93
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 工厂发布和使用多播器效果是一样的
|