Spring instantiation source code analysis of MessageSource (7)

Foreword

After reading the source code of registerBeanPostProcessors, the next step is to enter initMessageSource. The main function of this step is to initialize the internationalization file.

Source code analysis

This source code is a Java method used to initialize the message source (MessageSource). In the Spring framework, message sources are used to provide localized messages, such as error messages or user interface text, to support internationalization and localization.

Let’s analyze this source code line by line:

  1. Get the Bean factory (BeanFactory) of the current object.
  2. Check whether the bean factory contains a local message source bean named “MESSAGE_SOURCE_BEAN_NAME”. This constant may be the name of a message source bean defined elsewhere.
  3. If the local message source bean exists, obtain the bean from the bean factory and set it as the message source (messageSource) property of the current object.
  4. If the current object has a parent object (parent) and the message source implements the “HierarchicalMessageSource” interface, the parent object’s message source is set to the parent message source of the current message source.
  5. If the message source bean is empty, that is, no local message source bean named “MESSAGE_SOURCE_BEAN_NAME” is found, create a new DelegatingMessageSource object as an empty message source and set the parent message source to the parent message source of the current object.
  6. Register the message source bean to the bean factory.
  7. Logging, if trace level logging is enabled, will print the usage of the message source.

The purpose of this source code is to initialize the message source in the Spring application to support internationalization and localization message processing. The specific message source implementation may vary based on the needs of the application.

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 & amp; & amp; 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;
//Register a messageSource
beanFactory.registerSingleton(MESSAGE_SOURCE_BEAN_NAME, this.messageSource);
if (logger.isTraceEnabled()) {<!-- -->
logger.trace("No '" + MESSAGE_SOURCE_BEAN_NAME + "' bean, using [" + this.messageSource + "]");
}
}
}

Brief introduction to using the official website

MessageSource

The description of MessageSource on the official website is as shown below:

The ApplicationContext interface extends an interface called MessageSource, thus providing internationalization (“i18n”) functionality. Spring also provides the HierarchicalMessageSource interface, which can parse messages hierarchically. These interfaces together form the basis for Spring’s implementation of message parsing. The methods defined by these interfaces include:

  • String getMessage(String code, Object[] args, String default, Locale loc): The basic method to retrieve messages from MessageSource. When a message for the specified locale is not found, the default message is used. Any parameters passed in will become replacement values, using the MessageFormat function provided by the standard library.

  • String getMessage(String code, Object[] args, Locale loc): Basically the same as the previous method, but with one difference: the default message cannot be specified. If the message is not found, a NoSuchMessageException will be thrown.

  • String getMessage(MessageSourceResolvable resolvable, Locale locale): All the properties used in the previous method are also wrapped in a class called MessageSourceResolvable which you can use with this method.

When an ApplicationContext is loaded, it automatically searches for MessageSource beans defined in the context. The bean’s name must be messageSource. If such a bean is found, all calls to the preceding methods will be delegated to that message source. If the message source cannot be found, the ApplicationContext attempts to find a parent context and looks for a bean with the same name. If found, the bean is used as the MessageSource. If the ApplicationContext cannot find any message sources, an empty DelegatingMessageSource will be instantiated to accept calls to the methods defined above.

Spring provides three MessageSource implementations, namely ResourceBundleMessageSource, ReloadableResourceBundleMessageSource and StaticMessageSource. They all implement HierarchicalMessageSource to implement nested messages. StaticMessageSource is rarely used but provides a way to programmatically add messages to the source. Here is an example showing the usage of ResourceBundleMessageSource:

<beans>
<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basenames">
<list>
<value>format</value>
<value>exceptions</value>
<value>windows</value>
</list>
</property>
</bean>
</beans>

This example assumes you have three resource bundles in your classpath, called format, exceptions, and windows. Any request to parse a message is handled through a ResourceBundle object in a JDK standard manner. For example purposes, assume that the contents of the above two resource pack files look like this:

# in format.properties
message=Alligators rock!
# in exceptions.properties
argument.required=The {0} argument is required.

The following example shows a program running the MessageSource function. Remember that all ApplicationContext implementations are also MessageSource implementations, so they can be converted to the MessageSource interface.

public static void main(String[] args) {<!-- -->
MessageSource resources = new ClassPathXmlApplicationContext("beans.xml");
String message = resources.getMessage("message", null, "Default", Locale.ENGLISH);
System.out.println(message);
}

The result is as follows:

Alligators rock!

To summarize, the MessageSource is defined in a file called beans.xml, which is located at the root of the classpath. The messageSource bean definition references multiple resource bundles through its basenames attribute. The three files passed in the list of basenames properties exist as files at the root of the classpath, namely format.properties, exceptions.properties, and windows.properties.

The following example shows the parameters passed to the message lookup. These parameters are converted to String objects and inserted into placeholders in the lookup message.

<beans>

<!-- this MessageSource is being used in a web application -->
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="exceptions"/>
</bean>

<!-- lets inject the above MessageSource into this POJO -->
<bean id="example" class="com.something.Example">
<property name="messages" ref="messageSource"/>
</bean>

</beans>
public class Example {<!-- -->

private MessageSource messages;

public void setMessages(MessageSource messages) {<!-- -->
this.messages = messages;
}

public void execute() {<!-- -->
String message = this.messages.getMessage("argument.required",
new Object [] {<!-- -->"userDao"}, "Required", Locale.ENGLISH);
System.out.println(message);
}
}

The output after calling the execute() method is as follows:

The userDao argument is required.

Regarding internationalization (“i18n”), Spring’s various MessageSource implementations follow the same locale resolution and fallback rules as the standard JDK ResourceBundle. In short, continuing with the messageSource example defined earlier, if you wanted to parse messages for the UK (en-GB) locale, you would create files named format_en_GB.properties, exceptions_en_GB.properties, and windows_en_GB.properties respectively.

Typically, resolution of locales is managed by the application’s surrounding environment. In the following example, the locale used to parse (UK) messages is manually specified:

# in exceptions_en_GB.properties
argument.required=Ebagum lad, the ''{0}'' argument is required, I say, required.
public static void main(final String[] args) {<!-- -->
MessageSource resources = new ClassPathXmlApplicationContext("beans.xml");
String message = resources.getMessage("argument.required",
new Object [] {<!-- -->"userDao"}, "Required", Locale.UK);
System.out.println(message);
}

The output after running the above program is as follows:

Ebagum lad, the 'userDao' argument is required, I say, required.

You can also use the MessageSourceAware interface to get a reference to any MessageSource you have defined. Any bean defined in an ApplicationContext that implements the MessageSourceAware interface will be injected into the application context’s MessageSource when it is created and configured.

Note

Since Spring’s MessageSource is based on Java’s ResourceBundle, it will not merge resource bundles with the same base name, but will only use the first resource bundle found. Subsequent message resource bundles with the same base name will be ignored.

As an alternative to ResourceBundleMessageSource, Spring provides the ReloadableResourceBundleMessageSource class. This variant supports the same resource bundle file format as the standard JDK-based ResourceBundleMessageSource implementation, but is more flexible than it. In particular, it allows files to be read from any Spring resource location (not limited to the classpath) and supports efficient caching when hot-loading resource bundle properties files. For more information, see the Java documentation for ReloadableResourceBundleMessageSource.