title: Spring 框架源码解读2
date: 2020/04/15 15:04
本节内容 & 思考题
BeanFactory 中添加父 BeanFactory,写一个 ApplicationContext。
大家想一下 ApplicationContext 和 BeanFactory 的区别是什么?为啥要废弃 XmlBeanFactory?
组合模式:sharedInstanceCache 相当于具体的菜,parentBeanFactory 相当于菜单
模板方法:AbstractApplicationContext
最少知道原则:BF 提供了一个 isSingleton 方法,而不是直接返回 BD
观察者模式:事件
装饰者模式:AP 对 BF 进行了包装,将 ApplicationContext 注入实现了 Aware 接口的对象
修改 BeanFactory ,给他找个父亲
/** 新增 parentBeanFactory 属性,getBean时找不到去 父 BF 中找 */
public abstract class AbstractBeanFactory implements BeanFactory {
// 父 BF 为了应对多个 xml 配置文件的情况
// 这是不是组合模式啊。
private BeanFactory parentBeanFactory;
// key:名字 value:单例对象
private final Map<String, Object> sharedInstanceCache = new ConcurrentHashMap<>();
public AbstractBeanFactory() {
}
public AbstractBeanFactory(BeanFactory parentBeanFactory) {
this.parentBeanFactory = parentBeanFactory;
}
/**
* 根据名称获取对应的 bean (工厂方法模式)
*/
@Override
public Object getBean(String name) {
// -> 递归结束条件1
if (sharedInstanceCache.containsKey(name)) {
return sharedInstanceCache.get(name);
}
// 获取 bean 的属性信息
BeanDefinition beanDefinition = this.getBeanDefinition(name);
if (ObjectUtil.isNull(beanDefinition)) {
if (ObjectUtil.isNull(parentBeanFactory)) {
// -> 递归结束条件3
throw new RuntimeException("获取的bean不存在!");
} else {
// -> 这相当于递归
// 这里可以直接 return,因为已经再父 BF 中的缓存里了
return parentBeanFactory.getBean(name);
}
}
Object bean = this.createBean(beanDefinition);
// 如果该对象是单例的,则加入到缓存中。
if (beanDefinition.getScope().equals(BeanDefinition.ScopeEnum.SINGLETON)) {
sharedInstanceCache.put(name, bean);
}
// -> 递归结束条件2
return bean;
}
// 根据 bd 创建对象
private Object createBean(BeanDefinition beanDefinition) {
return ReflectUtil.newInstance(beanDefinition.getClassName());
}
/** ListableBeanFactoryImpl 新增构造方法 */
public class ListableBeanFactoryImpl extends AbstractBeanFactory implements ListableBeanFactory {
public ListableBeanFactoryImpl() {
}
public ListableBeanFactoryImpl(BeanFactory parentBeanFactory) {
super(parentBeanFactory);
}
这样父亲就加好了,为啥要加,请向下看。
手写 ApplicationContext
1、定义 ApplicationContext
ApplicationContext 继承自BeanFactory接口,除了包含BeanFactory的所有功能之外,在国际化支持、资源访问(如URL和文件)、事件传播等方面进行了良好的支持,被推荐为Java EE应用之首选,可应用在Java APP与Java Web中。
/**
* 应用上下文接口(Spring 0.9 中还继承了一个国际化相关的接口)
*
* @author yujx
* @date 2020/04/15 09:48
*/
public interface ApplicationContext extends ListableBeanFactory {
/**
* 返回父级上下文;如果没有父级,则返回null
*/
ApplicationContext getParent();
/**
* 加载或刷新配置文件(例如 XML、JSON)【资源访问】
*/
void refresh();
/**
* 触发一个事件【事件传播】
*/
void publishEvent(ApplicationEvent event);
}
2、定义一个子类 AbstractApplicationContext 定义初始化流程
流程:
- 初始化 BeanFactory,后面调用的方法都依赖于初始化的这个 BF
- 将单例对象全部初始化,如果实现了 ApplicationContextAware 则注入应用上下文(this)
- 找出容器中的所有 Listener 进行注册
- 留给子类的钩子(空实现)
- 触发应用上下文刷新完成的事件
/**
* 这个类采用了模版方法模式,定义了 ApplicationContext 的初始化流程,
* 它留下了2个方法 refreshBeanFactory() 和 getBeanFactory() 由子类实现
*
* 注意:ListableBeanFactory 接口的方法,不会考虑父容器
*
* @author yujx
* @date 2020/04/15 10:39
*/
public abstract class AbstractApplicationContext implements ApplicationContext {
// 父容器(这里地方稍后解析)
private ApplicationContext parent;
// 事件广播器
private ApplicationEventMulticaster eventMulticaster = new ApplicationEventMulticasterImpl();
/**
* 返回父级上下文;如果没有父级,则返回null
*/
@Override
public ApplicationContext getParent() {
return parent;
}
/**
* 加载或刷新配置文件(例如 XML、JSON)
* <p>
* 模版方法模式
*/
@Override
public void refresh() {
// 初始化 BF,生成 BF,好在调用 getBeanFactory() 时返回
this.refreshBeanFactory();
// 初始化单例对象,如果实现了 ApplicationContextAware 则为其注入应用上下文
this.configureAllManagedObjects();
// 初始化 Listener
this.refreshListeners();
// 留给子类在刷新时用的钩子(空实现)
this.onRefresh();
// 触发事件
this.publishEvent(new ContextRefreshedEvent(this));
}
/**
* 刷新 BF
*/
protected abstract void refreshBeanFactory();
/**
* 初始化单例对象,如果实现了 ApplicationContextAware 则为其注入应用上下文
*/
private void configureAllManagedObjects() {
String[] beanDefinitionNames = this.getBeanDefinitionNames();
for (String bdName : beanDefinitionNames) {
// 如果是单例,则对其初始化
if (this.getBeanFactory().isSingleton(bdName)) {
// 这句话会将其加入单例的 Map 中
Object bean = this.getBeanFactory().getBean(bdName);
this.configureManagedObject(bean);
}
}
}
/**
* 刷新 listener,将listener注册到事件广播器
*/
private void refreshListeners() {
Map<String, ApplicationListener> beansOfType = this.getBeansOfType(ApplicationListener.class);
for (ApplicationListener listener : beansOfType.values()) {
eventMulticaster.addApplicationListener(listener);
}
}
protected void onRefresh() {
// For subclasses: 默认情况下不执行任何操作。
}
/**
* 通知事件监听器触发了一个事件
*/
@Override
public void publishEvent(ApplicationEvent event) {
eventMulticaster.multicastEvent(event);
if (parent != null) {
parent.publishEvent(event);
}
}
// ------> ListableBeanFactory 接口的方法,不会考虑父容器(我也不知道它为啥不考虑,搞不懂)
/**
* 获取容器中所有的 bd 的 name
*/
@Override
public String[] getBeanDefinitionNames() {
return this.getBeanFactory().getBeanDefinitionNames();
}
/**
* 根据类型获取容器中所有这个类型的名字
*/
@Override
public String[] getBeanDefinitionNames(Class<?> type) {
return this.getBeanFactory().getBeanDefinitionNames(type);
}
/**
* 根据类型获取容器中所有该类型的对象
*/
@Override
public <T> Map<String, T> getBeansOfType(Class<T> type) {
Map<String, T> beansOfType = this.getBeanFactory().getBeansOfType(type);
for (T bean : beansOfType.values()) {
this.configureManagedObject(bean);
}
return beansOfType;
}
// ------> BeanFactory 接口的方法,会考虑父容器
/**
* 根据名称获取对应的 bean(工厂方法模式)
*/
@Override
public Object getBean(String name) {
Object bean = this.getBeanFactory().getBean(name);
this.configureManagedObject(bean);
return bean;
}
// 调用实现了 ApplicationContextAware 的 setApplicationContext 方法,注入应用上下文
private void configureManagedObject(Object o) {
if (o instanceof ApplicationContextAware) {
ApplicationContextAware aca = (ApplicationContextAware) o;
aca.setApplicationContext(this);
}
}
/**
* 根据名称和它的类型获取对应的 bean
*/
@Override
public <T> T getBean(String name, Class<T> requiredType) {
T bean = this.getBeanFactory().getBean(name, requiredType);
this.configureManagedObject(bean);
return bean;
}
/**
* 根据名称获取它是否是单例(直接朋友,最少知道原则)
*/
@Override
public boolean isSingleton(String name) {
return this.getBeanFactory().isSingleton(name);
}
// ------> 自定义方法,留着子类调用/实现
protected void setParent(ApplicationContext ac) {
this.parent = ac;
}
/**
* 获取 BF
*/
protected abstract ListableBeanFactory getBeanFactory();
}
3、扩展 BeanFactory 添加一个 isSingleton 方法
BeanFactory.java
/**
* 根据名称获取它是否是单例(直接朋友,最少知道原则)
*/
boolean isSingleton(String name);
AbstractBeanFactory.java 实现
/**
* 根据名称获取它是否是单例(直接朋友,最少知道原则)
*/
@Override
public boolean isSingleton(String name) {
BeanDefinition beanDefinition = this.getBeanDefinition(name);
if (ObjectUtil.isNull(beanDefinition)) {
if (ObjectUtil.isNull(parentBeanFactory)) {
throw new RuntimeException("输入的name有误!");
} else {
// 没找到就去父 bf 中找
return parentBeanFactory.isSingleton(name);
}
}
return beanDefinition.getScope() == BeanDefinition.ScopeEnum.SINGLETON;
}
AbstractApplicationContext.java
/**
* 根据名称获取它是否是单例(直接朋友,最少知道原则)
*/
@Override
public boolean isSingleton(String name) {
return this.getBeanFactory().isSingleton(name);
}
4、书写 AbstractApplicationContext 的实现
/**
* 读取 json 文件的 ApplicationContext
* <p>
* 它负责调用 AbstractApplicationContext 定义的 refresh() 方法,然后父类会调用它的 refreshBeanFactory() 方法,初始化 bean 工厂
* <p>
* 如果传入的是文件数组,那么前一个是后一个的父容器
*
* @author yujx
* @date 2020/04/15 12:39
*/
public class FileSystemJsonApplicationContext extends AbstractApplicationContext {
// 当前所在配置文件的路径
private String configLocation;
// 与当前 ApplicationContext 相关联的 BF
private ListableBeanFactory beanFactory;
public FileSystemJsonApplicationContext(String[] locations) {
// 将用户传入的数组的最后一个文件路径作为当前需要初始化的文件路径
configLocation = locations[locations.length - 1];
if (locations.length > 1) {
String[] temp = new String[locations.length - 1];
for (int i = 0; i < temp.length; i++) {
temp[i] = locations[i];
}
// 这个地方返回的是当前索引的上一个索引 new 出来的对象,所以要将这个 applicationContext 设为当前路径索引的父容器
// 假设用户传入2个文件路径,这个会返回用第二个文件创造出来的applicationContext,要将其设置为当前(也就是第一个文件创造出来的)容器的父容器
FileSystemJsonApplicationContext applicationContext = new FileSystemJsonApplicationContext(temp);
// 设置父容器的目的是为了下面 refresh() 时能够找到它,并将其作为父 BF。
super.setParent(applicationContext);
}
// 刷新容器,会调用 refreshBeanFactory() 方法
super.refresh();
}
/**
* 初始化当前 ApplicationContext 的 BF
*/
@Override
protected void refreshBeanFactory() {
beanFactory = new JsonBeanFactoryImpl(configLocation, super.getParent());
}
/**
* 获取当前 ApplicationContext 的 BF
*/
@Override
protected ListableBeanFactory getBeanFactory() {
return beanFactory;
}
}
我给大家捋一捋,假设用户会传入 2 个文件路径:new String[]{applicationContext.xml, applicationContext2.xml}
- 首先会将 configLocation 设置为 applicationContext.xml
- 判断长度是否大于 1 返回 true,进入 if 代码块,将 temp 赋值为 new String[]{applicationContext2.xml},然后"递归"的 new 了一个 FileSystemJsonApplicationContext
2.1 递归里,将 configLocation 设置为 applicationContext2.xml
2.2 判断,返回 false 则执行到了 refresh() 方法,refresh() 方法中调用了 refreshBeanFactory() 方法
2.3 refreshBeanFactory() 方法中先获取了当前 ApplicationContext 的父容器,因为这个是第一个被初始化的,所以为 null;
之后将当前的 ApplicationContext (我们称为 AP1)的 BF 设置为新 new 出来的(称为 BF1) 【AP1 中包含 BF1】
2.4 然后"递归"返回,返回了 AP1 - 将 AP1 设置为当前 AP2 的父容器【AP1 为 AP2 的父容器】
- 调用 refresh() 方法,refresh() 方法中调用了 refreshBeanFactory() 方法
- refreshBeanFactory() 方法中获取到了父容器 AP1,将 AP1 设置为当前 new 的 BF2 的父BF【BF2 的父BF 为 AP1】
- 然后将 BF2 设置为 AP2 中的 BF 属性上 【AP2 中包含 BF2】
- 结束,返回AP2(AP2 加载的是 applicationContext2.xml)
为什么BF2的父BF是AP1而不是BF1?
我一开始想的是如果BF2中没有就回去BF1那里找,找到之后就直接返回了,没有对Aware进行配置,,但看了下代码发现这个说法是错的,AP2获取到了bean之后也会对Aware进行配置,不管是不是父容器。可能原因是他们不想让ApplicationContext这个接口加一个getParentBF的方法,反正Application也只是对BF进行的包装,用谁都一样。
4、事件相关
/**
* Application 监听器(观察者模式)
*
* @author yujx
* @date 2020/04/15 11:10
*/
public interface ApplicationListener<T extends ApplicationEvent> extends EventListener {
/**
* 处理事件的方法
*/
void onApplicationEvent(T e);
/**
* 获取事件类型
*/
Class<T> getEventType();
}
/**
* 保存监听器的容器,当事件触发时调用他们
*/
public interface ApplicationEventMulticaster {
/**
* 添加事件监听器
*/
void addApplicationListener(ApplicationListener<?> l);
/**
* 删除事件监听器
*/
void removeApplicationListener(ApplicationListener<?> l);
/**
* 删除所有的事件监听器
*/
void removeAllListeners();
/**
* 广播事件
*/
void multicastEvent(ApplicationEvent event);
}
/**
* @author yujx
* @date 2020/04/15 11:20
*/
public class ApplicationEventMulticasterImpl implements ApplicationEventMulticaster {
private final Set<ApplicationListener<?>> listenerSet = new HashSet<>();
/**
* 添加事件监听器
*
* @param l
*/
@Override
public void addApplicationListener(ApplicationListener<?> l) {
listenerSet.add(l);
}
/**
* 删除事件监听器
*
* @param l
*/
@Override
public void removeApplicationListener(ApplicationListener<?> l) {
listenerSet.remove(l);
}
/**
* 删除所有的事件监听器
*/
@Override
public void removeAllListeners() {
listenerSet.clear();
}
/**
* 广播事件
*
* @param event
*/
@Override
@SuppressWarnings("all")
public void multicastEvent(ApplicationEvent event) {
// 把 Listener 的泛型擦除了,才可以调用。。Java的泛型是真恶心
for (ApplicationListener applicationListener : listenerSet) {
applicationListener.onApplicationEvent(event);
}
}
}
public abstract class ApplicationEvent extends EventObject {
private long timestamp;
public ApplicationEvent(Object source) {
super(source);
timestamp = System.currentTimeMillis();
}
public long getTimestamp() {
return timestamp;
}
}
/**
* 当ApplicationContext初始化或刷新时引发的事件。
*
* @author yujx
* @date 2020/04/15 12:29
*/
public class ContextRefreshedEvent extends ApplicationEvent {
public ContextRefreshedEvent(ApplicationContext source) {
super(source);
}
public ApplicationContext getApplicationContext() {
return (ApplicationContext) super.getSource();
}
}
5、测试
public class TestJsonAP {
public static void main(String[] args) {
FileSystemJsonApplicationContext fileSystemJsonApplicationContext = new FileSystemJsonApplicationContext(new String[]{
"/Users/x5456/IdeaProjects/Summer/src/test/resources/apple.json",
"/Users/x5456/IdeaProjects/Summer/src/test/resources/grape.json"
});
System.out.println(fileSystemJsonApplicationContext.getBean("apple"));
System.out.println(fileSystemJsonApplicationContext.getBean("grape"));
System.out.println(fileSystemJsonApplicationContext.getBeansOfType(Grape.class));
System.out.println(fileSystemJsonApplicationContext.getBeansOfType(Apple.class));
}
}
public class TestListener implements ApplicationListener<ContextRefreshedEvent> {
/**
* 处理事件的方法(每装配一个xml文件,就要调用一次)
*
* @param e
*/
@Override
public void onApplicationEvent(ContextRefreshedEvent e) {
System.out.println(e);
}
/**
* 获取事件类型
*/
@Override
public Class<ContextRefreshedEvent> getEventType() {
return ContextRefreshedEvent.class;
}
}
apple.json
[
{"name":"apple","className":"cn.x5456.summer.Apple"},
{"name":"apple2","className":"cn.x5456.summer.Apple"},
{"name":"apple3","className":"cn.x5456.summer.Apple"},
{"name":"testListener","className":"cn.x5456.summer.TestListener"}
]
grape.json
[
{"name":"grape","className":"cn.x5456.summer.Grape"}
]
Spring 0.9 中的实现
实现与我基本相同
它的 ApplicationContext 增加了对国际化的支持
其余地方基本相同
Spring 5.0 中的实现
1、refresh() 方法
我们先看 refresh 方法,先把我们自定义的 refresh 方法的流程拿过来看看:
我们的版本
- 初始化 BeanFactory,后面调用的方法都依赖于初始化的这个 BF
- 将单例对象全部初始化,如果实现了 ApplicationContextAware 则注入应用上下文(this)
- 找出容器中的所有 Listener 进行注册
- 留给子类的钩子(空实现)
- 触发应用上下文刷新完成的事件
5.0 版本
- 初始化准备工作,设置启动时间,是否激活标志位等一些配置
- 初始化 bean 工厂 (1)
- 对刚刚初始化的 bean 工厂进行一些配置,例如:类加载器、表达式解析器、bean 后置处理器、初始化一些配置等。
- 钩子方法(空) postProcessBeanFactory(bf) (允许在 ApplicationContext 子类中对bean工厂进行后置处理)
- 执行 bean 工厂后置处理器(允许用户对 bean 工厂做一些配置)
- 注册 bean 的后置处理器(在 bean 生成后,对 bean 进行一些配置)
- 国际化配置
- 初始化事件广播器 ApplicationEventMulticaster
- 钩子方法(空) (4)
- 注册监听器 (3)
- 实例化 bean (2)
- 初始化一些生命周期处理器调用他们的 onRefresh 方法,最后触发事件 (5)
后置处理器和 DI 我们以后会讲,所以这里暂不介绍,还是主要看与我们对应的那几个地方他们是怎么写的。
1)初始化 BF
将 XmlBF 换成了 DefaultListableBeanFactory ,loadBeanDefinitions() 方法也不在 DefaultListableBeanFactory 的构造中调用,改为在 refreshBeanFactory() 调用。
3)注册监听器
大体上还是相同的
2)实例化单例的 bean
5)触发事件
2、多个 xml 配置文件
在 0.9 版本中,是采用父子 BF 来实现的,但是在 Spring 5.0 中没有采用那种“递归”的方式,而是一个 for 循环将多个 xml 文件中的 bd 全部注册到了 bdMap 中。
暂时不了解父子 BF 在何时会用到。
思考题答案
BeanFactory 是 Spring 最基础的一个接口,它提供了 IoC 机制,负责生成和管理bean,为其他具体的IoC容器提供了基本的规范。
ApplicationContext 继承了 BeanFactory,在其基础上提供了更多面向应用的功能,例如:国际化、触发监听器监听的事件
BF 中的 bean 是延迟初始化,获取时才初始化。