springboot項目實現數據庫讀取國際化配置


首先,springboot是支持國際化的,上下文加載的時候會查詢messageSource的bean,如果沒有就會創建一個名為messageSource放在上下文中

所以,我們要創建一個messageSource的bean,代碼如下:

import com.mbuyy.servicemobile.mapper.ConfigI18nMapper;
import com.mbuyy.servicemobile.model.ConfigI18n;
import com.mbuyy.servicemobile.util.ValidateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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 org.springframework.stereotype.Service;
import org.springframework.web.servlet.support.RequestContextUtils;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

// 使用@Compnent("messageSource")注解注入
@Service("messageSource")
public class MyMessageSource extends AbstractMessageSource implements ResourceLoaderAware {
    private final Logger logger = LoggerFactory.getLogger(MyMessageSource.class);

    ResourceLoader resourceLoader;

    // 這個是用來緩存數據庫中獲取到的配置的 數據庫配置更改的時候可以調用reload方法重新加載
    private static final Map<String, Map<String, String>> LOCAL_CACHE = new ConcurrentHashMap<>(256);

    @Resource
    private ConfigI18nMapper configI18nMapper;
    @Autowired
    private HttpServletRequest request;

    /**
     * 初始化
     * Java中該注解的說明:@PostConstruct該注解被用來修飾一個非靜態的void()方法。
     * 被@PostConstruct修飾的方法會在服務器加載Servlet的時候運行,並且只會被服務器執行一次。
     * PostConstruct在構造函數之后執行,init()方法之前執行。
     */
    @PostConstruct
    public void init() {
        this.reload();
    }

    /**
     * 重新將數據庫中的國際化配置加載
     */
    public void reload() {
        LOCAL_CACHE.clear();
        LOCAL_CACHE.putAll(loadAllMessageResourcesFromDB());
    }

    /**
     * 從數據庫中獲取所有國際化配置 這邊可以根據自己數據庫表結構進行相應的業務實現
     * 對應的語言能夠取出來對應的值就行了 無需一定要按照這個方法來
     */
    public Map<String, Map<String, String>> loadAllMessageResourcesFromDB() {
        // 獲取數據庫配置
        List<ConfigI18n> list = configI18nMapper.selectAll();
        if (ValidateUtils.isNotEmpty(list)) {
            final Map<String, String> zhCnMessageResources = new HashMap<>(list.size());
            final Map<String, String> enUsMessageResources = new HashMap<>(list.size());
            final Map<String, String> myMessageResources = new HashMap<>(list.size());
            for (ConfigI18n item : list) {
                // 根據不同語言,分配到對應語言的值中
                if (item.getLanguage().equals("zh")){
                    zhCnMessageResources.put(item.getModel() + "." + item.getModelId(), item.getText());
                }else if (item.getLanguage().equals("en")){
                    enUsMessageResources.put(item.getModel() + "." + item.getModelId(), item.getText());
                }else if (item.getLanguage().equals("my")){
                    myMessageResources.put(item.getModel() + "." + item.getModelId(), item.getText());
                }
            }

            // 加入緩存
            LOCAL_CACHE.put("zh", zhCnMessageResources);
            LOCAL_CACHE.put("en", enUsMessageResources);
            LOCAL_CACHE.put("my", myMessageResources);
        }
        return new HashMap<>();
    }

    /**
     * 從緩存中取出國際化配置對應的數據 或者從父級獲取
     *
     * @param code
     * @param locale 可以為null, 表示從當前HttpServletRequest中獲取語言
     * @return
     */
    public String getSourceFromCache(String code, Locale locale) {
        String language = locale == null ? RequestContextUtils.getLocale(request).getLanguage() : 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);
    }

}

上面聲明了messageResource的Bean,並在服務啟動時,將數據庫文件加入了緩存,下面我們展示如何獲取國際化的對應信息


@Autowired
private MyMessageSource myMessageSource;

myMessageSource.getSourceFromCache("topSerch.content", null)
myMessageSource.getSourceFromCache中我們從緩存中取值,后面如果是null那么我們就從當前緩存中取
接下來我們說一下,接口請求中附帶語言標識的兩種方式
POST請求:Header中附帶Accept-Language,Accept-Language對應的值為數據庫存的國際語言縮寫值,如:"Accept-Language:zh"
GET請求:在url后增加lang參數,lang對應的值為數據庫存的國際語言縮寫值,如:"url?lang=zh"

 

以上就配置好了從數據庫中讀取數據國際化文件,但是我為了方便,需要再后台管理增加國際化文件的相關配置,所以有了下面的做法

先說下想法:我想讓后台管理新增一條數據時,自己去填寫相應的國際化配置,所以我需要讓后台管理做新增操作,但是為了同步新增和代碼中使用的模塊相同,所以我創建了一張config_i18n_model表,用來存儲固定的幾個模板,如果代碼中有變動,就手動修改

drop table if exists config_i18n;

/*==============================================================*/
/* Table: config_i18n                                           */
/*==============================================================*/
create table config_i18n
(
   id                   int not null auto_increment,
   model                varchar(50) comment '模塊,類型',
   model_id             int comment '模塊id',
   name                 varchar(255) comment '鍵名',
   text                 varchar(1024) comment '',
   language             varchar(255) comment '對應的語言',
   primary key (id)
);

alter table config_i18n comment '國際化配置數據庫';

 

並且,為了使每個模塊的配置,對應模塊的多條數據,我在config_i18n表中,增加了model_id字段,表示當前國際化數據對應的是model模塊的model_id這條記錄

drop table if exists config_i18n;

/*==============================================================*/
/* Table: config_i18n                                           */
/*==============================================================*/
create table config_i18n
(
   id                   int not null auto_increment,
   model                varchar(50) comment '模塊,類型',
   model_id             int comment '模塊id',
   name                 varchar(255) comment '鍵名',
   text                 varchar(1024) comment '',
   language             varchar(255) comment '對應的語言',
   primary key (id)
);

alter table config_i18n comment '國際化配置數據庫';

最后,在代碼的對應模塊中,根據數據id取對應模塊的國際化值。

注意:對國際化配置數據庫進行增刪改時,必須調用以下代碼重新加載數據庫配置文件,否則不會生效

myMessageSource.reload()
 
        

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM