Spring源码阅读目录
第一部分——IOC篇
第一章 Spring之最熟悉的陌生人——IOC
第二章 Spring之假如让你来写IOC容器——加载资源篇
第三章 Spring之假如让你来写IOC容器——解析配置文件篇
第四章 Spring之假如让你来写IOC容器——XML配置文件篇
第五章 Spring之假如让你来写IOC容器——BeanFactory和FactoryBean
第六章 Spring之假如让你来写IOC容器——Scope和属性填充
第七章 Spring之假如让你来写IOC容器——属性填充特别篇:SpEL表达式
第八章 Spring之假如让你来写IOC容器——拓展篇
第九章 Spring之源码阅读——环境搭建篇
第十章 Spring之源码阅读——IOC篇
第二部分——AOP篇
第十一章 Spring之不太熟的熟人——AOP
第十二章 Spring之不得不了解的内容——概念篇
第十三章 Spring之假如让你来写AOP——AOP联盟篇
第十四章 Spring之假如让你来写AOP——雏形篇
第十五章 Spring之假如让你来写AOP——Joinpoint(连接点)篇
第十六章 Spring之假如让你来写AOP——Pointcut(切点)篇
第十七章 Spring之假如让你来写AOP——Advice(通知)上篇
第十八章 Spring之假如让你来写AOP——Advice(通知)下篇
第十九章 Spring之假如让你来写AOP——番外篇:Spring早期设计
第二十章 Spring之假如让你来写AOP——Aspect(切面)篇
第二十一章 Spring之假如让你来写AOP——Weaver(织入器)篇
第二十二章 Spring之假如让你来写AOP——Target Object(目标对象)篇
第二十三章 Spring之假如让你来写AOP——融入IOC容器篇
第二十四章 Spring之源码阅读——AOP篇
第三部分——事务篇
第二十五章 Spring之曾经的老朋友——事务
第二十六章 Spring之假如让你来写事务——初稿篇
第二十七章 Spring之假如让你来写事务——铁三角篇
第二十八章 Spring之假如让你来写事务——属性篇
第二十九章 Spring之假如让你来写事务——状态篇
第三十章 Spring之假如让你来写事务——管理篇
第三十一章 Spring之假如让你来写事务——融入IOC容器篇
第三十二章 Spring之源码阅读——事务篇
第四部分——MVC篇
第三十三章 Spring之梦开始的地方——MVC
第三十四章 Spring之假如让你来写MVC——草图篇
第三十五章 Spring之假如让你来写MVC——映射器篇
第三十六章 Spring之假如让你来写MVC——拦截器篇
第三十七章 Spring之假如让你来写MVC——控制器篇
第三十八章 Spring之假如让你来写MVC——适配器篇
第三十九章 Spring之假如让你来写MVC——番外篇:类型转换
第四十章 Spring之假如让你来写MVC——ModelAndView篇
第四十一章 Spring之假如让你来写MVC——番外篇:数据绑定
第四十二章 Spring之假如让你来写MVC——视图篇
第四十三章 Spring之假如让你来写MVC——上传文件篇
第四十四章 Spring之假如让你来写MVC——异常处理器篇
第四十五章 Spring之假如让你来写MVC——国际化篇
第四十六章 Spring之假如让你来写MVC——主题解析器篇
第四十七章 Spring之假如让你来写MVC——闪存管理器篇
第四十八章 Spring之假如让你来写MVC——请求映射视图篇
第四十九章 Spring之假如让你来写MVC——番外篇:属性操作
第五十章 Spring之假如让你来写MVC——融入IOC容器篇
第五十一章 Spring之源码阅读——MVC篇
文章目录
前言
对于Spring一直都是既熟悉又陌生,说对它熟悉吧,平时用用没啥问题,但面试的时候被问的一脸懵逼,就很尴尬,都不好意思在简历上写着熟悉Spring了
所以决定花点时间研究研究Spring的源码。主要参考的书籍是:《Spring源码深度解析(第2版)》、《Spring揭秘》、《Spring技术内幕:深入解析Spring架构与设计原理(第2版)》
书接上回,在上篇 第三十八章 Spring之假如让你来写MVC——适配器篇 中,A君 已经完成了 适配器 部分的功能了。接下来看看 A君 会有什么骚操作吧
尝试动手写IOC容器
出场人物:A君(苦逼的开发)、老大(项目经理)
背景:老大 要求 A君在一周内开发个简单的 IOC容器
前情提要:A君 已经完成了 适配器 部分的功能了 。。。
类型转换概览
在之前的章节中,为了避免一叶障目,A君 没有把重心放在没必要的细节上,只求明白整个 Spring 的主流程。还有一个就是之前也没有想到过会写这么多章节,现在既然已经折腾到这一步了,那就不能在对 Spring 中的类型转换避而不谈,是时候进行一个深入的探讨了
1.0版本
基于JDK的类型转换
在 Spring 早期版本中,其实类型转换并不复杂,Spring 直接依赖于 JDK 自带的PropertyEditor
进行类型转换,其接口如下:
public interface PropertyEditor {
void setValue(Object value);
Object getValue();
boolean isPaintable();
void paintValue(java.awt.Graphics gfx, java.awt.Rectangle box);
String getJavaInitializationString();
String getAsText();
void setAsText(String text) throws java.lang.IllegalArgumentException;
String[] getTags();
java.awt.Component getCustomEditor();
boolean supportsCustomEditor();
void addPropertyChangeListener(PropertyChangeListener listener);
void removePropertyChangeListener(PropertyChangeListener listener);
}
这也是 A君 之前采用的方法,依赖于 JDK,那只需要提供对应的类型转换实现即可。JDK 也提供了公共类,封装了大部分方法。用户只需要重写setAsText
就行了。例子如下:
public class CustomBooleanEditor extends PropertyEditorSupport {
@Override
public void setAsText(String text) throws IllegalArgumentException{
this.setValue(Boolean.valueOf(text));
}
}
管理器
PropertyEditor
可谓是非常之简单。像这种转换类肯定会有一大堆,这时候就需要一个可以进行统一管理地方。于是,就有了接口PropertyEditorRegistry
。代码如下:
public interface PropertyEditorRegistry {
void registerCustomEditor(Class<?> requiredType, PropertyEditor propertyEditor);
void registerCustomEditor(@Nullable Class<?> requiredType, @Nullable String propertyPath, PropertyEditor propertyEditor);
@Nullable
PropertyEditor findCustomEditor(@Nullable Class<?> requiredType, @Nullable String propertyPath);
}
PropertyEditorRegistry
负责找到对应的转换器,然后PropertyEditor
负责类型转换,这两个接口组成了 Spring 早期的类型转换
注册器
转换器有了,管理器也有了,接着该考虑拓展了。对于用户来说,直接操作内部组件有点不合适,不符合开闭原则,所以这时候就需要暴露出个接口,提供给用户使用,于是就有了PropertyEditorRegistrar
,代码如下:
public interface PropertyEditorRegistrar {
void registerCustomEditors(PropertyEditorRegistry registry);
}
用户只要实现该接口,就能把自定义的类型转换器注册进 Spring 中去,这大大的简化了用户操作
2.0版本
新的类型转换器
PropertyEditor
虽然简单,但是简单的代价就是无法支持复杂场景。PropertyEditor
只支持字符串,显然,这对于后来的 Spring 来说有点捉襟见肘了。于是 Spring 对其进行了拓展。于是 Spring 定义了个TypeConverter
接口,代码如下:
public interface TypeConverter {
@Nullable
<T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType) throws TypeMismatchException;
@Nullable
<T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType,
@Nullable MethodParameter methodParam) throws TypeMismatchException;
@Nullable
<T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType, @Nullable Field field)
throws TypeMismatchException;
@Nullable
default <T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType,
@Nullable TypeDescriptor typeDescriptor) throws TypeMismatchException {
throw new UnsupportedOperationException("TypeDescriptor resolution not supported");
}
}
这个接口对弥补了PropertyEditor
只支持字符串的窘境。有了接口必然就得有实现了,它的实现分别为:SimpleTypeConverter
、BeanWrapperImpl
。有多个实现,但是转换逻辑其实都是一样,为了复用,Spring 把这些转换封装到TypeConverterDelegate
类中。这里以SimpleTypeConverter
为例,代码如下:
public class SimpleTypeConverter extends PropertyEditorRegistrySupport implements TypeConverter {
private final TypeConverterDelegate typeConverterDelegate = new TypeConverterDelegate(this);
public SimpleTypeConverter() {
this.registerDefaultEditors();
}
public Object convertIfNecessary(Object value, Class requiredType) throws TypeMismatchException {
return this.convertIfNecessary(value, requiredType, (MethodParameter)null);
}
public Object convertIfNecessary(Object value, Class requiredType, MethodParameter methodParam) throws TypeMismatchException {
try {
return this.typeConverterDelegate.convertIfNecessary(value, requiredType, methodParam);
} catch (IllegalArgumentException var5) {
IllegalArgumentException ex = var5;
throw new TypeMismatchException(value, requiredType, ex);
}
}
}
可以看到SimpleTypeConverter
基本上啥事都没干,都委托给了TypeConverterDelegate
3.0之后
单一类型转换
TypeConverter
和PropertyEditor
似乎已经足够用了,但是其本身依旧存在着问题,比如说:线程安全,随着 JDK 的发展,引入的注解等新特性。。。重构的时候到了,首当其冲的当然就是单一类型的转换,这种转换相对来说,也比较简单,比如:int --> String、boolean --> String、String --> int,这种单一类型的转换可以将他们归为一类,于是,Spring 定义了Converter
接口,代码如下:
@FunctionalInterface
public interface Converter<S, T> {
@Nullable
T convert(S source);
default <U> Converter<S, U> andThen(Converter<? super T, ? extends U> after) {
Assert.notNull(after, "After Converter must not be null");
return (S s) -> {
T initialResult = convert(s);
return (initialResult != null ? after.convert(initialResult) : null);
};
}
}
Converter
就不像PropertyEditor
了,只支持字符串类型,而是由S(源类型)
、T(目标类型)
两个泛型代替,意味着可以进行任意类型的转换,自由度大大的提高了。其类图如下:
多类型转换
Converter
再灵活,也只能执行单一类型转换。在 JAVA 语言中,还存在这多种类型——集合。集合也可以分为多种,例如:Array、List、Map、Set,这些在平时开发中屡见不鲜,他们的特点就是可以一次放入多个对象,而这些对象的类型不一定是同一种类型。这时候可能就有人会说了,那不是有泛型吗?这句话也对也错,Java 泛型设计的初衷,确实是为了解决集合类型问题;但是呢,Java 的泛型是基于类型擦除实现的,而不像 C++ 那样支持模板,众所周知,JAVA 的泛型在编译时就会被擦除了,运行时使用原始类型,通过 桥接方法 来支持泛型的调用。退一万步来说,抛去这些不讲,泛型也允许子类类型,例:
List<Object> list = new ArrayList<>();
list.add("1");
List<Number> list1 = new ArrayList<>();
list1.add(new Integer(1));
这种代码在 JAVA 中是完全允许的,不会提示任何错误的。更别说泛型还支持?
、? super Number
、? extends Object
。这种情况就不再是简简单单的单一类型了
基于上诉这种情况考虑,Spring 又新增了一个GenericConverter
接口,用以处理这类情况,其代码如下:
public interface GenericConverter {
@Nullable
Set<ConvertiblePair> getConvertibleTypes();
@Nullable
Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType);
final class ConvertiblePair {
private final Class<?> sourceType;
private final Class<?> targetType;
public ConvertiblePair(Class<?> sourceType, Class<?> targetType) {
Assert.notNull(sourceType, "Source type must not be null");
Assert.notNull(targetType, "Target type must not be null");
this.sourceType = sourceType;
this.targetType = targetType;
}
public Class<?> getSourceType() {
return this.sourceType;
}
public Class<?> getTargetType() {
return this.targetType;
}
@Override
public boolean equals(@Nullable Object other) {
if (this == other) {
return true;
}
if (other == null || other.getClass() != ConvertiblePair.class) {
return false;
}
ConvertiblePair otherPair = (ConvertiblePair) other;
return (this.sourceType == otherPair.sourceType && this.targetType == otherPair.targetType);
}
@Override
public int hashCode() {
return (this.sourceType.hashCode() * 31 + this.targetType.hashCode());
}
@Override
public String toString() {
return (this.sourceType.getName() + " -> " + this.targetType.getName());
}
}
}
看其类图,也能看出点端倪。如下:
转换器管理
在之前只有PropertyEditor
时,都需要一个管理者。跟别说现在了。只不过,现在的管理者更纯粹了,Spring 对其职责进一步的细化,不再负责寻找合适的转换器了,只做单纯的新增、删除。ConverterRegistry
代码如下:
public interface ConverterRegistry {
void addConverter(Converter<?, ?> converter);
<S, T> void addConverter(Class<S> sourceType, Class<T> targetType, Converter<? super S, ? extends T> converter);
void addConverter(GenericConverter converter);
void addConverterFactory(ConverterFactory<?, ?> factory);
void removeConvertible(Class<?> sourceType, Class<?> targetType);
}
这么简单的接口,A君 就不在多费唇舌了,相信大家都能看的懂
转换服务
介绍了这么多接口,还剩下一个功能没有人做,前面说到,ConverterRegistry
已经不再管寻找转换器的事了,那么这件事必须要得有单独的接口来干了,那就是ConversionService
,这个接口的作用就是找到合适的转换器并且进行转换。其代码如下:
public interface ConversionService {
boolean canConvert(@Nullable Class<?> sourceType, Class<?> targetType);
boolean canConvert(@Nullable TypeDescriptor sourceType, TypeDescriptor targetType);
@Nullable
<T> T convert(@Nullable Object source, Class<T> targetType);
@Nullable
Object convert(@Nullable Object source, @Nullable TypeDescriptor sourceType, TypeDescriptor targetType);
}
这个接口也相对简单,A君 也就不多废话了。到这里就结束了吗?不,显然不是,原因在于有时候类型转换的逻辑不仅仅依赖于类型本身,还可能依赖于一些额外的条件。例如,你可能想要只有在某个字段满足特定条件时才进行转换,或者某些类型在某些场景下才进行转换,这就需要专门的匹配方式,于是就有了新的匹配接口ConditionalConverter
。其代码如下:
public interface ConditionalConverter {
boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType);
}
统一管理
定义了这么多个接口,相信除了研究过 Spring 的人之外,其他人看了估计会一脸懵逼,这显然不符合 Spring 一贯拿来即用风格的风格。于是,Spring 又定义了ConfigurableConversionService
接口,诶,啥事也不干。就单纯的整合。代码如下:
public interface ConfigurableConversionService extends ConversionService, ConverterRegistry {
}
接口是定义好了,Spring 也决定让ConfigurableConversionService
提供统一的服务,那么接下来就该是具体实现了。它就是——GenericConversionService
。简单add
、remove
就不看了,不过呢,这里有个特殊的add
,需要留一下。代码如下:
@Override
public void addConverter(Converter<?, ?> converter) {
ResolvableType[] typeInfo = getRequiredTypeInfo(converter.getClass(), Converter.class);
if (typeInfo == null && converter instanceof DecoratingProxy) {
typeInfo = getRequiredTypeInfo(((DecoratingProxy) converter).getDecoratedClass(), Converter.class);
}
//去除一些检验
addConverter(new ConverterAdapter(converter, typeInfo[0], typeInfo[1]));
}
@Override
public void addConverter(GenericConverter converter) {
this.converters.add(converter);
invalidateCache();
}
这个方法就有意思,也就是说,如果使用添加Converter
的话,会被适配成GenericConverter
。嗯?Spring 为什么要这么干呢?A君 百思不得其解,唯一的解释可能就是统一管理了。好了,接下来把视角转到类型转换最重要的两部分内容:
一、如何找到对应的转换器?
方法细化是 Spring 的老传统了,这里直接翻到我们关心的部分内容,即getConverter
方法,代码如下:
protected GenericConverter getConverter(TypeDescriptor sourceType, TypeDescriptor targetType) {
/**
* 利用sourceType、targetType生成key
*/
ConverterCacheKey key = new ConverterCacheKey(sourceType, targetType);
/**
* 缓存获取
*/
GenericConverter converter = this.converterCache.get(key);
if (converter != null) {
return (converter != NO_MATCH ? converter : null);
}
/**
* 去集合里面找
*/
converter = this.converters.find(sourceType, targetType);
if (converter == null) {
converter = getDefaultConverter(sourceType, targetType);
}
/**
* 存入缓存
*/
if (converter != null) {
this.converterCache.put(key, converter);
return converter;
}
this.converterCache.put(key, NO_MATCH);
return null;
}
整个步骤简单明了,分为一下几步:
接着,A君 把目光转到了find
方法上了,看看 Spring 是如何寻找转换器的。代码如下:
public GenericConverter find(TypeDescriptor sourceType, TypeDescriptor targetType) {
// Search the full type hierarchy
//获取源类型的继承结构
List<Class<?>> sourceCandidates = getClassHierarchy(sourceType.getType());
//获取源类型的继承结构
List<Class<?>> targetCandidates = getClassHierarchy(targetType.getType());
for (Class<?> sourceCandidate : sourceCandidates) {
for (Class<?> targetCandidate : targetCandidates) {
ConvertiblePair convertiblePair = new ConvertiblePair(sourceCandidate, targetCandidate);
GenericConverter converter = getRegisteredConverter(sourceType, targetType, convertiblePair);
if (converter != null) {
return converter;
}
}
}
return null;
}
看到这里 A君基本就明白了大半了,JAVA 是面向对象语言,存在着复杂的继承体系,匹配方式肯定不是简单的==
或者equals
就能实现了,于是就出现了上面这出。A君 继续前行了,找到了getRegisteredConverter
方法。代码如下:
private GenericConverter getRegisteredConverter(TypeDescriptor sourceType,
TypeDescriptor targetType, ConvertiblePair convertiblePair) {
// Check specifically registered converters
//通过key直接换取
ConvertersForPair convertersForPair = this.converters.get(convertiblePair);
if (convertersForPair != null) {
/**
* 根据sourceType、targetType找到对应的转换器
*/
GenericConverter converter = convertersForPair.getConverter(sourceType, targetType);
if (converter != null) {
return converter;
}
}
// Check ConditionalConverters for a dynamic match
for (GenericConverter globalConverter : this.globalConverters) {
if (((ConditionalConverter) globalConverter).matches(sourceType, targetType)) {
return globalConverter;
}
}
return null;
}
public GenericConverter getConverter(TypeDescriptor sourceType, TypeDescriptor targetType) {
for (GenericConverter converter : this.converters) {
if (!(converter instanceof ConditionalGenericConverter) ||
((ConditionalGenericConverter) converter).matches(sourceType, targetType)) {
return converter;
}
}
return null;
}
噢,接下来不出意外,就是循环匹配对应的转换器了。到这里后,一切迷雾都散开了
Q:ConditionalGenericConverter
是什么玩意
A:其实也是一个整合接口,继承了GenericConverter
、GenericConverter
Q:Converter
一般都没有实现ConditionalGenericConverter
接口,他是怎么工作的?
A:还记得前面提到的addConverter
方法吗?这个方法会把Converter
包装成ConverterAdapter
,而正是ConverterAdapter
实现了ConditionalGenericConverter
接口
二、如何进行转换
明白了 Spring 是如何找到对应的类型转换器之后,剩下的疑问就是如何进行转换了。A君 把目光转移到了convert
方法上,代码如下:
public Object convert(@Nullable Object source, @Nullable TypeDescriptor sourceType, TypeDescriptor targetType) {
//去除了些检验
/**
* 去转换器集合中寻找
*/
GenericConverter converter = getConverter(sourceType, targetType);
if (converter != null) {
Object result = ConversionUtils.invokeConverter(converter, source, sourceType, targetType);
return handleResult(sourceType, targetType, result);
}
return handleConverterNotFound(source, sourceType, targetType);
}
没想到如此的简单,抛去一些校验后,和寻找转换器的过程一样,找到对应的转换器并调用对应的方法就完事了
兼容老版本
新东西再好也需要一段时间磨合,更何况还存在着这么多存量代码,任何变革都不是一蹴而就的,要时刻考虑着对版本的支持。作为一个热门框架,Spring 当然也充分考虑到了这些,但是,如何兼容呢?简单,新版本的代码直接用ConversionService
,这个跟本不需要考虑了,而老版本用的是PropertyEditorRegistrySupport
,也就是PropertyEditorRegistry
的默认实现类。那在PropertyEditorRegistrySupport
嵌入ConversionService
即可,优先使用PropertyEditor
进行处理,处理不了的就尝试用ConversionService
处理
一些补充
类型包装
在前文的描述中,屡次出现TypeDescriptor
类,它到底是用来干嘛的?实际上,它是对Class
类型的再次封装,JAVA 发展至今,出现了许多新的概念,像:泛型、注解 、接口、父类,这些概念使得一个对象所包含的元数据变得非常复杂,虽然都可以用Class
来进行操作,但是用过反射的小伙伴已经都清楚,这玩意的API没那么直观的,因此,Spring 对Class
进行了进一步的封装,以提供更直观、更简便的 API 来处理这些复杂的类型信息
总结
不得不说,研究源码从高版本开始真的挺折腾人,经历多年的迭代,要充分考虑其历史原因和早期设计。难怪之前听说:研究源码的脉络,从早期版本开始比较容易,A君 对此深有体会。。。