Java分析講解序列化與字典功能的序列化
兩種解決方案
- 前端查詢字典數(shù)據(jù)然后前端轉(zhuǎn)碼
- 后端查詢字典值,然后再轉(zhuǎn)碼返回給前段。
本文及時(shí)針對(duì)方案2 進(jìn)行的改進(jìn)
目標(biāo):
在需要返回給前段的字段上添加指定的注解例如:@DictDesc 則根據(jù)該字段定義的值結(jié)合注解配置生成 xxxDesc字段并自動(dòng)賦值為注解屬性值所對(duì)應(yīng)的字典描述;
具體使用的技術(shù)涉及到j(luò)ackson序列化與反序列化,其他JSON工具包也類型的效果;
字典注解定義
/**
* 字典類型字段自動(dòng)生成Desc描述字段
*/
@Inherited
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@JacksonAnnotationsInside
@JsonSerialize(using = DictDescSerializer.class)
@JsonDeserialize(using = DictDescDeserializer.class)
public @interface DictDesc {
/**
* 枚舉類型的class
* 取值:getValue, getCode, getStatus, name
* 描述:getDesc
*
* @return 字典類型
*/
Class<? extends Enum<? extends DictEnum>>[] enumType() default {};
/**
* 字典類型分組
*
* @return 字典類型
*/
String[] dictType() default {};
/**
* 字典轉(zhuǎn)換失敗時(shí)默認(rèn)值
*
* @return String 默認(rèn)值
*/
String defaultValue() default "";
/**
* 是否拋出異常,默認(rèn)不拋出異常,返回默認(rèn)值
*
* @return true 轉(zhuǎn)換失敗則拋出異常,false 異常返回默認(rèn)值
*/
boolean throwException() default false;
}該注解中定義了解析該注解需要序列化器與返序列化器:
@JsonSerialize(using = DictDescSerializer.class)
@JsonDeserialize(using = DictDescDeserializer.class)
字典序列化與返序列化器的實(shí)現(xiàn)
import com.fasterxml.jackson.core.JacksonException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
import com.aimilin.common.dict.annotation.DictDesc;
import com.aimilin.common.dict.service.impl.DictDescSerializerUtils;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
import java.beans.PropertyDescriptor;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Objects;
/**
* 字典類型返序列化器
*
* @author liujunguang1
* @version V1.0
* @date 2022/5/20 21:08
*/
@Slf4j
@NoArgsConstructor
public class DictDescDeserializer extends JsonDeserializer<Object> implements ContextualDeserializer {
/**
* 生成序列化字段后綴
*/
private static final String LABEL_SUFFIX = "Desc";
/**
* 參數(shù)類型
*/
private Class<?> rawClass;
/**
* 默認(rèn)轉(zhuǎn)換器
*/
private ConversionService converter;
/**
* 設(shè)置方法
*/
private Method writeMethod;
/**
* 字典配置信息
*/
private DictDesc dict;
public DictDescDeserializer(DictDesc dict, BeanProperty property) {
this.dict = dict;
this.rawClass = property.getType().getRawClass();
this.converter = new DefaultConversionService();
Class<?> targetClass = property.getMember().getDeclaringClass();
String writeField = property.getName() + LABEL_SUFFIX;
PropertyDescriptor propertyDescriptor = BeanUtils.getPropertyDescriptor(targetClass, writeField);
this.writeMethod = Objects.isNull(propertyDescriptor) ? null : propertyDescriptor.getWriteMethod();
if (Objects.isNull(this.writeMethod)) {
log.info("類:{},字典屬性:{},沒(méi)有寫(xiě)入方法:{},不設(shè)置值!", targetClass.getName(), property.getName(), writeField);
}
}
@Override
public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property) throws JsonMappingException {
DictDesc dict = property.getAnnotation(DictDesc.class);
if (dict != null) {
return new DictDescDeserializer(dict, property);
}
return this;
}
@Override
public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JacksonException {
Object result = this.getValue(p.getText());
this.setDictDesc(result, p.getCurrentName(), p.getCurrentValue());
return result;
}
/**
* 將數(shù)據(jù)類型轉(zhuǎn)換為目標(biāo)類型
*
* @param value 字符串值
* @return 目標(biāo)類型值
* @throws IOException
*/
public Object getValue(String value) throws IOException {
return converter.convert(value, this.rawClass);
}
/**
* 設(shè)置字典會(huì)限制
*
* @param result 字典value
* @param currentName 當(dāng)前屬性名稱
* @param currentValue 當(dāng)前對(duì)象
*/
private void setDictDesc(Object result, String currentName, Object currentValue) {
try {
if (this.writeMethod != null) {
writeMethod.invoke(currentValue, DictDescSerializerUtils.getDesc(this.dict, currentName, result));
}
} catch (Exception e) {
log.error("類:{},字典屬性:{},回顯異常:{}", currentValue.getClass(), currentName, e.getMessage(), e);
}
}
}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.aimilin.common.dict.annotation.DictDesc;
import com.aimilin.common.dict.service.impl.DictDescSerializerUtils;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
/**
* 字典序列化器
*
* @author liujunguang1
* @version V1.0
* @date 2022/5/20 20:48
*/
@Slf4j
@NoArgsConstructor
public class DictDescSerializer extends JsonSerializer<Object> implements ContextualSerializer {
/**
* 生成序列化字段后綴
*/
private static final String LABEL_SUFFIX = "Desc";
/**
* 字典配置信息
*/
private DictDesc dict;
/**
* 構(gòu)造方法
*
* @param dict 字典描述
*/
public DictDescSerializer(DictDesc dict) {
this.dict = dict;
}
@Override
public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException {
DictDesc dict = property.getAnnotation(DictDesc.class);
if (dict != null) {
return new DictDescSerializer(dict);
}
return this;
}
/**
* Method that can be called to ask implementation to serialize
* values of type this serializer handles.
*
* @param value Value to serialize; can <b>not</b> be null.
* @param gen Generator used to output resulting Json content
* @param provider Provider that can be used to get serializers for
* serializing Objects value contains, if any.
*/
@Override
public void serialize(Object value, JsonGenerator gen, SerializerProvider provider) throws IOException {
provider.defaultSerializeValue(value, gen);
if (dict != null) {
String fieldName = gen.getOutputContext().getCurrentName();
// 添加轉(zhuǎn)換之后的字段:xxxDesc
gen.writeStringField(fieldName.concat(LABEL_SUFFIX), DictDescSerializerUtils.getDesc(dict, fieldName, value));
}
}
}字典序列化與反序列工具類
import cn.hutool.extra.spring.SpringUtil;
import com.aimilin.common.core.pojo.system.SysDict;
import com.aimilin.common.dict.annotation.DictDesc;
import com.aimilin.common.dict.annotation.DictEnum;
import com.aimilin.common.dict.exception.DictException;
import com.aimilin.common.dict.exception.enums.DictExceptionEnum;
import com.aimilin.common.dict.service.SysDictService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
/**
* 字典轉(zhuǎn)換工具類
*
* @author liujunguang1
* @version V1.0
* @date 2022/5/20 23:19
*/
@Slf4j
public class DictDescSerializerUtils {
/**
* 獲取字典信息
*
* @param dict 字典對(duì)象
* @param value 字典值
* @return
*/
public static String getDesc(DictDesc dict, String field, Object value) {
if (ArrayUtils.isEmpty(dict.dictType()) && ArrayUtils.isEmpty(dict.enumType())) {
throw new DictException(DictExceptionEnum.REQUEST_DICT_TYPE, field);
}
try {
if (value == null) {
throw new DictException(DictExceptionEnum.REQUEST_NOT_NULL, field);
}
if (ArrayUtils.isNotEmpty(dict.enumType())) {
return getEnumDesc(dict, field, value);
}
return getDictDesc(dict, field, value);
} catch (Exception e) {
log.error("字典轉(zhuǎn)換異常, field:{}, enumType:{}, dictType:{}, 值:{}, 異常:{}",
field, dict.enumType(), dict.dictType(), value, e.getMessage(), e);
if (dict.throwException()) {
throw e instanceof DictException ? (DictException) e : new DictException(DictExceptionEnum.DICT_EXCEPTION, e);
}
return dict.defaultValue();
}
}
/**
* 獲取枚舉類型的描述信息
*
* @param dict 字典
* @param value 值
* @return 枚舉desc字段
*/
public static String getEnumDesc(DictDesc dict, String field, Object value) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
for (Class<? extends Enum<? extends DictEnum>> dictEnum : dict.enumType()) {
Method getCode = dictEnum.getMethod("getCode");
Method getMessage = dictEnum.getMethod("getMessage");
for (Enum<? extends DictEnum> e : dictEnum.getEnumConstants()) {
if (value.equals(getCode.invoke(e))) {
return Objects.toString(getMessage.invoke(e));
}
}
}
throw new DictException(DictExceptionEnum.UNKNOWN_ENUM_DICT_VALUE,
String.format("Field:%s, EnumType: %s, Value: %s", field, Arrays.toString(dict.enumType()), value));
}
/**
* 獲取字典中的值
*
* @param dict 字典注解
* @param value 屬性值
* @return 字典名稱
*/
public static String getDictDesc(DictDesc dict, String field, Object value) {
if (ArrayUtils.isEmpty(dict.dictType())) {
throw new DictException(DictExceptionEnum.REQUEST_DICT_TYPE, field);
}
List<SysDict> sysDictList = SpringUtil.getBean(SysDictService.class).getDictByDictTypeCode(dict.dictType());
if (CollectionUtils.isEmpty(sysDictList)) {
throw new DictException(DictExceptionEnum.NO_DICT_DATA, field, Arrays.toString(dict.dictType()));
}
for (SysDict sysDict : sysDictList) {
if (StringUtils.equals(sysDict.getCode(), Objects.toString(value))) {
return sysDict.getValue();
}
}
throw new DictException(DictExceptionEnum.UNKNOWN_DICT_VALUE, field, Arrays.toString(dict.dictType()));
}
}字典轉(zhuǎn)換服務(wù)類
/**
* 字典服務(wù)類
*
* @author liujunguang1
* @version V1.0
* @date 2022/5/20 16:03
*/
public interface SysDictService {
/**
* 根據(jù)字典類型code獲取字典列表
*
* @param dictTypeCodes 字典類型code
* @return List<SysDict>
*/
public List<SysDict> getDictByDictTypeCode(String... dictTypeCodes);
}服務(wù)實(shí)現(xiàn)類:
/**
* 系統(tǒng)字典服務(wù)實(shí)現(xiàn)類
*
* @author liujunguang1
* @version V1.0
* @date 2022/5/20 16:13
*/
@Service
public class SysDictServiceImpl implements SysDictService {
@Resource
private SystemContextServiceApi systemContextServiceApi;
@Resource
private SysDictCache sysDictCache;
/**
* 根據(jù)字典類型編碼獲取字典數(shù)據(jù)
*
* @param dictTypeCodes 字典類型編碼值
* @return List<SysDict>
*/
@Override
public List<SysDict> getDictByDictTypeCode(String... dictTypeCodes) {
List<SysDict> dictTypeCache = sysDictCache.getDictTypeCache(dictTypeCodes);
if (CollectionUtils.isNotEmpty(dictTypeCache)) {
return dictTypeCache;
}
return systemContextServiceApi.getDictByDictTypeCode(dictTypeCodes).getData();
}
}字典緩存服務(wù)
可以修改為使用本地緩存方式
/**
* 字典緩存服務(wù)
*
* @version V1.0
* @date 2022/5/19 12:13
*/
@Slf4j
@Service
public class SysDictCache {
@Resource
private RedisService redisService;
/**
* 獲取字典類型緩存
*
* @param dictTypes 字典類型
* @return 字典列表
*/
public List<SysDict> getDictTypeCache(String... dictTypes) {
if (Objects.isNull(redisService)) {
log.info("redisService 為空,不使用字典緩存");
return null;
}
List<List<SysDict>> dictValues = redisService.getMultiCacheMapValue(CommonConstant.DICT_CACHE_KEY, Arrays.asList(dictTypes));
if (CollectionUtils.isEmpty(dictValues)) {
return null;
}
List<SysDict> result = new ArrayList<>();
dictValues.stream().filter(Objects::nonNull).forEach(result::addAll);
log.debug("查詢字典緩存,dictTypes:{}, 結(jié)果:{}", dictTypes, result);
return result;
}
/**
* 清空字典類型緩存
*
* @param dictTypes 字典類型
*/
public void cleanDictTypeCache(String... dictTypes) {
if (Objects.isNull(redisService)) {
return;
}
redisService.deleteCacheMapValue(CommonConstant.DICT_CACHE_KEY, dictTypes);
log.info("清除字典緩存,dictTypes:{}", StringUtils.join(dictTypes));
}
/**
* 添加緩存
*
* @param sysDictList 系統(tǒng)字典列表
*/
public void putDictTypeCache(List<SysDict> sysDictList) {
if (Objects.isNull(redisService) || CollectionUtils.isEmpty(sysDictList)) {
return;
}
Map<String, List<SysDict>> collect = sysDictList.stream().collect(Collectors.groupingBy(SysDict::getTypeCode));
for (Map.Entry<String, List<SysDict>> entry : collect.entrySet()) {
redisService.setCacheMapValue(CommonConstant.DICT_CACHE_KEY, entry.getKey(), entry.getValue());
log.info("設(shè)置字典緩存,dictType:{},結(jié)果:{}", entry.getKey(), entry.getValue());
}
}
}到此這篇關(guān)于Java分析講解序列化與字典功能的序列化的文章就介紹到這了,更多相關(guān)Java序列化內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
微信公眾號(hào)測(cè)試賬號(hào)自定義菜單的實(shí)例代碼
這篇文章主要介紹了微信公眾號(hào)測(cè)試賬號(hào)自定義菜單的實(shí)例代碼,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2017-02-02
JavaWeb購(gòu)物車(chē)項(xiàng)目開(kāi)發(fā)實(shí)戰(zhàn)指南
之前沒(méi)有接觸過(guò)購(gòu)物車(chē)的東東,也不知道購(gòu)物車(chē)應(yīng)該怎么做,所以在查詢了很多資料,總結(jié)一下購(gòu)物車(chē)的功能實(shí)現(xiàn),下面這篇文章主要給大家介紹了關(guān)于JavaWeb購(gòu)物車(chē)項(xiàng)目開(kāi)發(fā)的相關(guān)資料,需要的朋友可以參考下2022-06-06
Java通過(guò)百度API實(shí)現(xiàn)圖片車(chē)牌號(hào)識(shí)別
這段時(shí)間做項(xiàng)目需要用java程序進(jìn)行車(chē)牌識(shí)別,因此嘗試做了下這個(gè)程序,本代碼功能是通過(guò)調(diào)用百度API實(shí)現(xiàn)的,感興趣的可以了解一下2021-06-06
JAVA PDF操作之實(shí)現(xiàn)截取N頁(yè)和多個(gè)PDF合并
這篇文章主要為大家詳細(xì)介紹了java關(guān)于PDF的一些操作,例如截取N頁(yè)并生成新文件,轉(zhuǎn)圖片以及多個(gè)PDF合并,文中的示例代碼講解詳細(xì),感興趣的可以了解下2025-01-01
Maven修改運(yùn)行環(huán)境配置代碼實(shí)例
這篇文章主要介紹了Maven修改運(yùn)行環(huán)境配置代碼實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-04-04
SpringbootJPA分頁(yè) PageRequest過(guò)時(shí)的替代方法
這篇文章主要介紹了SpringbootJPA分頁(yè) PageRequest過(guò)時(shí)的替代方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-06-06
Java面試題沖刺第十二天--數(shù)據(jù)庫(kù)(2)
這篇文章主要為大家分享了最有價(jià)值的三道數(shù)據(jù)庫(kù)面試題,涵蓋內(nèi)容全面,包括數(shù)據(jù)結(jié)構(gòu)和算法相關(guān)的題目、經(jīng)典面試編程題等,感興趣的小伙伴們可以參考一下2021-07-07
spring boot 配置動(dòng)態(tài)刷新實(shí)現(xiàn)詳解
這篇文章主要介紹了spring boot 配置動(dòng)態(tài)刷新實(shí)現(xiàn)詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-09-09
SpringBoot Redis實(shí)現(xiàn)接口冪等性校驗(yàn)方法詳細(xì)講解
這篇文章主要介紹了SpringBoot Redis實(shí)現(xiàn)接口冪等性校驗(yàn)方法,近期一個(gè)老項(xiàng)目出現(xiàn)了接口冪等性校驗(yàn)問(wèn)題,前端加了按鈕置灰,依然被人拉著接口參數(shù)一頓輸出,還是重復(fù)調(diào)用了接口,通過(guò)復(fù)制粘貼,完成了后端接口冪等性調(diào)用校驗(yàn)2022-11-11

