ok,本博客并非入门博客,建议先看具体实现,参考我的博客:SpringBoot系列之i18n国际化多语言支持教程 之后,再来看本博客
MessageSourceAutoConfiguration是国际化语言i18n的自动配置类,然后本博客简单跟一下源码,看一下SpringBoot是怎么实现对locale,也可以说是国际化语言i18n的自动配置
MessageSourceAutoConfiguration是i18n自动配置很关键的类,跟一下其源码,@ResourceBundleCondition是 MessageSourceAutoConfiguration的一个条件主键,当条件符合时候就自动配置,跟一下这个条件主键的源码,MessageSourceAutoConfiguration.ResourceBundleCondition 源码:
protected static class ResourceBundleCondition extends SpringBootCondition {
//定义一个map缓存池
private static ConcurrentReferenceHashMap<String, ConditionOutcome> cache = new ConcurrentReferenceHashMap<>();
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
//2.2.1版本spring.messages.basename必须在application.properties配置
String basename = context.getEnvironment().getProperty("spring.messages.basename", "messages");
ConditionOutcome outcome = cache.get(basename);//缓存拿得到,直接从缓存池读取
if (outcome == null) {//缓存拿不到,重新读取
outcome = getMatchOutcomeForBasename(context, basename);
cache.put(basename, outcome);
}
return outcome;
}
private ConditionOutcome getMatchOutcomeForBasename(ConditionContext context, String basename) {
//读取ResourceBundle资源文件
ConditionMessage.Builder message = ConditionMessage.forCondition("ResourceBundle");
for (String name : StringUtils.commaDelimitedListToStringArray(StringUtils.trimAllWhitespace(basename))) {
for (Resource resource : getResources(context.getClassLoader(), name)) {
if (resource.exists()) {
//匹配resource bundle资源
return ConditionOutcome.match(message.found("bundle").items(resource));
}
}
}
return ConditionOutcome.noMatch(message.didNotFind("bundle with basename " + basename).atAll());
}
//解析资源文件
private Resource[] getResources(ClassLoader classLoader, String name) {
String target = name.replace('.', '/');//spring.messages.basename参数值的点号换成斜杆
try {
return new PathMatchingResourcePatternResolver(classLoader)
.getResources("classpath*:" + target + ".properties");
}
catch (Exception ex) {
return NO_RESOURCES;
}
}
}
ok,这个自动配置类还是比较容易理解的,我们可以知道spring.messages.basename配置ResourceBundle资源文件位置,然后这个自动配置类就会扫描全部的ResourceBundle进行分析匹配后实现自动配置,比较容易理解,所以本博客列举一些注意要点
@Bean
public LocaleResolver localeResolver(){
CustomLocalResolver localResolver = new CustomLocalResolver();
localResolver.setDefaultLocale(webMvcProperties.getLocale());
return localResolver;
}
原理: 跟一下源码,点进LocaleChangeInterceptor类
DispatcherServlet是Spring一个很重要的分发器类,在DispatcherServlet的一个init方法里找到这个LocaleResolver的init方法
这个IOC获取的bean类名固定为localeResolver,写例子的时候,我就因为改了bean类名,导致一直报错,跟了源码才知道Bean类名要固定为localeResolver
继续跟源码,抛异常的时候,也是会获取默认的LocaleResolver的
找到一个properties配置文件,全局搜索
找到资源文件,确认,还是默认为AcceptHeaderLocaleResolver
所以在SpringBoot中默认的Locale解析器类是AcceptHeaderLocaleResolver
spring.mvc.locale=zh_CN
WebMvcAutoConfiguration.localeResolver方法源码,ConditionalOnMissingBean主键的意思是LocaleResolver没有自定义的时候,才作用,ConditionalOnProperty的意思,有配了属性才走这里的逻辑
localeChangeInterceptor.setParamName("lang");
附录:
具体实现,参考我的博客:SpringBoot系列之i18n国际化多语言支持教程