This is the 30th day of my participation in the "Nuggets Daily New Plan·August Update Challenge", click to view the details of the event
Based on Spring Framework v5.2.6.RELEASE
Continued from the previous article: Spring source code reading 14: Registering BeanPostProcessor
Recap
In the previous article, ApplicationContext initializes the Spring container , it is mentioned that the AbstractApplicationContext#refresh
method is a very important method, which includes the entire process of the Spring container initialization. A series of recent articles are in-depth analysis of the specific principles of each step in this method. This article then analyzes the initialization of MessageSource, which is refresh
this line of code in the method:
// Initialize message source for this context.
initMessageSource();
复制代码
Let's first introduce what a MessageSource is.
What is MessageSource
In Spring, MessageSource is mainly used to deal with the problem of internationalization, which is often said i18n
(Internationalization).
For example, if the website we develop needs to adapt to the languages of multiple countries and regions, we need to use different languages to display the content of the website. This requires the use i18n
of components. MessageSource is a i18n
component in Spring.
MessageSource itself is an interface, the interface is defined as follows:
public interface MessageSource {
@Nullable
String getMessage(String code, @Nullable Object[] args, @Nullable String defaultMessage, Locale locale);
String getMessage(String code, @Nullable Object[] args, Locale locale) throws NoSuchMessageException;
String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException;
}
复制代码
All three methods are used to code
obtain the localized text of a specific message by a specified identifier ( ) and language information. Important implementation classes related to MessageSource and the relationship between them are as follows:
There is an important HierarchicalMessageSource sub-interface, which provides the function of hierarchical processing of messages. Following is the definition of the interface.
public interface HierarchicalMessageSource extends MessageSource {
void setParentMessageSource(@Nullable MessageSource parent);
@Nullable
MessageSource getParentMessageSource();
}
复制代码
简而言之,就是给 MessageSource 提供了父子关系,开发者可以用一个父级的 MessageSource 处理公共的消息内容,子级的 MessageSource 只需要处理对应模块的消息内容即可。当通过子级的 MessageSource 获取不到消息内容是,就会到父级的 MessageSource 中获取。
接下来是两个比较典型的实现类,ResourceBundleMessageSource 和 StaticMessageSource。StaticMessageSource 是一个静态的消息源,需要提前硬编码内容,因此不是很常用。比较常用的是 ResourceBundleMessageSource,它从给定路径的消息文件中加载内容,供程序获取。
初始化 MessageSource
接下来看 Spring 初始化 MessageSource 的过程,也就是 initMessageSource 的代码。
protected void initMessageSource() {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (beanFactory.containsLocalBean(MESSAGE_SOURCE_BEAN_NAME)) {
this.messageSource = beanFactory.getBean(MESSAGE_SOURCE_BEAN_NAME, MessageSource.class);
// Make MessageSource aware of parent MessageSource.
if (this.parent != null && this.messageSource instanceof HierarchicalMessageSource) {
HierarchicalMessageSource hms = (HierarchicalMessageSource) this.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.
DelegatingMessageSource dms = new DelegatingMessageSource();
dms.setParentMessageSource(getInternalParentMessageSource());
this.messageSource = dms;
beanFactory.registerSingleton(MESSAGE_SOURCE_BEAN_NAME, this.messageSource);
if (logger.isTraceEnabled()) {
logger.trace("No '" + MESSAGE_SOURCE_BEAN_NAME + "' bean, using [" + this.messageSource + "]");
}
}
}
复制代码
首先,获取到当前上下文内部的 BeanFactory,之后整个方法体都是一个if-else
语句块,判断条件是 BeanFactory 中是否包含名为MESSAGE_SOURCE_BEAN_NAME
的 Bean。
public static final String MESSAGE_SOURCE_BEAN_NAME = "messageSource";
复制代码
也就是 Spring 在容器中查找 MessageSource 时使用的是固定的名称messageSource
,因此,如果要在配置文件中配置 MessageSource,需要把bean
标签的id
属性设置为messageSource
,才能被 Spring 作为消息源。如果我们在配置文件中配置了messageSource
,则会执行if
语句块的逻辑,否则执行else
语句块的逻辑。下面分别来说分析。
If beanFactory
there is a named messageSource
bean, get an instance of this bean and set it as the MessageSource of the current context. There is also a judgment here. If the obtained one messageSource
implements the HierarchicalMessageSource interface and the current context parent
attribute is not empty, it will be parent
assigned messageSource
as the messageSource
parent message source.
If we don't configure a MessageSource in the configuration file, Spring will create one, assign it to a messageSource
member variable of the current context, and register beanFactory
it.
The message source type created here is DelegatingMessageSource. The logic of DelegatingMessageSource is to directly obtain the message from the parent message source, or generate a default message according to the provided content, otherwise it will return null or throw an exception. Its role is that when the method in the MessageSource of the current context is called, there is an implementation class to respond to the method call, and that's it.
follow-up
This part of the content introduced in this article is relatively simple, and the use of MessageSource in actual development is not high. The code for Spring container initialization will be analyzed later.