Spring实例化源码解析之MessageSource(七)

前言

在阅读完 registerBeanPostProcessors 源码之后, 下一步就进入到 initMessageSource,这一步主要作用是初始化国际化文件。

源码分析

这段源码是一个Java方法,用于初始化消息源(MessageSource)。在Spring框架中,消息源用于提供本地化消息,例如错误消息或用户界面文本,以便支持国际化和本地化。

让我们逐行分析这段源码:

  1. 获取当前对象的Bean工厂(BeanFactory)。
  2. 检查Bean工厂中是否包含名为"MESSAGE_SOURCE_BEAN_NAME"的本地消息源Bean。这个常量可能是在其他地方定义的消息源Bean的名称。
  3. 如果存在该本地消息源Bean,则从Bean工厂中获取该Bean,并将其设置为当前对象的消息源(messageSource)属性。
  4. 如果当前对象有一个父级对象(parent)且消息源实现了"HierarchicalMessageSource"接口,则将父级对象的消息源设置为当前消息源的父级消息源。
  5. 如果消息源Bean是空的,即没有找到名为"MESSAGE_SOURCE_BEAN_NAME"的本地消息源Bean,则创建一个新的DelegatingMessageSource对象作为空消息源,并将父级消息源设置为当前对象的父级消息源。
  6. 将消息源Bean注册到Bean工厂中。
  7. 日志记录,如果启用了跟踪级别的日志,将打印消息源的使用情况。

这段源码的作用是在Spring应用程序中初始化消息源,以便支持国际化和本地化的消息处理。具体的消息源实现可能会根据应用程序的需求而有所不同。

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;
			// 注册一个messageSource
			beanFactory.registerSingleton(MESSAGE_SOURCE_BEAN_NAME, this.messageSource);
			if (logger.isTraceEnabled()) {
				logger.trace("No '" + MESSAGE_SOURCE_BEAN_NAME + "' bean, using [" + this.messageSource + "]");
			}
		}
	}

官网使用简述

MessageSource

MessageSource在官网中的描述如下图所示:

Spring实例化源码解析之MessageSource(七)_第1张图片

ApplicationContext接口扩展了一个名为MessageSource的接口,因此提供了国际化(“i18n”)功能。Spring还提供了HierarchicalMessageSource接口,它可以按层次结构解析消息。这些接口共同构成了Spring实现消息解析的基础。这些接口定义的方法包括:

  • String getMessage(String code, Object[] args, String default, Locale loc):从MessageSource中检索消息的基本方法。当找不到指定区域设置的消息时,将使用默认消息。传入的任何参数都将成为替换值,使用标准库提供的MessageFormat功能。

  • String getMessage(String code, Object[] args, Locale loc):与前一个方法基本相同,但有一个区别:无法指定默认消息。如果找不到消息,将抛出NoSuchMessageException异常。

  • String getMessage(MessageSourceResolvable resolvable, Locale locale):前面的方法中使用的所有属性也都包装在一个名为MessageSourceResolvable的类中,您可以将其与此方法一起使用。

当加载一个ApplicationContext时,它会自动搜索在上下文中定义的MessageSource bean。该bean的名称必须为messageSource。如果找到这样的bean,所有对前面方法的调用将委托给该消息源。如果找不到消息源,ApplicationContext会尝试查找一个父级上下文,并查找具有相同名称的bean。如果找到,则使用该bean作为MessageSource。如果ApplicationContext找不到任何消息源,将实例化一个空的DelegatingMessageSource以接受上述定义的方法的调用。

Spring提供了三种MessageSource实现,分别是ResourceBundleMessageSource、ReloadableResourceBundleMessageSource和StaticMessageSource。它们都实现了HierarchicalMessageSource以实现嵌套消息。StaticMessageSource很少使用,但提供了以编程方式向源中添加消息的方法。下面是一个示例,展示了ResourceBundleMessageSource的用法:

<beans>
	<bean id="messageSource"
			class="org.springframework.context.support.ResourceBundleMessageSource">
		<property name="basenames">
			<list>
				<value>formatvalue>
				<value>exceptionsvalue>
				<value>windowsvalue>
			list>
		property>
	bean>
beans>

该示例假设您的类路径中有三个资源包,分别称为format、exceptions和windows。通过ResourceBundle对象处理任何解析消息的请求遵循JDK标准的方式。为了示例的目的,假设上述两个资源包文件的内容如下所示:

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

下面的示例展示了运行MessageSource功能的程序。请记住,所有的ApplicationContext实现也是MessageSource实现,因此可以将其转换为MessageSource接口。

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);
}

结果如下:

Alligators rock!

总结一下,MessageSource在名为beans.xml的文件中进行了定义,该文件位于类路径的根目录下。messageSource bean定义通过其basenames属性引用了多个资源包。在basenames属性的列表中传递的三个文件作为文件存在于类路径的根目录下,分别为format.properties、exceptions.properties和windows.properties。

下面的示例展示了传递给消息查找的参数。这些参数将被转换为String对象,并插入到查找消息中的占位符中。

<beans>

	
	<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
		<property name="basename" value="exceptions"/>
	bean>

	
	<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);
	}
}

调用execute()方法后的输出结果如下所示:

The userDao argument is required.

关于国际化(“i18n”),Spring的各种MessageSource实现遵循与标准JDK ResourceBundle相同的区域设置解析和回退规则。简而言之,继续使用先前定义的messageSource示例,如果您想针对英国(en-GB)区域设置解析消息,您将分别创建名为format_en_GB.properties、exceptions_en_GB.properties和windows_en_GB.properties的文件。

通常,区域设置的解析由应用程序的周围环境管理。在下面的示例中,手动指定了用于解析(英国)消息的区域设置:

# 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);
}

运行上述程序后的输出结果如下所示:

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

您还可以使用MessageSourceAware接口来获取对已定义的任何MessageSource的引用。在实现了MessageSourceAware接口的ApplicationContext中定义的任何bean在创建和配置时都会被注入应用程序上下文的MessageSource。

注意

由于Spring的MessageSource基于Java的ResourceBundle,它不会合并具有相同基本名称的资源包,而只会使用找到的第一个资源包。后续具有相同基本名称的消息资源包将被忽略。

作为ResourceBundleMessageSource的替代方案,Spring提供了ReloadableResourceBundleMessageSource类。这个变种支持与标准的基于JDK的ResourceBundleMessageSource实现相同的资源包文件格式,但比它更灵活。特别是,它允许从任何Spring资源位置读取文件(不仅限于类路径),并支持在热加载资源包属性文件时高效地进行缓存。有关详细信息,请参阅ReloadableResourceBundleMessageSource的Java文档。

你可能感兴趣的:(Spring专题,spring,数据库,java,spring,cloud,spring,boot,后端)