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篇
第五部分——Boot篇
第五十二章 Spring之再进一步——Boot
第五十三章 Spring之假如让你来写Boot——环境篇
第五十四章 Spring之假如让你来写Boot——注解篇(上)
第五十五章 Spring之假如让你来写Boot——注解篇(下)
第五十六章 Spring之假如让你来写Boot——SPI篇
第五十七章 Spring之假如让你来写Boot——配置文件篇(上)
第五十八章 Spring之假如让你来写Boot——配置文件篇(下)
第五十九章 Spring之假如让你来写Boot——番外篇:再谈Bean定义
第六十章 Spring之假如让你来写Boot——自动装配篇
第六十一章 Spring之假如让你来写Boot——番外篇:杂谈Starter
第六十二章 Spring之假如让你来写Boot——番外篇:重构BeanFactory
第六十三章 Spring之假如让你来写Boot——番外篇:再谈ApplicationContext
第六十四章 Spring之假如让你来写Boot——内嵌Web容器篇
第六十五章 Spring之假如让你来写Boot——Main方法启动篇
第六十六章 Spring之最终章——结语篇
文章目录
前言
对于Spring一直都是既熟悉又陌生,说对它熟悉吧,平时用用没啥问题,但面试的时候被问的一脸懵逼,就很尴尬,都不好意思在简历上写着熟悉Spring了
所以决定花点时间研究研究Spring的源码。主要参考的书籍是:《Spring源码深度解析(第2版)》、《Spring揭秘》、《Spring技术内幕:深入解析Spring架构与设计原理(第2版)》
书接上回,在上篇 四十四章 Spring之假如让你来写MVC——异常处理器篇 中,A君 已经完成了 异常处理器 部分的功能了。接下来看看 A君 会有什么骚操作吧
尝试动手写IOC容器
出场人物:A君(苦逼的开发)、老大(项目经理)
背景:老大 要求 A君在一周内开发个简单的 IOC容器
前情提要:A君 已经完成了 异常处理器 部分的功能了 。。。
第三十九版 国际化
“可以可以,A君 。我看你在写测试的时候,似乎有意在回避什么?” 老大 笑道
“额。是!我在写测试的时候全用的是英文,没用中文。” A君 没想到,这点小心思也被 老大 看穿了,只能老实回应道
“果然!那这就是今天要做的事情了,相信你之前也有接触过!” 老大 依旧笑意不减,对 A君 说到
“国际化!” A君 惊呼道
“对!既然你已经知道了,之前也接触过了,我就不在多说什么了,你自己发挥!” 老大 挥了挥手,结束了这场谈话
JDK自带的国际化
又是逝去的记忆,A君 之前玩 国际化 的时候,都不知道多久之前的事情了。东翻翻,西看看。A君 总算回想起来一些东西了,所谓的 国际化,说到底就是显示的不同文字罢了,那如果支持多国语言??AI?既要实时翻译,又得符合语境,现在的AI还没厉害到这种地步,既然AI不太合适,那这时候还是得依靠人来,人要怎么解决呢?肯定不能让一个人在那里盯着实时翻译(笑)。其实要显示的文字基本上在系统成型的时候就已经定好了,这时候预先把要显示的文字定义好,而后再根据地区获取对应的配置就行了。这部分内容 JDK 其实有自带的工具了。A君 简单的写个例子,首先需要定义资源文件,里边配置了各种内容:
而后,就可以根据API直接使用了,代码如下:
@Test
public void v39() {
Locale defaultLocale = Locale.getDefault();
System.out.println("Default Locale: " + defaultLocale);
// 加载对应 Locale 的资源文件
ResourceBundle bundle = ResourceBundle.getBundle("messages", defaultLocale);
// 获取国际化消息
String message = bundle.getString("welcome.message");
System.out.println("不带参数的国际化:" + message);
String messageTemplate = bundle.getString("greeting");
// 替换占位符 {0},这里使用 "张三" 作为参数
message = MessageFormat.format(messageTemplate, "张三");
System.out.println("带参数的国际化:" + message);
}
这时候就会根据对应的资源名称+地区去找对应的文件,来读取要显示的内容。结果如下:
国际化信息
国际化 这部分内容显然不止在Web环境上能使用,各个环境都可以使用,这部分可以先提取出来。为啥不直接使用 JDK 自带的,原因还是一样的,需要对其进行更高层次的封装,提供更为丰富的API。为此,A君 打算先把国际化信息封装成一个对象,方便操作。定义MessageSourceResolvable
接口,代码如下:
/**
* 国际化信息
*/
@FunctionalInterface
public interface MessageSourceResolvable {
/**
* 获取国际化的key,或者说是code
*
* @return
*/
String[] getCodes();
/**
* 获取国际化参数
*
* @return
*/
default Object[] getArguments() {
return null;
}
/**
* 获取默认值
*
* @return
*/
default String getDefaultMessage() {
return null;
}
}
在提供个默认实现,就是一个简单的 JavaBean。DefaultMessageSourceResolvable
代码如下:
/**
* 封装国际化包含的信息,如:
* code
* 参数
* 默认值
*/
public class DefaultMessageSourceResolvable implements MessageSourceResolvable, Serializable {
private final String[] codes;
private final Object[] arguments;
private final String defaultMessage;
public DefaultMessageSourceResolvable(
String[] codes, Object[] arguments, String defaultMessage) {
this.codes = codes;
this.arguments = arguments;
this.defaultMessage = defaultMessage;
}
//省略get/set方法。。。
}
公共国际化
封装好国际化信息之后,接下来就可以定义真正的 国际化 接口了。大体功能和 JDK 自带的差不多,但是之前的例子中,如果Message
存在变量还得用户再次调用相关的类进行转换,这个显然太不方便了。于是,A君 对其进行了改造,新增MessageSource
接口,代码如下:
/**
* 国际化接口
*/
public interface MessageSource {
/**
* 获取信息,带默认值
*
* @param code
* @param args
* @param defaultMessage
* @param locale
* @return
*/
String getMessage(String code, Object[] args, String defaultMessage, Locale locale);
/**
* 获取信息,不带默认值
*
* @param code
* @param args
* @param locale
* @return
* @throws NoSuchMessageException
*/
String getMessage(String code, Object[] args, Locale locale) throws NoSuchMessageException;
/**
* 功能类似,不过MessageSourceResolvable将国际化信息封装成一个类
*
* @param resolvable
* @param locale
* @return
* @throws NoSuchMessageException
*/
String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException;
}
OK,其实这个接口大致功能足够了,再玩的花一点,让它有层级结构。A君 定义HierarchicalMessageSource
接口,代码如下:
/**
* MessageSource子接口,使其拥有层级结构
*/
public interface HierarchicalMessageSource extends MessageSource {
/**
* 获取父级解析器
*
* @return
*/
MessageSource getParentMessageSource();
/**
* 设置父级解析器
*
* @param parent
*/
void setParentMessageSource(MessageSource parent);
}
接下来就是实现类,NO,NO,NO!和之前类型转换一样的道理,MessageSource
可能有多个实现,但是 国际化 信息转换有那么点内容,没必要每个实现类都写一遍,把这些公共的转换封装成一个类就行了。A君 新增MessageSourceSupport
类,代码如下:
/**
* 提取国际化公共转换内容
*/
public abstract class MessageSourceSupport {
private static final MessageFormat INVALID_MESSAGE_FORMAT = new MessageFormat("");
private final Map<String, Map<Locale, MessageFormat>> messageFormatsPerMessage = new HashMap<>();
private boolean alwaysUseMessageFormat = false;
protected String formatMessage(