乐于分享
好东西不私藏

Spring——国际化之源码

Spring——国际化之源码

    每天进步一点点,大家好,我是大龄码农。

    今天我们聊聊Spring中国际化的源码。

    细心的朋友可能发现了,在Spring—国际化这篇文章的实践中,定义了messageSource对象,尽管在测试类中没用到,但是依然输出了想要的正确结果,这是为什么呢?我们进入源码一探究竟。

    说明:三个重载方法,其实就是对应MessageSource接口中的三个方法
getMessageSource()方法又是什么呢?
    说明:最终还是调用了MessageSource类的方法,从方法实现上看,调用时对象是不为空的。那么,我们就需要看下refresh()这个重要方法中的操作了
    点进去看一下
    方法体就这么多,下面是用到的常量

    说明:结合着代码看,逻辑就很清晰了。如果容器中有定义名为“messageSource”的bean,就使用这个bean,如果没有就创建DelegatingMessageSource对象。

MessageSource接口的方法实现类

    说明:通过上面的分析,大家应该也知道了,我们可以把重点放在AbstractMessageSource类上

    说明:其他两个方法大同小异。

    那么,我们就来看看这里面的两个方法:

    一、getMessageInternal()

        这个方法可以分三部分看,无参、有参、公共配置

        注:这里的参数是指消息中可以被动态填充的内容

    1、无参

    说明:其实这个方法已经被一些常用类重写了。进到这个类简单看下

    说明1:resolveCode()是个抽象方法,需要被子类实现,用于处理消息

    说明2:关于MessageFormat,类似的Format实现类还有:

    2、有参

    说明:先处理参数,resolveCode需要被子类实现

    说明:其实就是将Object[]数组转化为List,只是其中增加了一个递归拆解消息的动作

    3、公共配置

    说明:getCommonMessages()方法就是获取对应的属性

    说明:如果在资源文件中没找到时,就会到公共配置中查找。如果找不到,就会再去查找parentMessageSource(如果配置的话)

    二、getDefaultMessage()

        如果上面都查不到的话,就来到了这个方法。其实这个方法是对应入参类型为MessageSourceResolvable的getMessages()方法

        最后,如果都找不到的话,就会抛出异常NoSuchMessageException

常用

  • ResourceBundleMessageSource

    说明:这是一个比较常用的实现类,通过定义多个properties资源文件,在创建ResourceBundleMessageSource时将定义的资源文件名注入到其basenameSet属性中。当项目启动后,就可以读取资源文件中的配置信息

    我们以resolveCodeWithoutArguments()方法为例,看一下在这个类中是如何重写父类的方法的

    说明:我们看一下getResourceBundle()方法

    说明:doGetBundle()就是获取对应的资源文件,这个方法就不展开讲了,其中定义了一个CacheKey类

    说明:这里创建LoaderReference类,它继承了弱引用。

    同时,还有一个BundleReference类,它继承了软引用

    说明:弱引用和软引用都可以被垃圾回收器回收

    我们回到前面的方法,ResourceBundleMessageSource默认是一个带有缓存功能的类,如果设置了cacheMillis值大于0,则不会走缓存的判断分支,而是有种实现了动态加载静态文件的效果。

    注:在生产环境上不能将cacheMillis值设置为0,因为在每次信息访问时都会校验

    说明:缓存的数据类型

  • ReloadableResourceBundleMessageSource

    看名字就能知道,它有可重加载的功能,简单说下与ResourceBundleMessageSource的区别:

ResourceBundleMessageSource

ReloadableResourceBundleMessageSource

加载资源的方式
通过JDK提供的ResourceBundle加载
通过PropertiesPersister加载资源

静态文件的刷新(cacheMillis值大于0)

每一次都刷新
通过两个时间大小判断是否要刷新(上次刷新时间和[当前时间-cacheMillis])

        对于该类的具体实现细节,有兴趣的同学,可以去研究下。


实践

    一、带参数的消息

    测试类

    说明:我只在一个资源文件中配置了信息,所以测试时必须指定Locale,否则会抛出异常。这个例子比较巧,因为默认Locale就是zh_CN

    问题:如果不传入参数值呢?

    回答:就不替换占位符呗    

    二、公共配置

    说明:这里只是简单说明下,当然也可以使用读取配置文件的形式

    三、默认方法

    说明:大家看下效果就好,主要就是用实例说明一下源码

    总结:
    1、介绍了国际化的源码
    2、用程序讲解了今天的知识