Spring source code reading 15: Initialize MessageSource

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#refreshmethod 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 refreshthis 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 i18nof components. MessageSource is a i18ncomponent 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 codeobtain 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 beanFactorythere is a named messageSourcebean, 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 messageSourceimplements the HierarchicalMessageSource interface and the current context parentattribute is not empty, it will be parentassigned messageSourceas the messageSourceparent message source.

If we don't configure a MessageSource in the configuration file, Spring will create one, assign it to a messageSourcemember variable of the current context, and register beanFactoryit.

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.

Guess you like

Origin juejin.im/post/7135649446930415653