1.前言
本文主要讲Spring IOC容器初始化过程中的国际化的初始化(refresh()中的initMessageSource()方法) 和事件多路广播器的初始化(refresh()中的initApplicationEventMulticaster ()方法),如想看之前的内容可查看 Spring IOC容器初始化原理分析 (第三节)
2.refresh() 源码
如果需要复制代码,可前往Spring IOC容器初始化原理分析 (第一节)
3. initMessageSource() 方法详解
3.1 MessageSource 接口
- 为了待会讲起来大家更容易理解,所以这里我们先讲一下 MessageSource 接口,源码如下:
我们可以看到在 MessageSource 中 里面包含三个同名重构方法。
-
第一个方法:解析code对应的信息进行返回,如果对应的code不能被解析则返回默认信息defaultMessage。
-
第二个方法:解析code对应的信息进行返回,如果对应的code不能被解析则抛出异常 NoSuchMessageException 参数比上面少一个 String defaultMessage ,即当对应code的信息不存在时,直接抛出NoSuchMessageException ,不返回默认值
-
第三个方法:通过传递的 MessageSourceResolvable 对应来解析对应的信息 , MessageSourceResolvable 是一个接口,大家可以自定义解析 MessageSource 的规则。
-
参数说明:
- @param code 需要进行解析的code,对应资源文件中的一个属性名鼓励用户将消息名称基于限定的类或包名称,以避免潜在的冲突并确保最大程度的清晰。
- @param args 一个参数数组 用于填充消息中的参数(消息中的参数看起来像“{0}”,“{1,date}”,“{2,time}”)
- @param defaultMessage 当对应code对应的信息不存在时需要返回的默认值
- @param locale 对应的Locale
- @param MessageSourceResolvable MessageSource的解析类
- 查看initMessageSource()的源码 因为这里面的代码比较长,为了方便起见,我就直接在源码上写注释了。
protected void initMessageSource() {
//得到当前的 beanFactory
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
// 判断当前 beanFactory 中是否包含有名为 messageSource 的 bean
if (beanFactory.containsLocalBean(MESSAGE_SOURCE_BEAN_NAME)) {
// 如果有就从从其中取出来,赋值给自己的MessageSource
this.messageSource = beanFactory.getBean(MESSAGE_SOURCE_BEAN_NAME, MessageSource.class);
// Make MessageSource aware of parent MessageSource.
// 当这个容器的父类 beanFactory 不为空时 ,且 这个MessageSource对象是 HierarchicalMessageSource 类型时
if (this.parent != null && this.messageSource instanceof HierarchicalMessageSource) {
// 先把它强转成 HierarchicalMessageSource 类型
HierarchicalMessageSource hms = (HierarchicalMessageSource) this.messageSource;
// 如果父类 MessageSource 为空, 则设置父类MessageSource 为内部的parent messageSource
if (hms.getParentMessageSource() == null) {
// Only set parent context as parent MessageSource if no parent MessageSource
// registered already.
hms.setParentMessageSource(getInternalParentMessageSource());
}
}
// 日志记录
if (logger.isTraceEnabled()) {
logger.trace("Using MessageSource [" + this.messageSource + "]");
}
}
else {
// Use empty MessageSource to be able to accept getMessage calls.
// 如果容器中没有 MessageSource 则它自己帮我们创建一个 DelegatingMessageSource bean
DelegatingMessageSource dms = new DelegatingMessageSource();
// 设置它的父类 MessageSource
dms.setParentMessageSource(getInternalParentMessageSource());
this.messageSource = dms;
// 把这个bean 注册到beanFactory 中去
beanFactory.registerSingleton(MESSAGE_SOURCE_BEAN_NAME, this.messageSource);
if (logger.isTraceEnabled()) {
logger.trace("No '" + MESSAGE_SOURCE_BEAN_NAME + "' bean, using [" + this.messageSource + "]");
}
}
}
DelegatingMessageSource: 消息源解析委派类(用户未指定时,Spring IOC 容器初始u啊默认使用当前类),功能比较简单:将字符串和参数数组格式化为一个消息字符串
这部分的代码也比较简单,总结下来就是: 优先读取用户配置的 MessageSource , 如果没有Spring 默认帮我们创建一个 DelegatingMessageSource
4. initApplicationEventMulticaster()
这个方法就是,先判断有没有自定义的 ApplicationEventMulticaster ,没有的话就注册一个。
SimpleApplicationEventMulticaster 。
protected void initApplicationEventMulticaster() {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
// 判断 容器中有没有 applicationEventMulticaster 这个bean
if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
//有的话 就把它赋值给自己的 applicationEventMulticaster
this.applicationEventMulticaster =
beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
if (logger.isTraceEnabled()) {
logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
}
}
else {
// 没有的话 就新建一个SimpleApplicationEventMulticaster对象,注册到beanFactory 中去
this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
if (logger.isTraceEnabled()) {
logger.trace("No '" + APPLICATION_EVENT_MULTICASTER_BEAN_NAME + "' bean, using " +
"[" + this.applicationEventMulticaster.getClass().getSimpleName() + "]");
}
}
}
注意:SimpleApplicationEventMulticaster是要与 后面的 ApplicationListener 搭配使用,这个在后续讲到 ApplicationListener这个的时候 我在细讲,它们俩搭配属于设计模式中的 观察者模式(ps:这个后续我会细讲),用来监听回调容器中的事件。
总结:今天讲的这两个方法,其实设计思想都一样,先取用户配置,如果有就直接使用用户的,没有的话spring IOC 容器则会帮我们创建一个默认。现在可能很多人会觉得spring 配置繁杂,springboot 就很人性化,实际上 我们深入学习spring 就会发现 spring boot 其实是在spring 的基础上给我们 又封装了一层,使得我们少写很多配置文件。