springmvc +mybatis 动态国际化(使用数据库保存国际化配置)
程序员文章站
2022-05-16 10:27:44
...
springMVC的动态国际化,是使用数据库保存国际化对应的value值,系统启动后加载所有国际化配置到缓存(也可以直接读取数据库,效率低,不推荐),修改数据库中对应的国际化value值后刷新。自定义国际化的解析类,从缓存中取得对应的value值
1.定义国际化对应的实体类,以及对应的mybatis mapper类
//entry
public class SysMessageResource implement Serializable {
private static final long serialVersionUID = 1L;
private String code; // 键值
private String lang; // 国家区域
private String label; // 显示的标签
//....
}
// mapper
public class SysMessageResourceMapper {
List<SysMessageResource> findList(SysMessageResource resource);
// insert
// update
// delete
}
2.定义SpringContextHolder:
可以根据class直接获取对应的spring容器管理的对象,(因为我是在Utils的static块中加载的国际化资源,不能使用@autowried注入,当然也可以不用该方法,可以将Utils标为spring对象,继承InitializingBean,在afterPropertiesSet方法中加载国际化资源)
@Service
@Lazy(false)
public class SpringContextHolder implements ApplicationContextAware, DisposableBean {
private static ApplicationContext applicationContext = null;
private static Logger logger = LoggerFactory.getLogger(SpringContextHolder.class);
/**
* 取得存储在静态变量中的ApplicationContext.
*/
public static ApplicationContext getApplicationContext() {
assertContextInjected();
return applicationContext;
}
/**
* 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
*/
@SuppressWarnings("unchecked")
public static <T> T getBean(String name) {
assertContextInjected();
return (T) applicationContext.getBean(name);
}
/**
* 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
*/
public static <T> T getBean(Class<T> requiredType) {
assertContextInjected();
return applicationContext.getBean(requiredType);
}
/**
* 清除SpringContextHolder中的ApplicationContext为Null.
*/
public static void clearHolder() {
if (logger.isDebugEnabled()){
logger.debug("清除SpringContextHolder中的ApplicationContext:" + applicationContext);
}
applicationContext = null;
}
/**
* 实现ApplicationContextAware接口, 注入Context到静态变量中.
*/
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
SpringContextHolder.applicationContext = applicationContext;
}
public static String getStatic(){
return SpringContextHolder.getApplicationContext().getApplicationName()+ "/static";
}
/**
* 实现DisposableBean接口, 在Context关闭时清理静态变量.
*/
@Override
public void destroy() throws Exception {
SpringContextHolder.clearHolder();
}
/**
* 检查ApplicationContext不为空.
*/
private static void assertContextInjected() {
Validate.validState(applicationContext != null, "applicaitonContext属性未注入, 请在applicationContext.xml中定义SpringContextHolder.");
}
}
3.定义ResourceUtils类,用来加载国际化资源到缓存,或者更新缓存,要在对应的mapper执行插入,更新,删除后执行该utils对应的方法更新缓存,或者reload
public class MessageResourceUtils {
private static Logger logger = LoggerFactory.getLogger(MessageResourceUtils.class);
//可以使用别的方式,查询数据库
private static SysMessageResourceMapper sysMessageResourceMapper = SpringContextHolder.getBean(SysMessageResourceMapper.class);
public static final String MESSAGE_SOURCE_KEY = "message.source.key";
static {
reloadCache();
}
/**
* 根据local获取bundle
* @param locale
* @return
*/
public static Properties getResourceProperties(Locale locale){
return getResourceMap().get(locale);
}
/**
* get resource map of all locale
* @return
*/
public static Map<Locale, Properties> getResourceMap() {
Map<Locale, Properties> localeMap = (Map<Locale, Properties>) CacheUtils.get(MESSAGE_SOURCE_KEY);
if (localeMap == null) {
localeMap = generateLocaleProperties();
CacheUtils.put(MESSAGE_SOURCE_KEY, localeMap);
}
return localeMap;
}
/**
* reload message resource
*/
public static void reloadCache() {
// 可以使用别的缓存方式,可以选择多种方式,在此不提供缓存实现
CacheUtils.put(MESSAGE_SOURCE_KEY, generateLocaleProperties());
}
/**
* 清除Message缓存
* @param user
*/
public static void clearCache(){
CacheUtils.remove(MESSAGE_SOURCE_KEY);
}
/**
* reload message
* @param locale locale
*/
public static void reloadLocaleMessage(Locale locale) {
if (locale == null) {
return;
}
Map<Locale, Properties> localeMap = getResourceMap();
SysMessageResource search = new SysMessageResource();
search.setLang(locale.toString());
List<SysMessageResource> resourcesList = sysMessageResourceMapper.findList(search);
Properties prop = new Properties();
for (SysMessageResource messageResource : resourcesList) {
if (StringUtils.isBlank(messageResource.getLang()) || StringUtils.isBlank(messageResource.getCode())) {
continue;
}
prop.put(messageResource.getCode(), StringUtils.defaultString(messageResource.getLabel()));
}
localeMap.put(locale, prop);
}
/**
* insert message
* @param resource
*/
public static void insertMessageResource(SysMessageResource resource) {
if (StringUtils.isBlank(resource.getLang()) || StringUtils.isBlank(resource.getCode())) {
return;
}
Locale locale = org.springframework.util.StringUtils.parseLocaleString(resource.getLang());
if (locale != null) {
Properties prop = getResourceProperties(locale);
if (prop == null) {
prop = new Properties();
getResourceMap().put(locale, prop);
}
prop.put(resource.getCode(), resource.getLabel());
}
}
/**
* update message
* @param resource
*/
public static void updateMessageResource(SysMessageResource resource) {
insertMessageResource(resource);
}
/**
* delete message
* @param resource
*/
public static void deleteMessageResource(SysMessageResource resource) {
if (StringUtils.isBlank(resource.getLang()) || StringUtils.isBlank(resource.getCode())) {
return;
}
Locale locale = org.springframework.util.StringUtils.parseLocaleString(resource.getLang());
if (locale != null) {
Properties prop = getResourceProperties(locale);
if (prop != null) {
prop.remove(resource.getCode());
}
}
}
/**
* load all message
* @return
*/
private static Map<Locale, Properties> generateLocaleProperties() {
Map<Locale, Properties> localPropertiesMap = new HashMap<>();
List<SysMessageResource> resourcesList = Collections.emptyList();
try {
resourcesList = sysMessageResourceMapper.findList(new SysMessageResource());
} catch (Exception e) {
// TODO Auto-generated catch block
logger.warn("Query messageResource from database error");
}
for (SysMessageResource messageResource : resourcesList) {
if (StringUtils.isBlank(messageResource.getLang()) || StringUtils.isBlank(messageResource.getCode())) {
continue;
}
Locale locale = org.springframework.util.StringUtils.parseLocaleString(messageResource.getLang());
if (locale != null) {
Properties prop = localPropertiesMap.get(locale);
if (prop == null) {
prop = new Properties();
localPropertiesMap.put(locale, prop);
}
prop.put(messageResource.getCode(), StringUtils.defaultString(messageResource.getLabel()));
}
}
return localPropertiesMap;
}
}
4.定义message解析类,继承自spring的AbstractMessageSource :
public class ResourceBundleMessageSource extends AbstractMessageSource {
public ResourceBundleMessageSource() {
super();
}
@Override
protected MessageFormat resolveCode(String code, Locale locale) {
Properties prop = MessageResourceUtils.getResourceProperties(locale);
if (prop != null) {
String label = prop.getProperty(code);
if (label != null) {
return createMessageFormat(label, locale);
}
}
return null;
}
}
5. spring-mvc.xml 配置对应的messageSource,以及拦截器
<!-- 拦截器配置,拦截顺序:先执行后定义的,排在第一位的最后执行。-->
<mvc:interceptors>
<!-- 国际化操作拦截器 如果采用基于(请求/Session/Cookie)则必需配置 -->
<bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor" />
</mvc:interceptors>
<!-- 支持Shiro对Controller的方法级AOP安全控制 begin-->
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor">
<property name="proxyTargetClass" value="true" />
</bean>
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<prop key="org.apache.shiro.authz.UnauthorizedException">error/403</prop>
<prop key="java.lang.Throwable">error/500</prop>
</props>
</property>
</bean>
<!-- 支持Shiro对Controller的方法级AOP安全控制 end -->
<!-- 国际化 -->
<bean id="baseMessageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<!-- 国际化信息所在的文件名 -->
<!-- <property name="basename" value="messages" /> -->
</bean>
<bean id="messageSource" class="com.wenbo.common.bundle.ResourceBundleMessageSource">
<!-- 如果在国际化资源文件中找不到对应代码的信息,就用这个代码作为名称 -->
<property name="parentMessageSource" ref="baseMessageSource" />
<property name="useCodeAsDefaultMessage" value="true" />
</bean>
<!-- session 方式 -->
<bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver" >
<property name="defaultLocale" value="en_US" />
</bean>
<!-- cookie 方式
<bean id="localeResolver" class="org.springframework.web.servlet.i18n.CookieLocaleResolver" />
-->
使用方式和使用properties国际化一样,可以参考: