review一下之前写的内容,修正一些小瑕疵

pull/28/head
AmyliaY 5 years ago
parent 6951a5b6ce
commit b31168adfe

@ -22,7 +22,7 @@
- [Spring AOP 如何生效(Spring AOP标签解析)](/docs/Spring/AOP/Spring-Aop如何生效.md)
### SpringMVC
- [温习一下servlet](/docs/Spring/SpringMVC/温习一下servlet.md)
- [温习一下 servlet](/docs/Spring/SpringMVC/温习一下servlet.md)
- [IoC 容器在 Web 环境中的启动](/docs/Spring/SpringMVC/IoC容器在Web环境中的启动.md)
- [SpringMVC 的设计与实现](/docs/Spring/SpringMVC/SpringMVC的设计与实现.md)
- [SpringMVC 跨域解析](/docs/Spring/SpringMVC/SpringMVC-CROS.md)
@ -30,10 +30,10 @@
努力编写中...
### Spring事务
- [Spring与事务处理](/docs/Spring/SpringTransaction/Spring与事务处理.md)
- [Spring声明式事务处理](/docs/Spring/SpringTransaction/Spring声明式事务处理.md)
- [Spring事务处理的设计与实现](/docs/Spring/SpringTransaction/Spring事务处理的设计与实现.md)
- [Spring事务管理器的设计与实现](/docs/Spring/SpringTransaction/Spring事务管理器的设计与实现.md)
- [Spring 与事务处理](/docs/Spring/SpringTransaction/Spring与事务处理.md)
- [Spring 声明式事务处理](/docs/Spring/SpringTransaction/Spring声明式事务处理.md)
- [Spring 事务处理的设计与实现](/docs/Spring/SpringTransaction/Spring事务处理的设计与实现.md)
- [Spring 事务管理器的设计与实现](/docs/Spring/SpringTransaction/Spring事务管理器的设计与实现.md)
### Spring源码故事瞎编版
- [面筋哥 IoC 容器的一天(上)](/docs/Spring/Spring源码故事瞎编版/面筋哥IoC容器的一天(上).md)
@ -54,17 +54,17 @@
## MyBatis
### 基础支持层
- [反射工具箱和TypeHandler系列](docs/Mybatis/基础支持层/1、反射工具箱和TypeHandler系列.md)
- [DataSource及Transaction模块](docs/Mybatis/基础支持层/2、DataSource及Transaction模块.md)
- [binding模块](docs/Mybatis/基础支持层/3、binding模块.md)
- [反射工具箱和 TypeHandler 系列](docs/Mybatis/基础支持层/1、反射工具箱和TypeHandler系列.md)
- [DataSource Transaction 模块](docs/Mybatis/基础支持层/2、DataSource及Transaction模块.md)
- [binding 模块](docs/Mybatis/基础支持层/3、binding模块.md)
- [缓存模块](docs/Mybatis/基础支持层/4、缓存模块.md)
### 核心处理层
- [MyBatis初始化](docs/Mybatis/核心处理层/1、MyBatis初始化.md)
- [SqlNode和SqlSource](docs/Mybatis/核心处理层/2、SqlNode和SqlSource.md)
- [MyBatis 初始化](docs/Mybatis/核心处理层/1、MyBatis初始化.md)
- [SqlNode SqlSource](docs/Mybatis/核心处理层/2、SqlNode和SqlSource.md)
- [ResultSetHandler](docs/Mybatis/核心处理层/3、ResultSetHandler.md)
- [StatementHandler](docs/Mybatis/核心处理层/4、StatementHandler.md)
- [Executor组件](docs/Mybatis/核心处理层/5、Executor组件.md)
- [SqlSession组件](docs/Mybatis/核心处理层/6、SqlSession组件.md)
- [Executor 组件](docs/Mybatis/核心处理层/5、Executor组件.md)
- [SqlSession 组件](docs/Mybatis/核心处理层/6、SqlSession组件.md)
### 类解析
- [Mybatis-Cache](/docs/Mybatis/基础支持层/Mybatis-Cache.md)
- [Mybatis-log](/docs/Mybatis/基础支持层/Mybatis-log.md)
@ -84,7 +84,7 @@
## Netty
### IO
- [把被说烂的BIO、NIO、AIO再从头到尾扯一遍](docs/Netty/IO/把被说烂的BIO、NIO、AIO再从头到尾扯一遍.md)
- [把被说烂的 BIO、NIO、AIO 再从头到尾扯一遍](docs/Netty/IO/把被说烂的BIO、NIO、AIO再从头到尾扯一遍.md)
### 设计原理
努力编写中...
@ -97,15 +97,15 @@
## 学习心得
### 个人经验
- [初级开发者应该从 spring 源码中学什么](docs/LearningExperience/PersonalExperience/初级开发者应该从spring源码中学什么.md)
- [初级开发者应该从 Spring 源码中学什么](docs/LearningExperience/PersonalExperience/初级开发者应该从spring源码中学什么.md)
### 编码规范
- [一个程序员的自我修养](docs/LearningExperience/EncodingSpecification/一个程序员的自我修养.md)
### 设计模式
- [Spring及Mybatis框架源码中学习设计模式(创建型)](docs/LearningExperience/DesignPattern/从Spring及Mybatis框架源码中学习设计模式(创建型).md)
- [Spring及Mybatis框架源码中学习设计模式(行为型)](docs/LearningExperience/DesignPattern/从Spring及Mybatis框架源码中学习设计模式(行为型).md)
- [Spring及Mybatis框架源码中学习设计模式(结构型)](docs/LearningExperience/DesignPattern/从Spring及Mybatis框架源码中学习设计模式(结构型).md)
- [ Spring Mybatis 框架源码中学习设计模式(创建型)](docs/LearningExperience/DesignPattern/从Spring及Mybatis框架源码中学习设计模式(创建型).md)
- [ Spring Mybatis 框架源码中学习设计模式(行为型)](docs/LearningExperience/DesignPattern/从Spring及Mybatis框架源码中学习设计模式(行为型).md)
- [ Spring Mybatis 框架源码中学习设计模式(结构型)](docs/LearningExperience/DesignPattern/从Spring及Mybatis框架源码中学习设计模式(结构型).md)
- [从框架源码中学习设计模式的感悟](docs/LearningExperience/DesignPattern/从框架源码中学习设计模式的感悟.md)
## 贡献者

@ -3,10 +3,10 @@
本篇博文主要看一下创建型的几个设计模式,即,单例模式、各种工厂模式 及 建造者模式。
## 单例模式
#### 个人理解
### 个人理解
确保某个类只有一个实例并提供该实例的获取方法。实际应用很多不管是框架、JDK还是实际的项目开发但大都会使用“饿汉式”或“枚举”来实现单例。“懒汉式”也有一些应用但通过“双检锁机制”来保证单例的实现很少见。
#### 实现方式
### 实现方式
最简单的就是 使用一个私有构造函数、一个私有静态变量以及一个公共静态方法的方式来实现。懒汉式、饿汉式等简单实现就不多BB咯这里强调一下双检锁懒汉式实现的坑以及枚举方式的实现吧最后再结合spring源码 扩展一下单例bean的实现原理。
**1. 双检锁实现的坑**
@ -62,7 +62,7 @@ public enum SqlCommandType {
}
```
#### JDK中的范例
### JDK中的范例
**1. java.lang.Runtime**
```java
/**
@ -119,7 +119,7 @@ public class Desktop {
}
```
#### Spring的单例bean是如何实现的
### Spring的单例bean是如何实现的
Spring实现单例bean是使用map注册表和synchronized同步机制实现的通过分析spring的 AbstractBeanFactory 中的 doGetBean 方法和DefaultSingletonBeanRegistry的getSingleton()方法,可以理解其实现原理。
```java
public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {
@ -226,9 +226,9 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
```
## 简单工厂模式
#### 个人理解
### 个人理解
把同一系列类的实例化交由一个工厂类进行集中管控。与其说它是一种设计模式,倒不如把它看成一种编程习惯,因为它不符合“开闭原则”,增加新的产品类需要修改工厂类的代码。
#### 简单实现
### 简单实现
```java
public interface Hero {
void speak();
@ -263,10 +263,10 @@ public class HeroFactory {
这种设计方式只在我们产品的“FBM资金管理”模块有看到过其中对100+个按钮类进行了集中管控,不过其设计结构比上面这种要复杂的多。
## 工厂方法模式
#### 个人理解
### 个人理解
在顶级工厂(接口/抽象类)中定义 产品类的获取方法,由具体的子工厂实例化对应的产品,一般是一个子工厂对应一个特定的产品,实现对产品的集中管控,并且符合“开闭原则”。
#### Mybatis中的范例
### Mybatis中的范例
mybatis中数据源DataSource的获取使用到了该设计模式。接口DataSourceFactory定义了获取DataSource对象的方法各实现类 完成了获取对应类型的DataSource对象的实现。(mybatis的源码都是缩进两个空格难道国外的编码规范有独门派系)
```java
public interface DataSourceFactory {
@ -347,17 +347,16 @@ public interface DataSource extends CommonDataSource, Wrapper {
throws SQLException;
}
```
DataSource最主要的几个实现类内容都比较多代码就不贴出来咯感兴趣的同学可以到我的源码分析专题中看到详细解析地址
https://github.com/doocs/source-code-hunter
DataSource最主要的几个实现类内容都比较多代码就不贴出来咯感兴趣的同学可以到我的源码分析专题中看到详细解析。
**tips什么时候该用简单工厂模式什么时候该用工厂方法模式呢**
个人认为,工厂方法模式符合“开闭原则”,增加新的产品类不用修改代码,应当优先考虑使用这种模式。如果产品类结构简单且数量庞大时,还是使用简单工厂模式更容易维护些,如:上百个按钮类。
## 抽象工厂模式
#### 个人理解
### 个人理解
设计结构上与“工厂方法”模式很像,最主要的区别是,工厂方法模式中 一个子工厂只对应**一个**具体的产品,而抽象工厂模式中,一个子工厂对应**一组**具有相关性的产品,即,存在多个获取不同产品的方法。这种设计模式也很少见人用,倒是“工厂方法”模式见的最多。
#### 简单实现
### 简单实现
```java
public abstract class AbstractFactory {
@ -409,7 +408,7 @@ public class Client {
}
```
#### JDK中的范例
### JDK中的范例
JDK的javax.xml.transform.TransformerFactory组件使用了类似“抽象工厂”模式的设计抽象类TransformerFactory定义了两个抽象方法newTransformer()和newTemplates()分别用于生成Transformer对象 和 Templates对象其两个子类进行了不同的实现源码如下版本1.8)。
```java
public abstract class TransformerFactory {
@ -483,7 +482,7 @@ public class SmartTransformerFactoryImpl extends SAXTransformerFactory {
```
## 建造者模式
#### 个人理解
### 个人理解
该模式主要用于将复杂对象的构建过程分解成一个个简单的步骤,或者分摊到多个类中进行构建,保证构建过程层次清晰,代码不会过分臃肿,屏蔽掉了复杂对象内部的具体构建细节,其类图结构如下所示。
![avatar](/images/DesignPattern/建造者模式类图.png)
@ -497,7 +496,7 @@ public class SmartTransformerFactoryImpl extends SAXTransformerFactory {
其中的导演角色不必了解产品类的内部细节,只提供需要的信息给建造者,由具体建造者处理这些信息(这个处理过程可能会比较复杂)并完成产品构造,使产品对象的上层代码与产品对象的创建过程解耦。建造者模式将复杂产品的创建过程分散到不同的构造步骤中,这样可以对产品创建过程实现更加精细的控制,也会使创建过程更加清晰。每个具体建造者都可以创建出完整的产品对象,而且具体建造者之间是相互独立的, 因此系统就可以通过不同的具体建造者,得到不同的产品对象。当有新产品出现时,无须修改原有的代码,只需要添加新的具体建造者即可完成扩展,这符合“开放一封闭” 原则。
#### 典型的范例 StringBuilder和StringBuffer
### 典型的范例 StringBuilder和StringBuffer
相信在拼SQL语句时大家一定经常用到StringBuffer和StringBuilder这两个类它们就用到了建造者设计模式源码如下版本1.8
```java
abstract class AbstractStringBuilder implements Appendable, CharSequence {
@ -582,7 +581,7 @@ public final class StringBuffer extends AbstractStringBuilder
}
}
```
#### Mybatis中的范例
### Mybatis中的范例
MyBatis 的初始化过程使用了建造者模式,抽象类 BaseBuilder 扮演了“建造者接口”的角色对一些公用方法进行了实现并定义了公共属性。XMLConfigBuilder、XMLMapperBuilder、XMLStatementBuilder 等实现类扮演了“具体建造者”的角色分别用于解析mybatis-config.xml配置文件、映射配置文件 以及 SQL节点。Configuration 和 SqlSessionFactoryBuilder 则分别扮演了“产品” 和 “导演”的角色。
BaseBuilder组件的设计与上面标准的建造者模式是有很大不同的BaseBuilder的建造者模式主要是为了将复杂对象Configuration的构建过程分解的层次更清晰将整个构建过程分解到多个“具体构造者”类中需要这些“具体构造者”共同配合才能完成Configuration的构造单个“具体构造者”不具有单独构造产品的能力这与StringBuilder及StringBuffer是不同的。

@ -1,4 +1,4 @@
本文于总结《阿里Java开发手册》、《用友技术review手册》及个人Java开发工作经验并结合这半年来的源码阅读经验进行编写。回顾那些写过的 读过的代码,回顾自己。
本文于总结《阿里Java开发手册》、《用友技术review手册》及个人Java开发工作经验并结合这半年来的源码阅读经验进行编写。回顾那些写过的读过的代码,回顾自己。
## 第一章 基础编码规范
### 1.1 命名规范

@ -7,346 +7,356 @@ spring 源码这种东西真的是一回生二回熟,第一遍会被各种设
首先对于我们新手来说,还是从我们最常用的两个 IoC 容器开始分析,这次我们先分析 FileSystemXmlApplicationContext 这个 IoC 容器的具体实现ClassPathXmlApplicationContext 留着下次讲解。
PS可以结合我 GitHub 上对 spring 框架源码的翻译注解一起看,会更有助于各位开发姥爷的理解。
地址:
PS可以结合我 GitHub 上对 Spring 框架源码的翻译注释一起看,会更有助于各位同学的理解。地址:
spring-beans https://github.com/AmyliaY/spring-beans-reading
spring-context https://github.com/AmyliaY/spring-context-reading
## FileSystemXmlApplicationContext 的构造方法
当我们传入一个 spring 配置文件去实例化 FileSystemXmlApplicationContext() 时,可以看一下它的构造方法都做了什么。
## 正文
当我们传入一个 Spring 配置文件去实例化 FileSystemXmlApplicationContext 时,可以看一下它的构造方法都做了什么。
```java
/**
* 下面这 4 个构造方法都调用了第 5 个构造方法
* @param configLocation
* @throws BeansException
*/
// configLocation 包含了 BeanDefinition 所在的文件路径
public FileSystemXmlApplicationContext(String configLocation) throws BeansException {
this(new String[] {configLocation}, true, null);
}
// 可以定义多个 BeanDefinition 所在的文件路径
public FileSystemXmlApplicationContext(String... configLocations) throws BeansException {
this(configLocations, true, null);
}
// 在定义多个 BeanDefinition 所在的文件路径 的同时,还能指定自己的双亲 IoC 容器
public FileSystemXmlApplicationContext(String[] configLocations, ApplicationContext parent) throws BeansException {
this(configLocations, true, parent);
}
public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh) throws BeansException {
this(configLocations, refresh, null);
}
/**
* 如果应用直接使用 FileSystemXmlApplicationContext 进行实例化,则都会进到这个构造方法中来
* @param configLocations
* @param refresh
* @param parent
* @throws BeansException
*/
public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
throws BeansException {
//动态地确定用哪个加载器去加载我们的配置文件
super(parent);
//告诉读取器 配置文件放在哪里,该方法继承于爷类 AbstractRefreshableApplicationContext
setConfigLocations(configLocations);
if (refresh) {
//容器初始化
refresh();
}
}
/**
* 实例化一个 FileSystemResource 并返回,以便后续对资源的 IO 操作
* 本方法是在其父类 DefaultResourceLoader 的 getResource 方法中被调用的,
*/
@Override
protected Resource getResourceByPath(String path) {
if (path != null && path.startsWith("/")) {
path = path.substring(1);
}
return new FileSystemResource(path);
}
/**
* 下面这 4 个构造方法都调用了第 5 个构造方法
* @param configLocation
* @throws BeansException
*/
// configLocation 包含了 BeanDefinition 所在的文件路径
public FileSystemXmlApplicationContext(String configLocation) throws BeansException {
this(new String[] {configLocation}, true, null);
}
// 可以定义多个 BeanDefinition 所在的文件路径
public FileSystemXmlApplicationContext(String... configLocations) throws BeansException {
this(configLocations, true, null);
}
// 在定义多个 BeanDefinition 所在的文件路径 的同时,还能指定自己的双亲 IoC 容器
public FileSystemXmlApplicationContext(String[] configLocations, ApplicationContext parent) throws BeansException {
this(configLocations, true, parent);
}
public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh) throws BeansException {
this(configLocations, refresh, null);
}
/**
* 如果应用直接使用 FileSystemXmlApplicationContext 进行实例化,则都会进到这个构造方法中来
*/
public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
throws BeansException {
// 动态地确定用哪个加载器去加载我们的配置文件
super(parent);
// 告诉读取器 配置文件放在哪里,该方法继承于爷类 AbstractRefreshableApplicationContext
setConfigLocations(configLocations);
if (refresh) {
// 容器初始化
refresh();
}
}
/**
* 实例化一个 FileSystemResource 并返回,以便后续对资源的 IO 操作
* 本方法是在其父类 DefaultResourceLoader 的 getResource 方法中被调用的,
*/
@Override
protected Resource getResourceByPath(String path) {
if (path != null && path.startsWith("/")) {
path = path.substring(1);
}
return new FileSystemResource(path);
}
```
## 看看其父类 AbstractApplicationContext 实现的 refresh() 方法,该方法就是 IoC 容器初始化的入口类
看看其父类 AbstractApplicationContext 实现的 refresh() 方法,该方法就是 IoC 容器初始化的入口类
```java
/**
* 容器初始化的过程BeanDefinition 的 Resource 定位、BeanDefinition 的载入、BeanDefinition 的注册。
* BeanDefinition 的载入和 bean 的依赖注入是两个独立的过程,依赖注入一般发生在 应用第一次通过 getBean() 方法从容器获取 bean 时。
*
* 另外需要注意的是IoC 容器有一个预实例化的配置(即,将 AbstractBeanDefinition 中的 lazyInit 属性设为 true使用户可以对容器的初始化
* 过程做一个微小的调控lazyInit 设为 false 的 bean 将在容器初始化时进行依赖注入,而不会等到 getBean() 方法调用时才进行
*/
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 调用容器准备刷新的方法,获取容器的当前时间,同时给容器设置同步标识
prepareRefresh();
/**
* 容器初始化的过程BeanDefinition 的 Resource 定位、BeanDefinition 的载入、BeanDefinition 的注册。
* BeanDefinition 的载入和 bean 的依赖注入是两个独立的过程,依赖注入一般发生在 应用第一次通过
* getBean() 方法从容器获取 bean 时。
*
* 另外需要注意的是IoC 容器有一个预实例化的配置(即,将 AbstractBeanDefinition 中的 lazyInit 属性
* 设为 true使用户可以对容器的初始化过程做一个微小的调控lazyInit 设为 false 的 bean
* 将在容器初始化时进行依赖注入,而不会等到 getBean() 方法调用时才进行
*/
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 调用容器准备刷新,获取容器的当前时间,同时给容器设置同步标识
prepareRefresh();
// 告诉子类启动 refreshBeanFactory() 方法Bean 定义资源文件的载入从子类的 refreshBeanFactory() 方法启动开始
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 告诉子类启动 refreshBeanFactory() 方法BeanDefinition 资源文件的载入从子类的 refreshBeanFactory() 方法启动开始
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 为 BeanFactory 配置容器特性,例如类加载器、事件处理器等
prepareBeanFactory(beanFactory);
// 为 BeanFactory 配置容器特性,例如类加载器、事件处理器等
prepareBeanFactory(beanFactory);
try {
// 为容器的某些子类指定特殊的 BeanPost 事件处理器
postProcessBeanFactory(beanFactory);
try {
// 为容器的某些子类指定特殊的 BeanPost 事件处理器
postProcessBeanFactory(beanFactory);
// 调用所有注册的 BeanFactoryPostProcessor 的 Bean
invokeBeanFactoryPostProcessors(beanFactory);
// 调用所有注册的 BeanFactoryPostProcessor 的 Bean
invokeBeanFactoryPostProcessors(beanFactory);
// 为 BeanFactory 注册 BeanPost 事件处理器.
// BeanPostProcessor 是 Bean 后置处理器,用于监听容器触发的事件
registerBeanPostProcessors(beanFactory);
// 为 BeanFactory 注册 BeanPost 事件处理器.
// BeanPostProcessor 是 Bean 后置处理器,用于监听容器触发的事件
registerBeanPostProcessors(beanFactory);
// 初始化信息源,和国际化相关.
initMessageSource();
// 初始化信息源,和国际化相关.
initMessageSource();
// 初始化容器事件传播器
initApplicationEventMulticaster();
// 初始化容器事件传播器
initApplicationEventMulticaster();
// 调用子类的某些特殊 Bean 初始化方法
onRefresh();
// 调用子类的某些特殊 Bean 初始化方法
onRefresh();
// 为事件传播器注册事件监听器.
registerListeners();
// 为事件传播器注册事件监听器.
registerListeners();
// 初始化 Bean并对 lazy-init 属性进行处理
finishBeanFactoryInitialization(beanFactory);
// 初始化 Bean并对 lazy-init 属性进行处理
finishBeanFactoryInitialization(beanFactory);
// 初始化容器的生命周期事件处理器,并发布容器的生命周期事件
finishRefresh();
}
// 初始化容器的生命周期事件处理器,并发布容器的生命周期事件
finishRefresh();
}
catch (BeansException ex) {
// 销毁以创建的单态 Bean
destroyBeans();
catch (BeansException ex) {
// 销毁以创建的单态 Bean
destroyBeans();
// 取消 refresh 操作,重置容器的同步标识.
cancelRefresh(ex);
// 取消 refresh 操作,重置容器的同步标识.
cancelRefresh(ex);
throw ex;
}
}
}
throw ex;
}
}
}
```
## 看看 obtainFreshBeanFactory 方法,该方法告诉了子类去刷新内部的 beanFactory
看看 obtainFreshBeanFactory() 方法,该方法告诉了子类去刷新内部的 beanFactory
```java
/**
* Tell the subclass to refresh the internal bean factory.
* 告诉子类去刷新内部的 beanFactory
*/
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
// 自己定义了抽象的 refreshBeanFactory() 方法,具体实现交给了自己的子类
refreshBeanFactory();
// getBeanFactory() 也是一个抽象方法,委派给子类实现
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (logger.isDebugEnabled()) {
logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
}
return beanFactory;
}
/**
* Tell the subclass to refresh the internal bean factory.
* 告诉子类去刷新内部的 beanFactory
*/
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
// 自己定义了抽象的 refreshBeanFactory() 方法,具体实现交给了自己的子类
refreshBeanFactory();
// getBeanFactory() 也是一个抽象方法,交由子类实现
// 看到这里是不是很容易想起 “模板方法模式”,父类在模板方法中定义好流程,定义好抽象方法
// 具体实现交由子类完成
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (logger.isDebugEnabled()) {
logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
}
return beanFactory;
}
```
## AbstractRefreshableApplicationContext 中对 refreshBeanFactory() 方法的实现
FileSystemXmlApplicationContext 从上层体系的各抽象类中继承了大量的方法实现,抽象类中抽取大量公共行为进行具体实现,留下 abstract 的个性化方法交给具体的子类实现,这是一个很好的 OOP 编程设计,我们在自己编码时也可以尝试这样设计自己的类图。理清 FileSystemXmlApplicationContext 的上层体系设计,就不易被各种设计模式搞晕咯。
下面看一下 AbstractRefreshableApplicationContext 中对 refreshBeanFactory() 方法的实现。FileSystemXmlApplicationContext 从上层体系的各抽象类中继承了大量的方法实现,抽象类中抽取大量公共行为进行具体实现,留下 abstract 的个性化方法交给具体的子类实现,这是一个很好的 OOP 编程设计,我们在自己编码时也可以尝试这样设计自己的类图。理清 FileSystemXmlApplicationContext 的上层体系设计,就不易被各种设计模式搞晕咯。
```java
// 在这里完成了容器的初始化,并赋值给自己 private 的 beanFactory 属性,为下一步调用做准备
// 从父类 AbstractApplicationContext 继承的抽象方法,自己做了实现
@Override
protected final void refreshBeanFactory() throws BeansException {
// 如果已经建立了 IoC 容器,则销毁并关闭容器
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
// 创建 IoC 容器DefaultListableBeanFactory 类实现了 ConfigurableListableBeanFactory 接口
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
// 对 IoC 容器进行定制化,如设置启动参数,开启注解的自动装配等
customizeBeanFactory(beanFactory);
// 载入 BeanDefinition在当前类中只定义了抽象的 loadBeanDefinitions 方法,具体实现 调用子类容器
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
// 给自己的属性赋值
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
/**
* 在这里完成了容器的初始化,并赋值给自己私有的 beanFactory 属性,为下一步调用做准备
* 从父类 AbstractApplicationContext 继承的抽象方法,自己做了实现
*/
@Override
protected final void refreshBeanFactory() throws BeansException {
// 如果已经建立了 IoC 容器,则销毁并关闭容器
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
// 创建 IoC 容器DefaultListableBeanFactory 类实现了 ConfigurableListableBeanFactory 接口
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
// 对 IoC 容器进行定制化,如设置启动参数,开启注解的自动装配等
customizeBeanFactory(beanFactory);
// 载入 BeanDefinition在当前类中只定义了抽象的 loadBeanDefinitions() 方法,具体实现 调用子类容器
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
// 给自己的属性赋值
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
```
## AbstractXmlApplicationContext 中对 loadBeanDefinitions(DefaultListableBeanFactory beanFactory) 的实现
AbstractXmlApplicationContext 中对 loadBeanDefinitions(DefaultListableBeanFactory beanFactory) 的实现
```java
/*
* 实现了爷类 AbstractRefreshableApplicationContext 的抽象方法
*/
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// DefaultListableBeanFactory 实现了 BeanDefinitionRegistry 接口,在初始化 XmlBeanDefinitionReader 时
// 将 BeanDefinition 注册器注入该 BeanDefinition 读取器
// 创建 用于从 Xml 中读取 BeanDefinition 的读取器,并通过回调设置到 IoC 容器中去,容器使用该读取器读取 BeanDefinition 资源
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
beanDefinitionReader.setEnvironment(this.getEnvironment());
// 为 beanDefinition 读取器设置 资源加载器,由于本类的基类 AbstractApplicationContext
// 继承了 DefaultResourceLoader因此本容器自身也是一个资源加载器
beanDefinitionReader.setResourceLoader(this);
// 设置 SAX 解析器SAXsimple API for XML是另一种 XML 解析方法。相比于 DOMSAX 速度更快,占用内存更小。
// 它逐行扫描文档,一边扫描一边解析。相比于先将整个 XML 文件扫描近内存,再进行解析的 DOMSAX 可以在解析文档的任意时刻停止解析,但操作也比 DOM 复杂。
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// 初始化 beanDefinition 读取器,该方法同时启用了 Xml 的校验机制
initBeanDefinitionReader(beanDefinitionReader);
// Bean 读取器真正实现加载的方法
loadBeanDefinitions(beanDefinitionReader);
}
/*
* 实现了基类 AbstractRefreshableApplicationContext 的抽象方法
*/
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// DefaultListableBeanFactory 实现了 BeanDefinitionRegistry 接口,在初始化 XmlBeanDefinitionReader 时
// 将 BeanDefinition 注册器注入该 BeanDefinition 读取器
// 创建用于从 Xml 中读取 BeanDefinition 的读取器,并通过回调设置到 IoC 容器中去,容器使用该读取器读取 BeanDefinition 资源
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
beanDefinitionReader.setEnvironment(this.getEnvironment());
// 为 beanDefinition 读取器设置 资源加载器,由于本类的基类 AbstractApplicationContext
// 继承了 DefaultResourceLoader因此本容器自身也是一个资源加载器
beanDefinitionReader.setResourceLoader(this);
// 设置 SAX 解析器SAXsimple API for XML是另一种 XML 解析方法。相比于 DOMSAX 速度更快,占用内存更小。
// 它逐行扫描文档,一边扫描一边解析。相比于先将整个 XML 文件扫描近内存,再进行解析的 DOMSAX 可以在解析文档的
// 任意时刻停止解析,但操作也比 DOM 复杂。
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// 初始化 beanDefinition 读取器,该方法同时启用了 Xml 的校验机制
initBeanDefinitionReader(beanDefinitionReader);
// Bean 读取器真正实现加载的方法
loadBeanDefinitions(beanDefinitionReader);
}
```
## 继续看 AbstractXmlApplicationContext 中 loadBeanDefinitions 的重载方法
继续看 AbstractXmlApplicationContext 中 loadBeanDefinitions() 的重载方法
```java
// 用传进来的 XmlBeanDefinitionReader 读取器加载 Xml 文件中的 BeanDefinition
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
/**
* ClassPathXmlApplicationContext 与 FileSystemXmlApplicationContext
* 在这里的调用出现分歧,各自按不同的方式加载解析 Resource 资源
* 最后在具体的解析和 BeanDefinition 定位上又会殊途同归
*/
// 获取存放了 BeanDefinition 的所有 Resource
// FileSystemXmlApplicationContext 类未对 getConfigResources() 进行重新,
// 所以调用父类的return null。
// 而 ClassPathXmlApplicationContext 对该方法进行了重写,返回设置的值
Resource[] configResources = getConfigResources();
if (configResources != null) {
// Xml Bean 读取器调用其父类 AbstractBeanDefinitionReader 读取定位的 Bean 定义资源
reader.loadBeanDefinitions(configResources);
}
// 调用父类 AbstractRefreshableConfigApplicationContext 实现的返回值为 String[] 的 getConfigLocations() 方法,
// 优先返回 FileSystemXmlApplicationContext 构造方法中调用 setConfigLocations() 方法设置的资源
String[] configLocations = getConfigLocations();
if (configLocations != null) {
// XmlBeanDefinitionReader 读取器调用其父类 AbstractBeanDefinitionReader 的方法从配置位置加载 BeanDefinition
reader.loadBeanDefinitions(configLocations);
}
}
/**
* 用传进来的 XmlBeanDefinitionReader 读取器加载 Xml 文件中的 BeanDefinition
*/
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
/**
* ClassPathXmlApplicationContext 与 FileSystemXmlApplicationContext 在这里的调用出现分歧,
* 各自按不同的方式加载解析 Resource 资源,最后在具体的解析和 BeanDefinition 定位上又会殊途同归。
*/
// 获取存放了 BeanDefinition 的所有 ResourceFileSystemXmlApplicationContext 类未对
// getConfigResources() 进行重写所以调用父类的return null。
// 而 ClassPathXmlApplicationContext 对该方法进行了重写,返回设置的值
Resource[] configResources = getConfigResources();
if (configResources != null) {
// XmlBeanDefinitionReader 调用其父类 AbstractBeanDefinitionReader 的方法加载 BeanDefinition
reader.loadBeanDefinitions(configResources);
}
// 调用父类 AbstractRefreshableConfigApplicationContext 的实现,
// 优先返回 FileSystemXmlApplicationContext 构造方法中调用 setConfigLocations() 方法设置的资源
String[] configLocations = getConfigLocations();
if (configLocations != null) {
// XmlBeanDefinitionReader 调用其父类 AbstractBeanDefinitionReader 的方法从配置位置加载 BeanDefinition
reader.loadBeanDefinitions(configLocations);
}
}
```
## AbstractBeanDefinitionReader 中对 loadBeanDefinitions 方法的各种重载及调用
AbstractBeanDefinitionReader 中对 loadBeanDefinitions 方法的各种重载及调用
```java
// loadBeanDefinitions() 方法的重载方法之一,调用了另一个重载方法 loadBeanDefinitions(String)
public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
Assert.notNull(locations, "Location array must not be null");
// 计数 加载了多少个配置文件
int counter = 0;
for (String location : locations) {
counter += loadBeanDefinitions(location);
}
return counter;
}
// 重载方法之一,调用了下面的 loadBeanDefinitions(String, Set<Resource>) 方法
public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
return loadBeanDefinitions(location, null);
}
// 获取在 IoC 容器初始化过程中设置的资源加载器
public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
// 在实例化 XmlBeanDefinitionReader 后 IoC 容器将自己注入进该读取器作为 resourceLoader 属性
ResourceLoader resourceLoader = getResourceLoader();
if (resourceLoader == null) {
throw new BeanDefinitionStoreException(
"Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
}
if (resourceLoader instanceof ResourcePatternResolver) {
try {
// 将指定位置的 BeanDefinition 资源文件解析为 IoC 容器封装的资源
// 加载多个指定位置的 BeanDefinition 资源文件
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
// 委派调用其子类 XmlBeanDefinitionReader 的方法,实现加载功能
int loadCount = loadBeanDefinitions(resources);
if (actualResources != null) {
for (Resource resource : resources) {
actualResources.add(resource);
}
}
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
}
return loadCount;
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"Could not resolve bean definition resource pattern [" + location + "]", ex);
}
}
else {
/**
*
* AbstractApplicationContext 继承了 DefaultResourceLoader所以 AbstractApplicationContext
* 及其子类都会调用 DefaultResourceLoader 中的实现,将指定位置的资源文件解析为 Resource
* 至此完成了对 BeanDefinition 的资源定位
*
*/
Resource resource = resourceLoader.getResource(location);
// 从 resource 中加载 BeanDefinitionloadCount 为加载的 BeanDefinition 个数
// 该 loadBeanDefinitions() 方法来自其 implements 的 BeanDefinitionReader 接口,
// 且本类是一个抽象类,并未对该方法进行实现。而是交由子类进行实现,如果是用 xml 文件进行
// IoC 容器初始化的,则调用 XmlBeanDefinitionReader 中的实现
int loadCount = loadBeanDefinitions(resource);
if (actualResources != null) {
actualResources.add(resource);
}
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
}
return loadCount;
}
}
/**
* loadBeanDefinitions() 方法的重载方法之一,调用了另一个重载方法 loadBeanDefinitions(String)
*/
public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
Assert.notNull(locations, "Location array must not be null");
// 计数器,统计加载了多少个配置文件
int counter = 0;
for (String location : locations) {
counter += loadBeanDefinitions(location);
}
return counter;
}
/**
* 重载方法之一,调用了下面的 loadBeanDefinitions(String, Set<Resource>) 方法
*/
public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
return loadBeanDefinitions(location, null);
}
/**
* 获取在 IoC 容器初始化过程中设置的资源加载器
*/
public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
// 在实例化 XmlBeanDefinitionReader 后 IoC 容器将自己注入进该读取器作为 resourceLoader 属性
ResourceLoader resourceLoader = getResourceLoader();
if (resourceLoader == null) {
throw new BeanDefinitionStoreException(
"Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
}
if (resourceLoader instanceof ResourcePatternResolver) {
try {
// 将指定位置的 BeanDefinition 资源文件解析为 IoC 容器封装的资源
// 加载多个指定位置的 BeanDefinition 资源文件
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
// 委派调用其子类 XmlBeanDefinitionReader 的方法,实现加载功能
int loadCount = loadBeanDefinitions(resources);
if (actualResources != null) {
for (Resource resource : resources) {
actualResources.add(resource);
}
}
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
}
return loadCount;
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"Could not resolve bean definition resource pattern [" + location + "]", ex);
}
}
else {
/**
*
* AbstractApplicationContext 继承了 DefaultResourceLoader所以 AbstractApplicationContext
* 及其子类都会调用 DefaultResourceLoader 中的实现,将指定位置的资源文件解析为 Resource
* 至此完成了对 BeanDefinition 的资源定位
*
*/
Resource resource = resourceLoader.getResource(location);
// 从 resource 中加载 BeanDefinitionloadCount 为加载的 BeanDefinition 个数
// 该 loadBeanDefinitions() 方法来自其实现的 BeanDefinitionReader 接口,
// 且本类是一个抽象类,并未对该方法进行实现。而是交由子类进行实现,如果是用 xml 文件进行
// IoC 容器的初始化,则调用 XmlBeanDefinitionReader 中的实现
int loadCount = loadBeanDefinitions(resource);
if (actualResources != null) {
actualResources.add(resource);
}
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
}
return loadCount;
}
}
```
## resourceLoader 的 getResource() 方法有多种实现,看清 FileSystemXmlApplicationContext 的继承体系就可以明确,其走的是 DefaultResourceLoader 中的实现
resourceLoader 的 getResource() 方法有多种实现,看清 FileSystemXmlApplicationContext 的继承体系就可以明确,其走的是 DefaultResourceLoader 中的实现
```java
// 获取 Resource 的具体实现方法
public Resource getResource(String location) {
Assert.notNull(location, "Location must not be null");
// 如果 location 是类路径的方式,返回 ClassPathResource 类型的文件资源对象
if (location.startsWith(CLASSPATH_URL_PREFIX)) {
return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());
}
else {
try {
// 如果是 URL 方式,返回 UrlResource 类型的文件资源对象,
// 否则将抛出的异常进入 catch 代码块,返回另一种资源对象
URL url = new URL(location);
return new UrlResource(url);
}
catch (MalformedURLException ex) {
// 如果既不是 classpath 标识,又不是 URL 标识的 Resource 定位,则调用
// 容器本身的 getResourceByPath 方法获取 Resource
// 根据实例化的子类对象,调用其子类对象中重写的此方法,
// 如 FileSystemXmlApplicationContext 子类中对此方法的重新
return getResourceByPath(location);
}
}
}
/**
* 获取 Resource 的具体实现方法
*/
public Resource getResource(String location) {
Assert.notNull(location, "Location must not be null");
// 如果 location 是类路径的方式,返回 ClassPathResource 类型的文件资源对象
if (location.startsWith(CLASSPATH_URL_PREFIX)) {
return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());
}
else {
try {
// 如果是 URL 方式,返回 UrlResource 类型的文件资源对象,
// 否则将抛出的异常进入 catch 代码块,返回另一种资源对象
URL url = new URL(location);
return new UrlResource(url);
}
catch (MalformedURLException ex) {
// 如果既不是 classpath 标识,又不是 URL 标识的 Resource 定位,则调用容器本身的
// getResourceByPath() 方法获取 Resource。根据实例化的子类对象调用其子类对象中
// 重写的此方法,如 FileSystemXmlApplicationContext 子类中对此方法的重写
return getResourceByPath(location);
}
}
}
```
## 其中的 getResourceByPath(location) 方法的实现则是在 FileSystemXmlApplicationContext 中完成的
其中的 getResourceByPath(location) 方法的实现则是在 FileSystemXmlApplicationContext 中完成的
```java
/**
* 实例化一个 FileSystemResource 并返回,以便后续对资源的 IO 操作
* 本方法是在 DefaultResourceLoader 的 getResource 方法中被调用的,
*/
@Override
protected Resource getResourceByPath(String path) {
if (path != null && path.startsWith("/")) {
path = path.substring(1);
}
return new FileSystemResource(path);
}
/**
* 实例化一个 FileSystemResource 并返回,以便后续对资源的 IO 操作
* 本方法是在 DefaultResourceLoader 的 getResource() 方法中被调用的,
*/
@Override
protected Resource getResourceByPath(String path) {
if (path != null && path.startsWith("/")) {
path = path.substring(1);
}
return new FileSystemResource(path);
}
```
至此我们可以看到FileSystemXmlApplicationContext 的 getResourceByPath() 方法返回了一个 FileSystemResource 对象,接下来 spring 就可以对这个对象进行相关的 I/O 操作,进行 BeanDefinition 的读取和载入了。

Loading…
Cancel
Save