Fork me on GitHub

使用Springboot进行国际化时自定义读取数据库配置

前言

springboot默认就支持国际化的,而且不需要你过多的做什么配置,只需要在resources/下创建国际化配置文件即可,注意名称必须以messages开始。 messages.properties (默认的语言配置文件,当找不到其他语言的配置的时候,使用该文件进行展示)。 具体的关于springboot的国际化配置我这边就不再过多介绍(包括Locale的设置以及如何根据区域设置语言等),关于页面上得使用可以参考:springboot国际化。在这篇博客中,我要介绍的是一个很有用的功能并且绝大部分人也会用得到,就是
不使用配置文件messages.properties储存国际化语言,而使用数据库进行动态配置,做到无需重启更改配置。

如何使用

MessageSource介绍

Spring提供了一个接口MessageSource用于获取国际化信息,ReloadableResourceBundleMessageSource和ResourceBundleMessageSource都是继承了该接口的一个抽象实现类AbstractMessageSource,在spring官网有一段这样介绍messageSource的话:
spring官网对于messageSource的介绍
图中红框画起来的意思就是,上下文加载的时候会查询messageSource的bean,如果没有就会创建一个名为messageSource放在上下文中… …等等。

在springboot中注入自定义messageSource

通过上面的介绍,我们就可以自己定义自己的messageSource进行配置的读取了。
我这边是把这个放在了业务层,大家用的时候也可以直接放在控制层(一般都放在控制层,要用到),使用@Compnent(“messageSource”)注解声明下bean名称即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
// MyMessageSourceService是我自己的接口 你也可以不需要。使用@Compnent("messageSource")注解就行
@Service("messageSource")
public class MyMessageSource extends AbstractMessageSource implements ResourceLoaderAware, MyMessageSourceService {

ResourceLoader resourceLoader;

// 这个是用来缓存数据库中获取到的配置的 数据库配置更改的时候可以调用reload方法重新加载
// 当然 实际使用者也可以不使用这种缓存的方式
private static final Map<String, Map<String, String>> LOCAL_CACHE = new ConcurrentHashMap<>(256);

@Autowired
SysI18nService sysI18nService;

private final Logger logger = LoggerFactory.getLogger(MyMessageSource.class);

/**
* 初始化
*/
@PostConstruct
public void init() {
this.reload();
}

/**
* 重新将数据库中的国际化配置加载
*/
public void reload() {
LOCAL_CACHE.clear();
LOCAL_CACHE.putAll(loadAllMessageResourcesFromDB());
}

/**
* 从数据库中获取所有国际化配置 这边可以根据自己数据库表结构进行相应的业务实现
* 对应的语言能够取出来对应的值就行了 无需一定要按照这个方法来
*/
public Map<String, Map<String, String>> loadAllMessageResourcesFromDB() {
List<SysI18nBO> list = sysI18nService.findList(new SysI18nAO());
if (CollectionUtils.isNotEmpty(list)) {
final Map<String, String> zhCnMessageResources = new HashMap<>(list.size());
final Map<String, String> enUsMessageResources = new HashMap<>(list.size());
final Map<String, String> idIdMessageResources = new HashMap<>(list.size());
for (SysI18nBO bo : list) {
String name = bo.getModel() + "." + bo.getName();
String zhText = bo.getZhCn();
String enText = bo.getEnUs();
String idText = bo.getInId();
zhCnMessageResources.put(name, zhText);
enUsMessageResources.put(name, enText);
idIdMessageResources.put(name, idText);
}
LOCAL_CACHE.put("zh", zhCnMessageResources);
LOCAL_CACHE.put("en", enUsMessageResources);
LOCAL_CACHE.put("in", idIdMessageResources);
}
return MapUtils.EMPTY_MAP;
}

/**
* 从缓存中取出国际化配置对应的数据 或者从父级获取
*
* @param code
* @param locale
* @return
*/
public String getSourceFromCache(String code, Locale locale) {
String language = locale.getLanguage();
Map<String, String> props = LOCAL_CACHE.get(language);
if (null != props && props.containsKey(code)) {
return props.get(code);
} else {
try {
if (null != this.getParentMessageSource()) {
return this.getParentMessageSource().getMessage(code, null, locale);
}
} catch (Exception ex) {
logger.error(ex.getMessage(), ex);
}
return code;
}
}

// 下面三个重写的方法是比较重要的
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = (resourceLoader == null ? new DefaultResourceLoader() : resourceLoader);
}

@Override
protected MessageFormat resolveCode(String code, Locale locale) {
String msg = getSourceFromCache(code, locale);
MessageFormat messageFormat = new MessageFormat(msg, locale);
return messageFormat;
}

@Override
protected String resolveCodeWithoutArguments(String code, Locale locale) {
return getSourceFromCache(code, locale);
}
}

最后

至此,自定义国际化配置读取数据库已经完成,只需要在更新数据库配置的时候调用一下reload重置一下缓存中的信息即可。

参考博客:spring xml配置自定义读取数据库的messageSource

陈年风楼 wechat
Add my WeChat, share tech-skills to each other 🙆‍