【深度剖析】Spring1.0源码--6、loadOptions和initMessageSource方法

前言

本文主要介绍SpringApplication在初始化的过程中,执行的两个动作,loadOptions和initMessageSource,loadOptions主要是控制是否允许重复调用刷新配置;initMessageSource主要初始化消息源,根据不同的地区返回不同的消息模板,一般用web用于支持i18n。

loadOptions

public static final String OPTIONS_BEAN_NAME = "contextOptions";
	private void loadOptions() {
		try {
			this.contextOptions = (ContextOptions) getBean(OPTIONS_BEAN_NAME);
		} catch (NoSuchBeanDefinitionException ex) {
			logger.info("No options bean (\"" + OPTIONS_BEAN_NAME + "\") found: using default");
			this.contextOptions = new ContextOptions();
		}
	}

先来看loadOptions方法,首先是从bean工厂中获取名称为contextOptions的bean,由于xml没有配置,此时会抛异常。那么spring的骚操作开始,进入异常,默认创建ContextOptions。

public class ContextOptions {
	private boolean reloadable = true;
	public boolean isReloadable() {
		return reloadable;
	}

	public void setReloadable(boolean reloadable) {
		this.reloadable = reloadable;
	}
}

这个类很简单,只有一个boolean类型的属性reloadable,看名字跟加载相关。如果使用idea可以按住ctrl+鼠标左键,看下哪里使用,最后发现在refresh方法入口处用到,用于判断能不能重复加载配置,其实就是能不能重复执行refresh方法。

	public final void refresh() throws ApplicationContextException, BeansException {
		if (this.contextOptions != null && !this.contextOptions.isReloadable())
			throw new ApplicationContextException("Forbidden to reload config");
			.....省略.....
	}

initMessageSource

在介绍initMessageSource方法之前,先假设有个需求:针对不同的国家和不同的消息编号返回不同的消息模板,你会怎么做?

首先有一个容器来存放消息模板,想要根据地区和编号获取模板(如上图),使用数组可不可以?可以,但每次匹配的时候需要变量数组,性能不高,时间复杂度O(n)。由于是一个映射关系,那么可以考虑使用map,时间复杂度O(1)。选好容器,地区和编号作为key,value就是消息模板。写出来的代码如下:

public static void main(String[] args){
      	Map<String,String> map = new HashMap<>();
		String code = "110";
        map.put("Chinese."+code,"我是{0}");
        map.put( + "USA."+code,"I am {0}");

        MessageFormat format = new MessageFormat(map.get( "Chinese."+code));
        System.out.println(format.format(new String[]{"AAA"}));

        MessageFormat format1 = new MessageFormat("USA."+code));
        System.out.println(format1.format(new String[]{"AAA"}));
}

该功能主要分两步:
第一步往消息里面添加模板;
第二步使用的时候拿到模板然后格式化。

上面是自己定义的地区,那有没有一个统一的规范呢?在java中一个类Locale,定义了很多地区,我们简单看看。相比于我们自己定义的,Locale适用性很高,可以说是jdk定义的标准,只要用java,那么都是这样。于是代码调整为

      	Map<String,String> map = new HashMap<>();
		String code = "110";
        map.put(Locale.SIMPLIFIED_CHINESE.toString() + "."+code,"我是{0}");
        map.put(Locale.US.toString() + "."+code,"I am {0}");

        MessageFormat format = new MessageFormat(map.get(Locale.SIMPLIFIED_CHINESE.toString() + "."+code));
        System.out.println(format.format(new String[]{"中国人"}));

        MessageFormat format1 = new MessageFormat(map.get(Locale.US.toString() + "."+code));
        System.out.println(format1.format(new String[]{"Ammerica "}));

有了上面的铺垫,接下来我们看看initMessageSource方法干了什么?还是spring的骚操作,抛异常默认创建StaticMessageSource。

	private void initMessageSource() {
		try {
			this.messageSource = (MessageSource) getBean(MESSAGE_SOURCE_BEAN_NAME);
		}
		catch (NoSuchBeanDefinitionException ex) {
			this.messageSource = new StaticMessageSource();
		}
	}

什么时候会使用到呢?来看看getMessage方法,可以发现,跟我们写的差不多,只是将其封装起来而已,比如添加消息模板和解析key,另外对format进行了缓存,避免每次都创建,具体的大家看下源码就知道了。

	public final String getMessage(String code, Object args[], Locale locale) throws NoSuchMessageException {
			String mesg = resolve(code, locale); // 根据code和locale找到模板
			if (locale == null)
				locale = defaultLocale;
			MessageFormat format = null;
			String formatKey = messageKey(locale, code);
			synchronized (formats) {
				format = (MessageFormat) formats.get(formatKey);
				if (format == null) {
					format = new MessageFormat(escape(mesg));
					formats.put(formatKey, format);
				}
			}
			return (format.format(args));
	}
	
	// messages中存放着各种模板
	protected String resolve(String code, Locale locale) {
		return (String) this.messages.get(messageKey(locale, code));
	}

题外话:不管formats中是否已经存在MessageFormat都直接上锁,其实可以优化为DCL(Double Check Lock),如果MessageFormat存在则直接使用,没必要加锁,代码如下

public final String getMessage(String code, Object args[], Locale locale) throws NoSuchMessageException {
			String mesg = resolve(code, locale); // 根据code和locale找到模板
			if (locale == null)
				locale = defaultLocale;
			MessageFormat format = null;
			String formatKey = messageKey(locale, code);
			format = (MessageFormat) formats.get(formatKey); // 存在则直接使用,没必要进入锁
			if(format == null){
				synchronized (formats) {
					format = (MessageFormat) formats.get(formatKey);
					if (format == null) {
						format = new MessageFormat(escape(mesg));
						formats.put(formatKey, format);
					}
				}
			}
			return (format.format(args));
	}

MessageSource主要是针对国际化,我们很少用到,除非涉及到境外的业务,所以我们简单看一下就可以, 看不懂也没有关系,这个方法并不重要,不影响我们对整体框架了解。

总结

主要介绍loadOptions和initMessageSource两个方法的逻辑,loadOptions用于控制是否允许重复加载和刷新配置,initMessageSource用于初始化消息源或者说消息模板,提到synchronized使用DCL优化。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值