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在官网中的描述如下图所示:

在这里插入图片描述

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>format</value>
				<value>exceptions</value>
				<value>windows</value>
			</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>

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

调用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文档。

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
`refresh()` 是 Spring 框架中的一个方法,它用于刷新应用程序上下文(ApplicationContext)以更新其内部状态。在 Spring 框架中,`refresh()` 方法是非常重要的,因为它负责完成应用程序上下文的初始化和配置,并准备好所有的单例 bean 以供使用。 下面是 `refresh()` 方法的主要流程: 1. 准备刷新过程中需要用到的变量和标志位; 2. 调用 `prepareRefresh()` 方法,进行一些预处理工作; 3. 调用 `obtainFreshBeanFactory()` 方法,创建 BeanFactory 并进行一些初始化工作; 4. 调用 `prepareBeanFactory(beanFactory)` 方法,对 BeanFactory 进行一些后续处理; 5. 调用 `postProcessBeanFactory(beanFactory)` 方法,对 BeanFactory 进行后置处理; 6. 调用 `invokeBeanFactoryPostProcessors(beanFactory)` 方法,执行 BeanFactoryPostProcessor 的 postProcessBeanFactory() 方法; 7. 调用 `registerBeanPostProcessors(beanFactory)` 方法,注册 BeanPostProcessor 实例; 8. 调用 `initMessageSource()` 方法,初始化 MessageSource 组件; 9. 调用 `initApplicationEventMulticaster()` 方法,初始化 ApplicationEventMulticaster 组件; 10. 调用 `onRefresh()` 方法,进行一些自定义的刷新工作; 11. 调用 `registerListeners()` 方法,注册事件监听器; 12. 调用 `finishBeanFactoryInitialization(beanFactory)` 方法,完成所有非延迟初始化的单例 bean 的初始化工作; 13. 调用 `finishRefresh()` 方法,完成上下文的刷新工作。 需要注意的是,`refresh()` 方法在执行过程中会涉及到很多细节,比如如何处理环境变量、如何处理自定义的 bean 定义、如何处理多个上下文之间的关系等等。如果需要深入了解 `refresh()` 方法的实现细节,可以查看 Spring 框架的源代码。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Thomas & Friends

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值