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國際化一樣,可以參考: