1. 程式人生 > >Spring國際化--通過資料庫儲存國際化資料實現動態國際化

Spring國際化--通過資料庫儲存國際化資料實現動態國際化

最近參與了一個境外錢包的專案,要求是實現SpringMVC的動態國際化功能,以應對多國家多語言快速切換配置。

之前用Spring做國際化,都將國際化資訊寫在properties檔案中。這次在專案中遇到一個需求,需要把properties檔案去掉,直接從資料庫讀取國際化資訊。

在網上查了好多資料,和很多朋友一樣一直卡在一個地方一直出不來,就是Spring容器初始化時,要查詢資料庫國際化表裡的國際化資料時,DAO層介面一直注入不進來,導致查庫動作不能正常執行。翻了好多部落格,都是錯誤的,最後終於搞清楚問題出在了哪裡!

一開始我看一些部落格的程式碼,按照他們的方式做時,資料庫查詢的依賴一直注入不進來,他們的寫法是將初始化的查詢方法寫在了MessageSource的無參構造器中,目的是希望伺服器初始化MessageSource類是,順便載入查詢方法,以便把所有的國際化資料載入到快取中,可是實際上容器在初始化,Spring容器還沒有載入完就去呼叫Dao層查詢服務,於是找不到要載入的bean,所以Spring注入依賴失敗,查不到國際化資料。

那麼接下來,我們要解決的問題就是在Spring容器初始化時,在所有bean被注入到容器中之後,我們再去注入Dao層的查詢方法,我用的方法是實現Spring的一個介面InitializingBean,實現方法afterPropertiesSet()方法,可以在Bean被載入完成後,執行你要執行的方法,OK大致解決方案就這樣了,下面來看下程式碼吧:

大部分的配置都是原汁原味的,重點可以到MessageSource類中去尋找

SpringMVC配置檔案

<!-- 定義國際化檔案和編碼 --> 
<bean id="propertiesMessageSource"  class="org.springframework.context.support.ResourceBundleMessageSource">  
        <property name="basenames" value="messages/message"/>  
        <property name="defaultEncoding" value="utf8" />  
</bean>  

<!-- 使用session判斷使用者語言 -->
<bean id="localeResolver"  class="org.springframework.web.servlet.i18n.SessionLocaleResolver">  
       <property name="defaultLocale" value="cn"/>  
</bean>  
<mvc:interceptors>  
       <bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">  
       <property name="paramName" value="locale"/>  
       </bean>  
</mvc:interceptors> 

<!-- 資料庫國際化資源 自定義的MessageResource -->
<bean id="messageSource" class="com.platform.i18n.MessageResource">
        <property name="parentMessageSource" ref="propertiesMessageSource"/>
</bean>

cookie的國際化暫時沒有進行配置,通過重寫MessageResource後,就可以從資料庫讀取資訊了。目前這樣的配置也可以讀取properties檔案中的國際化資訊。    

dto程式碼


/**
* 根據properties定義的物件
* */
package com.platform.i18n.dto;
import java.io.Serializable;
public class Resource implements Serializable
{
    //唯一標識
    private long resourceId;
    //鍵名
    private String name;
    //值
    private String text;
    //對應的語言
    private String language;

    public long getResourceId()
    {
        return resourceId;
    }
    public void setResourceId(long resourceId)
    {
        this.resourceId = resourceId;
    }
    public String getName()
    {
        return name;
    }
    public void setName(String name)
    {
        this.name = name;
    }
    public String getText()
    {
        return text;
    }
    public void setText(String text)
    {
        this.text = text;
    }
    public String getLanguage()
    {
        return language;
    }
    public void setLanguage(String language)
    {
        this.language = language;
    }
}

自定義的MessageSource程式碼

import java.text.MessageFormat;
import java.util.List;
import java.util.Locale;
import java.util.Map;


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.support.AbstractMessageSource;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.ResourceLoader;


import com.google.common.collect.Lists;
import com.google.common.collect.Maps;


import cn.sunline.cap.infrastructure.model.bo.BKwLanguage;
import cn.sunline.cap.surface.QueryI18nDataService;


public class MessageSource extends AbstractMessageSource implements ResourceLoaderAware, InitializingBean {


private Logger logger = LoggerFactory.getLogger(getClass());

//map切分字元
protected final String MAP_SPLIT_CODE = "|";

@SuppressWarnings("unused")
private ResourceLoader resourceLoader;

@Autowired
private QueryI18nDataService queryI18nDataService;
/**
 * 存放所有國際化資料
 */
private final Map<String, String> dataMap = Maps.newHashMap();

public MessageSource() {


}

@Override
public void afterPropertiesSet() throws Exception {
dataMap.clear();
dataMap.putAll(LoadDesc());
}


private Map<String, String> LoadDesc() {
List<BKwLanguage> languages = Lists.newArrayList();
Map<String, String> mapResource = Maps.newHashMap();

logger.info("開始查詢國際化資料");
languages = queryI18nDataService.queryData();
for (BKwLanguage bKwLanguage : languages) {
String code = bKwLanguage.getKeyword() + MAP_SPLIT_CODE + bKwLanguage.getLanguage();
mapResource.put(code, bKwLanguage.getInternationalContent());
}
return mapResource;
}




@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader != null?resourceLoader : new DefaultResourceLoader();
}


@Override
protected MessageFormat resolveCode(String code, Locale locale) {
String msg = getText(code, locale);
MessageFormat result = createMessageFormat(msg, locale);
return result;
}


@Override
protected String resolveCodeWithoutArguments(String code, Locale locale) {
String result = getText(code, locale);
return result;
}


private String getText(String code, Locale locale){

String localeCode = locale.getLanguage();
//本地語言編碼
String key = code + MAP_SPLIT_CODE + localeCode;

String localeText = dataMap.get(key);

String resourceText =code;

if(localeText != null){
resourceText = localeText;
}else{
localeCode = Locale.CHINESE.getLanguage();
key = code + MAP_SPLIT_CODE + localeCode;
localeText = dataMap.get(key);
if(localeText != null){
resourceText = localeText;
}else{
try {
if(getParentMessageSource() != null){
resourceText = getParentMessageSource().getMessage(code, null, locale);
}
} catch (Exception e) {
logger.error("Con not find message with code:", code, e.getMessage());
}
}
}

return resourceText;
}

}

jsp頁面呼叫國際化資訊程式碼塊

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<%@taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
properties國際化資訊顯示:<br/>
   <spring:message code="common.emptyrecords"/><br/>
資料庫讀取的國際化資訊顯示:<br/>
   <spring:message code="common.name"/><br/>

</body>
</html>