国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

基于spring boot的JsonSerializer 業(yè)務(wù)內(nèi)容國(guó)際化

這篇具有很好參考價(jià)值的文章主要介紹了基于spring boot的JsonSerializer 業(yè)務(wù)內(nèi)容國(guó)際化。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問(wèn)。

說(shuō)起國(guó)際化,真的是老生常談了。后端有各種i18n的依賴組件,springboot本身也支持i18n的設(shè)置,前端vue也有i18n的設(shè)置,這些常規(guī)操作就不提了,大家可以去搜索其他博客,寫的都很詳細(xì)。

本篇博客主要寫的是業(yè)務(wù)內(nèi)容國(guó)際化。舉一個(gè)最常用最簡(jiǎn)單的例子,學(xué)生選課,課程有"語(yǔ)文","數(shù)學(xué)","英語(yǔ)"。這個(gè)課程也是一張業(yè)務(wù)表,隨著課程的增多數(shù)據(jù)是逐漸增多的。一個(gè)學(xué)生要查看自己選擇的課程時(shí),如何根據(jù)語(yǔ)言進(jìn)行國(guó)際化的反顯"數(shù)學(xué)"還是"mathematics"。

最開始我拿到這個(gè)需求的時(shí)候,很撓頭,怎么辦,難得不是把這個(gè)需求做出來(lái),這個(gè)需求實(shí)現(xiàn)得方式很多:

  1. 多建冗余字段,把”數(shù)學(xué)“和”mathematics“都存到表里,這樣有明顯得缺點(diǎn),語(yǔ)言增多時(shí)需要一直在表里加字段。
  2. 建一張code、language、value的對(duì)應(yīng)關(guān)系,查詢數(shù)據(jù)的時(shí)候根據(jù)code和language進(jìn)行value的匹配,這種缺陷也很明顯,業(yè)務(wù)侵入性很強(qiáng)。

我要做的事情是讓業(yè)務(wù)開發(fā)人員在無(wú)感知的情況下或侵入性很小的情況下把需求實(shí)現(xiàn)。提到侵入性小,大家很容易聯(lián)想到切面編程AOP。我個(gè)人認(rèn)為AOP最好用的地方就是能拿到自定義注解,通過(guò)在java類或者java方法上增加注解,在切面獲取引入的東西并將我們相要的東西織入。

靈感一來(lái),我們就開干。

一、建表,并存儲(chǔ)基礎(chǔ)數(shù)據(jù)

表的作用是能將各種code對(duì)應(yīng)的各種語(yǔ)言的各種value進(jìn)行匹配,建表比寫在配置文件的好處是顯而易見的,因?yàn)槲覀冏龅氖菢I(yè)務(wù)內(nèi)容的國(guó)際化,而不是定死的幾個(gè)值得國(guó)際化,我們需要根據(jù)業(yè)務(wù)動(dòng)態(tài)得調(diào)整內(nèi)容。這個(gè)表的數(shù)據(jù)可以開一個(gè)接口,業(yè)務(wù)數(shù)據(jù)發(fā)生變化時(shí),可以直接調(diào)用這個(gè)接口,對(duì)表中數(shù)據(jù)進(jìn)行更新。

表結(jié)構(gòu)如下:

基于spring boot的JsonSerializer 業(yè)務(wù)內(nèi)容國(guó)際化

?LANGUAGE_ID 主鍵

LANGUAGE_KEY 存在業(yè)務(wù)表中得業(yè)務(wù)標(biāo)識(shí)

LANGUAGE 語(yǔ)言標(biāo)識(shí)

LANGUAGE_VALUE 國(guó)際化后的值

MODEL 模塊名稱,主要防止KEY重復(fù),同一個(gè)key在不同的業(yè)務(wù)中代表的含義不同。

以上面選課為例,該表存放的值為

1?course math?en?mathematics

2?course math?zh-CN 數(shù)學(xué)

二、獲取表中數(shù)據(jù)放入緩存

數(shù)據(jù)咱們都有了,怎么把數(shù)據(jù)拿出來(lái)用呢,每次查庫(kù)?肯定不現(xiàn)實(shí),我們應(yīng)該提前把準(zhǔn)備好,放在緩存中,誰(shuí)想用直接取。緩存有多種方式。我們做jvm和redis兩種,讓大家做選擇,追求效率就用jvm緩存,不求效率就用redis,對(duì)本身服務(wù)影響小一些。

1、首先定義實(shí)體類

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

/**
 * 系統(tǒng)語(yǔ)言ResultDTO
 */
@Getter
@Setter
@ToString
public class SysLanguageConfig {


    private Long languageId;

    private String model;

    private String languageKey;

    private String language;

    private String languageValue;
    private long currentPage;
    private long pageSize;
}

2、獲取數(shù)據(jù)并緩存的配置類

package com.cnhtc.hdf.wf.common.i18n;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.xxl.job.core.context.XxlJobHelper;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisStringCommands;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.types.Expiration;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StopWatch;

import java.util.*;
import java.util.concurrent.*;

@Configuration  
@EnableFeignClients(clients = {SysLanguageConfigService.class})
@Slf4j
public class LanguageCahceConfigration {
    public static ConcurrentHashMap<String, String> localCacheMap = new ConcurrentHashMap<>(); //本地存儲(chǔ)緩存的map

    /**
     * 存儲(chǔ)redis 熱點(diǎn)數(shù)據(jù)
     */
    public static ConcurrentHashMap<String, String> redisHotspotCacheMap = new ConcurrentHashMap<>();

    public final static String CACHE_KEY_JOIN_SYMBOL = "_";

    private static Boolean i18n;

    @Value("${i18nPageSize: 5000}")
    private Integer i18nPageSize;

    @Value("${i18nEnableInitDataParallel: false}")
    private Boolean i18nEnableInitDataParallel;

    /**
     * 是否開啟redis熱點(diǎn)數(shù)據(jù)緩存,默認(rèn)不開啟
     */
    private static Boolean i18nEnableRedisHotspotCache;

    /**
     * 開啟緩存模式
     * local MAP
     * redis
     */
    private static String i18nEnableCacheMode;

    private final static String CACHE_MODE = "local";

    /**
     * 多少個(gè)元素拆分一個(gè)List
     */
    private final Integer splitListSize = 10;


    /**
     * 批量插入 條數(shù)
     */
    private final Integer REDIS_BATCH_SAVE_SIZE = 5000;

    /**
     * 失效時(shí)間
     */
    private final long EXPIRE_SECONDS = 3600 * 1000;

    @Autowired
    private SysLanguageConfigService sysLanguageConfigService;


    @Bean
    public SysLanguageConfigServiceFallback sysLanguageConfigServiceFallback() {
        return new SysLanguageConfigServiceFallback();
    }

    public LanguageCahceConfigration() {
        System.out.println("------------------- 加載 LanguageCahceConfigration ----------------------------------");
    }

    @Scheduled(initialDelay = 1000, fixedRateString = "${i18nScheduledFixedRate:3600000}")
    public void setLanguageCacheMap() {
        if (i18n) {
            if (!CACHE_MODE.equals(i18nEnableCacheMode)) {
                return;
            }
            CopyOnWriteArrayList<SysLanguageConfig> allList = new CopyOnWriteArrayList<>();
            StopWatch sw = new StopWatch();
            try {
                sw.start("數(shù)據(jù)查詢");
                if (i18nEnableInitDataParallel) {
                    this.selectDataCompletableFuture(allList);
                } else {
                    this.selectData(allList);
                }
                sw.stop();
            } catch (Exception e) {
                e.printStackTrace();
                allList.clear();
            }
            log.debug("allList size = {}", allList.size());
            sw.start("本地緩存");
            localCacheMap.clear();
            localCacheMap.putAll(this.getCacheDataMap(allList));
            sw.stop();
            log.warn("初始化i18n 緩存耗時(shí) , {}", sw.prettyPrint());
            log.warn("初始化i18n 緩存總耗時(shí) , {}", sw.getTotalTimeSeconds());
        }
    }


    /**
     * 循環(huán)查詢數(shù)據(jù)
     *
     * @param allList 數(shù)據(jù)集合
     */
    private void selectData(CopyOnWriteArrayList<SysLanguageConfig> allList) {
        int page = 1;
        boolean isContinue = false;
        do {
            SysLanguageConfig sysLanguageConfig = new SysLanguageConfig();
            sysLanguageConfig.setCurrentPage(page);
            sysLanguageConfig.setPageSize(i18nPageSize);
            Page<SysLanguageConfig> result = sysLanguageConfigService.getAll(sysLanguageConfig);
            if (result != null && !CollectionUtils.isEmpty(result.getRecords())) {
                allList.addAll(result.getRecords());
                if (result.getPages() > page) {
                    isContinue = true;
                    page = page + 1;
                } else {
                    isContinue = false;
                }
            }
        } while (isContinue);
    }


    /**
     * 異步分頁(yè)查詢數(shù)據(jù)
     *
     * @param allList 數(shù)據(jù)集合
     * @throws Exception 異常
     */
    private void selectDataCompletableFuture(CopyOnWriteArrayList<SysLanguageConfig> allList) throws Exception {
        Page<SysLanguageConfig> result = this.getData();
        if (result != null && result.getPages() > 0) {
            allList.addAll(result.getRecords());
            if (result.getPages() > 1) {

                ForkJoinPool pool = new ForkJoinPool();
                List<Integer> pageList = new ArrayList<>();
                for (int i = 2; i <= result.getPages(); i++) {
                    pageList.add(i);
                }
                List<List<Integer>> partition = Lists.partition(pageList, splitListSize);
                for (List<Integer> pages : partition) {
                    List<CompletableFuture<Void>> futureList = new ArrayList<>();
                    for (Integer page : pages) {
                        SysLanguageConfig param = new SysLanguageConfig();
                        param.setCurrentPage(page);
                        param.setPageSize(i18nPageSize);

                        CompletableFuture<Void> future = CompletableFuture.runAsync(() ->
                                allList.addAll(sysLanguageConfigService.getAll(param).getRecords()), pool);
                        futureList.add(future);
                    }
                    CompletableFuture<Void> allSources = CompletableFuture.allOf(futureList.toArray(new CompletableFuture[futureList.size()]));
                    allSources.get();
                }
            }
        }
    }

    /**
     * 獲取數(shù)據(jù)
     * @return Page<SysLanguageConfig>
     */
    private Page<SysLanguageConfig> getData(){
        SysLanguageConfig sysLanguageConfig = new SysLanguageConfig();
        sysLanguageConfig.setCurrentPage(1);
        sysLanguageConfig.setPageSize(i18nPageSize);
        return sysLanguageConfigService.getAll(sysLanguageConfig);
    }

    /**
     * 批量插入并設(shè)置 失效時(shí)間,但是性能慢
     *
     * @param map 數(shù)據(jù)
     */
    private void redisPipelineInsert(ConcurrentHashMap<String, String> map) {
        StringRedisTemplate stringRedisTemplate = ApplicationContextUtil.getBean(StringRedisTemplate.class);
        RedisSerializer<String> serializer = stringRedisTemplate.getStringSerializer();
        stringRedisTemplate.executePipelined(new RedisCallback<String>() {
            @Override
            public String doInRedis(RedisConnection connection) throws DataAccessException {
                map.forEach((key, value) -> {
                    connection.set(serializer.serialize(key), serializer.serialize(value), Expiration.seconds(EXPIRE_SECONDS), RedisStringCommands.SetOption.UPSERT);
                });
                return null;
            }
        }, serializer);
    }

    /**
     * 批量插入后 異步設(shè)置失效時(shí)間
     *
     * @param map 數(shù)據(jù)
     */
    //@Async
    public void setExpire(ConcurrentHashMap<String, String> map) {
        StringRedisTemplate stringRedisTemplate = ApplicationContextUtil.getBean(StringRedisTemplate.class);
        map.forEach((k, v) -> stringRedisTemplate.expire(k, EXPIRE_SECONDS, TimeUnit.SECONDS));
    }

    /**
     * 刷新redis緩存
     */
    @XxlJob("i18nRefreshRedisCache")
    public void refreshRedisCache() {
        XxlJobHelper.log("回調(diào)任務(wù)開始");
        if (i18n) {
            if (CACHE_MODE.equals(i18nEnableCacheMode)) {
                log.error("i18n國(guó)際化配置本地緩存,請(qǐng)勿用redis刷新緩存");
            }

            CopyOnWriteArrayList<SysLanguageConfig> allList = new CopyOnWriteArrayList<>();
            StopWatch sw = new StopWatch();
            try {
                sw.start("數(shù)據(jù)查詢");
                if (i18nEnableInitDataParallel) {
                    this.selectDataCompletableFuture(allList);
                } else {
                    this.selectData(allList);
                }
                sw.stop();
            } catch (Exception e) {
                e.printStackTrace();
                allList.clear();
            }
            sw.start("redis緩存");
            StringRedisTemplate stringRedisTemplate = ApplicationContextUtil.getBean(StringRedisTemplate.class);
            if (ObjectUtils.isEmpty(stringRedisTemplate)) {
                throw new BaseException(ErrorEnum.NOTHROWABLE_ERROR, "StringRedisTemplate is null");
            }
            redisHotspotCacheMap.clear();
            ConcurrentHashMap<String, String> cacheDataMap = this.getCacheDataMap(allList);
            List<Map<String, String>> maps = splitMap(cacheDataMap, REDIS_BATCH_SAVE_SIZE);
            // multiSet 批量插入,key值存在會(huì)覆蓋原值
            maps.forEach(data -> stringRedisTemplate.opsForValue().multiSet(data));
            sw.stop();
            log.warn("初始化i18n redis緩存耗時(shí) , {}", sw.prettyPrint());
            log.warn("初始化i18n redis緩存總耗時(shí) , {}", sw.getTotalTimeSeconds());
        }
        XxlJobHelper.log("回調(diào)任務(wù)結(jié)束");
    }

    /**
     * 刷新local緩存
     */
    //@XxlJob("i18nRefreshLocalCache")
    public ResponseDTO refreshLocalCache() {
        if (CACHE_MODE.equals(i18nEnableCacheMode)) {
            this.setLanguageCacheMap();
            return new ResponseDTO(SysErrEnum.SUCCESS);
        }
        return new ResponseDTO(SysErrEnum.ERROR.code(), "i18n國(guó)際化配置Redis緩存,請(qǐng)勿用本地刷新緩存");
    }

    /**
     * 獲取緩存數(shù)據(jù)
     *
     * @param allList
     * @return
     */
    private ConcurrentHashMap<String, String> getCacheDataMap(List<SysLanguageConfig> allList) {
        ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>();
        allList.parallelStream().forEach(config -> map.put(config.getModel() + CACHE_KEY_JOIN_SYMBOL + config.getLanguageKey() + CACHE_KEY_JOIN_SYMBOL + config.getLanguage(), config.getLanguageValue()));
        log.warn("map size:{}", map.size());
        return map;
    }

    /**
     * 獲取緩存數(shù)據(jù)值
     *
     * @param key key
     * @return value
     */
    public static String getCacheValueByKey(String key) {
        if (i18n) {
            String value;
            if (CACHE_MODE.equals(i18nEnableCacheMode)) {
                value = localCacheMap.get(key);
                log.debug("多語(yǔ)言轉(zhuǎn)換:本地緩存數(shù)量 = {}, key = {}", +localCacheMap.values().size(), key);
            } else {
                if (i18nEnableRedisHotspotCache) {
                    if (redisHotspotCacheMap.containsKey(key)) {
                        value = redisHotspotCacheMap.get(key);
                    } else {
                        StringRedisTemplate stringRedisTemplate = ApplicationContextUtil.getBean(StringRedisTemplate.class);
                        value = stringRedisTemplate.opsForValue().get(key);
                        if (StringUtils.isNotBlank(value)) {
                            // 緩存熱點(diǎn)數(shù)據(jù)
                            redisHotspotCacheMap.put(key, value);
                        }
                    }
                } else {
                    StringRedisTemplate stringRedisTemplate = ApplicationContextUtil.getBean(StringRedisTemplate.class);
                    value = stringRedisTemplate.opsForValue().get(key);
                }
            }
            return value;
        }
        return null;
    }

    @Value("${i18nEnableCacheMode: local}")
    private void setI18nEnableCacheMode(String i18nEnableCacheMode) {
        LanguageCahceConfigration.i18nEnableCacheMode = i18nEnableCacheMode;
    }

    @Value("${i18nEnableRedisHotspotCache: false}")
    private void setI18nEnableRedisHotspotCache(Boolean i18nEnableRedisHotspotCache) {
        LanguageCahceConfigration.i18nEnableRedisHotspotCache = i18nEnableRedisHotspotCache;
    }

    @Value("${i18n: false}")
    private void setI18n(Boolean i18n) {
        LanguageCahceConfigration.i18n = i18n;
    }

    /**
     * Map拆分 (指定分組大小)
     *
     * @param map       Map
     * @param chunkSize 每個(gè)分組的大小 (>=1)
     * @param <K>       Key
     * @param <V>       Value
     * @return 子Map列表
     */
    private <K, V> List<Map<K, V>> splitMap(Map<K, V> map, int chunkSize) {
        if (Objects.isNull(map) || map.isEmpty() || chunkSize < 1) {
            //空map或者分組大小<1,無(wú)法拆分
            return Collections.emptyList();
        }

        int mapSize = map.size(); //鍵值對(duì)總數(shù)
        int groupSize = mapSize / chunkSize + (mapSize % chunkSize == 0 ? 0 : 1); //計(jì)算分組個(gè)數(shù)
        List<Map<K, V>> list = Lists.newArrayListWithCapacity(groupSize); //子Map列表

        if (chunkSize >= mapSize) { //只能分1組的情況
            list.add(map);
            return list;
        }

        int count = 0; //每個(gè)分組的組內(nèi)計(jì)數(shù)
        Map<K, V> subMap = Maps.newHashMapWithExpectedSize(chunkSize); //子Map

        for (Map.Entry<K, V> entry : map.entrySet()) {
            if (count < chunkSize) {
                //給每個(gè)分組放chunkSize個(gè)鍵值對(duì),最后一個(gè)分組可能會(huì)裝不滿
                subMap.put(entry.getKey(), entry.getValue());
                count++; //組內(nèi)計(jì)數(shù)+1
            } else {
                //結(jié)束上一個(gè)分組
                list.add(subMap); //當(dāng)前分組裝滿了->加入列表

                //開始下一個(gè)分組
                subMap = Maps.newHashMapWithExpectedSize(chunkSize); //新的分組
                subMap.put(entry.getKey(), entry.getValue()); //添加當(dāng)前鍵值對(duì)
                count = 1; //組內(nèi)計(jì)數(shù)重置為1
            }
        }

        list.add(subMap);  //添加最后一個(gè)分組
        return list;
    }
}

整段代碼其中區(qū)分了本地緩存、redis緩存等等,還有就是查剛才數(shù)據(jù)庫(kù)表里得數(shù)據(jù),因?yàn)槲覀儾庞昧宋⒎?wù)得架構(gòu),所以獲取數(shù)據(jù)得部分是通過(guò)feign的方式獲取的,大家可以替換成自己的方法。另外,開啟redis緩存的部分可以取舍,沒必要這么完善,保留一種即可。本地緩存的定時(shí)任務(wù)是springboot的,redis的定時(shí)任務(wù)是xxl-job的,這些技術(shù)棧都可以替換。

其中最重要的一點(diǎn),redis比本地緩存慢很多,100條數(shù)據(jù)的國(guó)際化反顯,速度會(huì)差20倍。為什么差怎么多,接下來(lái)就到關(guān)鍵內(nèi)容了。

三、注解定義

注解定義的意義就是在序列化的時(shí)候,能通過(guò)注解拿到切入點(diǎn)并獲取注解的內(nèi)容


import java.lang.annotation.*;
import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;

@Documented
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@JsonSerialize(
        using = I18nSerializer.class
)
public @interface I18n {

    String model() default "common";
    String language() default "";
    String key() default "";
}

四、基于JsonSerializer的序列化處理,進(jìn)行國(guó)際化轉(zhuǎn)換

大家現(xiàn)在都在用springboot的restController,也就是說(shuō),前后端分離之后,前后端的交互就是json,在controller返回的內(nèi)容其實(shí)就是一個(gè)實(shí)體對(duì)象或者集合,那這個(gè)實(shí)體對(duì)象或者集合是怎么轉(zhuǎn)換成json的,就是通過(guò)springboot中引入的jackson來(lái)實(shí)現(xiàn)的,具體實(shí)現(xiàn)原理不多說(shuō)。

我們只需要知道,寫一個(gè)子類,來(lái)繼承JsonSerializer和實(shí)現(xiàn)ContextualSerializer就能實(shí)現(xiàn)序列化的時(shí)候進(jìn)行織入操作。

其中l(wèi)anguage是通過(guò)header從前端傳遞過(guò)來(lái)的。

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.ContextualSerializer;
import com.stdp.hdf.wf.common.core.constants.Constants;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.Map;
import java.util.Objects;

@Slf4j
public class I18nSerializer extends JsonSerializer<String> implements ContextualSerializer {

    private String model;

    private String language;

    private String key;

    public I18nSerializer(String model, String language, String key) {
        this.model = model;
        this.language = language;
        this.key = key;
    }

    public I18nSerializer() {
    }

    @Override
    public void serialize(String s, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
        String requestLanguage = null;
        String mapkey = s;
        if (StringUtils.isBlank(language)) {
            requestLanguage = getLanguage();
        } else {
            requestLanguage = language;
        }
        if (StringUtils.isNotBlank(requestLanguage)) {
            if (StringUtils.isNotBlank(key)) {
                Object o = jsonGenerator.getCurrentValue();
                mapkey = getPropertyValue(o, key).toString();
            }
            String keyString = model + LanguageCahceConfigration.CACHE_KEY_JOIN_SYMBOL + mapkey + LanguageCahceConfigration.CACHE_KEY_JOIN_SYMBOL + requestLanguage;
            String keyName = LanguageCahceConfigration.getCacheValueByKey(keyString);
            if (StringUtils.isBlank(keyName)) {
                keyName = s;
            }
            jsonGenerator.writeString(keyName);
        } else {
            jsonGenerator.writeString(s);
        }

    }


    @Override
    public JsonSerializer<?> createContextual(SerializerProvider serializerProvider, BeanProperty beanProperty) throws JsonMappingException {
        if (beanProperty != null) { // 為空直接跳過(guò)
            if (Objects.equals(beanProperty.getType().getRawClass(), String.class)) { // 非 String 類直接跳過(guò)
                I18n i18n = beanProperty.getAnnotation(I18n.class);
                if (i18n == null) {
                    i18n = beanProperty.getContextAnnotation(I18n.class);
                }
                if (i18n != null) { // 如果能得到注解,就將注解的 value 傳入 I18nSerializer
                    return new I18nSerializer(i18n.model(), i18n.language(), i18n.key());
                }
            }
            return serializerProvider.findValueSerializer(beanProperty.getType(), beanProperty);
        }
        return serializerProvider.findNullValueSerializer(beanProperty);
    }

    public String getLanguage() {
        //直接從request中獲取language信息
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        if (requestAttributes == null) {
            return null;
        }

        HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();

        return request.getHeader(Constants.LANGUAGE);
    }

    public Object getPropertyValue(Object t, String objProperty) {
        Map<String, String> objMap = null;
        try {
            objMap = BeanUtils.describe(t);
            if (objMap.get(objProperty) != null) {
                return objMap.get(objProperty);
            }
            return "";
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "";
    }
}

五、使用

還是以選課為例,返回的json信息,CourseName自動(dòng)就轉(zhuǎn)成了對(duì)應(yīng)的語(yǔ)言。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-492518.html

@Getter
@Setter
@ToString
public Course implements Serializable {
    private String courseCode; //課程編號(hào) 

    @I18n(model = "course",key = "courseCode")
    private String courseName; //課程名稱

}

到了這里,關(guān)于基于spring boot的JsonSerializer 業(yè)務(wù)內(nèi)容國(guó)際化的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來(lái)自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • Spring Boot 項(xiàng)目設(shè)計(jì)業(yè)務(wù)操作日志功能,寫得太好了!

    Spring Boot 項(xiàng)目設(shè)計(jì)業(yè)務(wù)操作日志功能,寫得太好了!

    很久以前都想寫這篇文章,一直沒有空,但直到現(xiàn)在我對(duì)當(dāng)時(shí)的情景還有印象,之所以有印象是因?yàn)樾枨蠛芎?jiǎn)單,業(yè)務(wù)操作日志的記錄與查詢的功能,但是具體實(shí)現(xiàn)真的很爛,具體的爛法會(huì)在反面示例里細(xì)說(shuō),領(lǐng)導(dǎo)以及客戶層面很認(rèn)可,一系列迷之操作,讓我印象深刻。 客戶

    2024年02月11日
    瀏覽(15)
  • 大型集團(tuán)借力泛微搭建語(yǔ)言匯率時(shí)區(qū)統(tǒng)一、業(yè)務(wù)協(xié)同的國(guó)際化OA系統(tǒng)

    大型集團(tuán)借力泛微搭建語(yǔ)言匯率時(shí)區(qū)統(tǒng)一、業(yè)務(wù)協(xié)同的國(guó)際化OA系統(tǒng)

    國(guó)際化、全球化集團(tuán),業(yè)務(wù)遍布全世界,下屬公司眾多,集團(tuán)對(duì)管理方式和企業(yè)文化塑造有著很高的要求。不少大型集團(tuán)以數(shù)字化方式助力全球統(tǒng)一辦公,深化企業(yè)統(tǒng)一管理。 面對(duì)大型集團(tuán)全球化的管理訴求,數(shù)字化辦公系統(tǒng)作為集團(tuán)日常使用的平臺(tái),自然需要適應(yīng)企業(yè)管理

    2024年02月07日
    瀏覽(27)
  • spring boot + Apache tika 實(shí)現(xiàn)文檔內(nèi)容解析

    spring boot + Apache tika 實(shí)現(xiàn)文檔內(nèi)容解析

    Apache tika 是 Apache 開源的一個(gè)文檔解析工具。 Apache Tika 可以解析和提取一千多種不同的文件類型(如PPT、XLS和PDF)的內(nèi)容和格式,并且 Apache Tika 提供了多種使用方式,既可以使用圖形化操作頁(yè)面(tika-app),又可以獨(dú)立部署(tika-server)通過(guò)接口調(diào)用,還可以引入到項(xiàng)目中使用。

    2024年02月14日
    瀏覽(12)
  • RabbitMQ和spring boot整合及其他內(nèi)容

    在現(xiàn)代分布式應(yīng)用程序的設(shè)計(jì)中,消息隊(duì)列系統(tǒng)是不可或缺的一部分,它為我們提供了解耦組件、實(shí)現(xiàn)異步通信和確保高性能的手段。RabbitMQ,作為一款強(qiáng)大的消息代理,能夠協(xié)助我們實(shí)現(xiàn)這些目標(biāo)。在本篇CSDN博客中,我們將探討一些高級(jí)主題,包括RabbitMQ與Spring Boot的整合、

    2024年02月07日
    瀏覽(22)
  • 【SpringBoot應(yīng)用篇】Spring Boot 配置HTTP 響應(yīng)內(nèi)容壓縮

    5、默認(rèn)情況下,要執(zhí)行壓縮,響應(yīng)的長(zhǎng)度至少為 2048 字節(jié),可以通過(guò) server.compression.min-response-size 屬性配置。 6、默認(rèn)情況下,僅當(dāng)響應(yīng)的內(nèi)容類型為以下內(nèi)容之一時(shí),才會(huì)對(duì)其進(jìn)行壓縮,可以通過(guò) mime-types 屬性配置:text/html,text/xml,text/plain,text/css,text/javascript,application/javasc

    2024年02月16日
    瀏覽(21)
  • 【Spring Boot】四種核心類的依賴關(guān)系:實(shí)體類、數(shù)據(jù)處理類、業(yè)務(wù)處理類、控制器類

    //1.配置項(xiàng)目環(huán)境,創(chuàng)建Spring Boot項(xiàng)目。 //2.數(shù)據(jù)庫(kù)設(shè)置,配置數(shù)據(jù)庫(kù)。 //3.創(chuàng)建實(shí)體類,映射到數(shù)據(jù)庫(kù)。 //4.創(chuàng)建數(shù)據(jù)處理層類,Repository //5.創(chuàng)建業(yè)務(wù)處理類,Service類 //6.創(chuàng)建控制器類,Controller類 Article.java java import javax.persistence.Entity; import javax.persistence.GeneratedValue; import java

    2024年02月11日
    瀏覽(18)
  • Spring Boot Maven package時(shí)顯式的跳過(guò)test內(nèi)容

    在pom.xml的編譯插件部分顯式的增加一段內(nèi)容: maven package時(shí)可以跳過(guò)Spring Boot項(xiàng)目中的各種TEST代碼,避免一些打包中的錯(cuò)誤。

    2024年02月13日
    瀏覽(21)
  • 自定義redission裝配和集成分布式開源限流業(yè)務(wù)組件ratelimiter-spring-boot-starter的正確姿勢(shì)

    自定義redission裝配和集成分布式開源限流業(yè)務(wù)組件ratelimiter-spring-boot-starter的正確姿勢(shì) ??由于使用了redisson-spring-boot-starter,在自定義redisson裝配的時(shí)候會(huì)被redisson-spring-boot-starter里面的start默認(rèn)裝配了,同時(shí)在使用開源分布式限流組件ratelimiter-spring-boot-starter的時(shí)候,這個(gè)里面

    2024年02月07日
    瀏覽(27)
  • Spring Boot 集成 WebSocket 實(shí)例 | 前端持續(xù)打印遠(yuǎn)程日志文件更新內(nèi)容(模擬 tail 命令)

    Spring Boot 集成 WebSocket 實(shí)例 | 前端持續(xù)打印遠(yuǎn)程日志文件更新內(nèi)容(模擬 tail 命令)

    這個(gè)是我在 CSDN 的第一百篇原則博文,留念?? 先說(shuō)下項(xiàng)目結(jié)構(gòu),后端基于 Spring Boot 3,前端為 node.js 開發(fā)的控制臺(tái)程序?,F(xiàn)在希望能夠在前端模擬 tail 命令,持續(xù)輸出后端的日志文件。 這個(gè)方案實(shí)施較為簡(jiǎn)單,通過(guò)前端不斷(定時(shí))發(fā)起請(qǐng)求,并攜帶已讀的內(nèi)容坐標(biāo)(posi

    2024年03月18日
    瀏覽(30)
  • Spring boot之WEB 開發(fā)-靜態(tài)資源訪問(wèn)--自定義轉(zhuǎn)換器--處理JSON--內(nèi)容協(xié)商

    Spring boot之WEB 開發(fā)-靜態(tài)資源訪問(wèn)--自定義轉(zhuǎn)換器--處理JSON--內(nèi)容協(xié)商

    在線文檔: https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.developing-web-applications 1. 只要靜態(tài)資源放在類路徑下: /static 、/public 、/resources 、/META-INF/resources可以被直接訪問(wèn)- 對(duì)應(yīng)文件WebProperties.java 2. 常見靜態(tài)資源:JS、CSS 、圖片(.jpg .png .gif .bmp .svg)、字體

    2024年02月09日
    瀏覽(36)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包