字典注解
定义注解
java
package org.elsfs.cloud.core.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 字典注解
*
* @author zeng
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Dict {
/**
* 用于区分不同业务
*
* @return 用于区分不同业务
*/
String dictType();
/**
* 目标显示属性(待绑定属性,注意非数据库字段请排除)
*
* @return 目标显示属性(待绑定属性,注意非数据库字段请排除)
*/
String target();
/**
* 数据字典表
*
* @return 数据字典表
*/
String dictTable() default "";
}
java
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 返回值 字典翻译
*
* @author zeng
*/
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DictionaryTranslation {}
定义拦截器
java
package org.elsfs.cloud.dict.aop;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Objects;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.elsfs.cloud.core.annotation.Dict;
import org.elsfs.cloud.core.aop.AnnotationAdvisor;
import org.elsfs.cloud.core.utils.StringUtil;
import org.elsfs.cloud.core.vo.R;
import org.elsfs.cloud.dict.service.DictService;
/**
* 执行翻译
*
* @see org.springframework.aop.Advisor
* @see AnnotationAdvisor
* @author zeng
*/
@Slf4j
@RequiredArgsConstructor
public class DictMethodInterceptor implements MethodInterceptor {
/** DictService的保护级别的最终成员变量。 这是一个字典服务,用于在类中进行字典相关的操作。 */
protected final DictService dictService;
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
long start = System.currentTimeMillis();
Object returnVar = invocation.proceed();
if (ignoreType(returnVar)) {
return returnVar;
}
LOGGER.debug("运行时间:" + (System.currentTimeMillis() - start) + "毫秒");
parseResult(returnVar);
return returnVar;
}
/**
* 翻译返回结果
*
* @param result result
*/
public void parseResult(Object result) {
// 判断结果类型
if (result instanceof List<?> resultList) {
resultList.forEach(this::parseDict);
}
if (result instanceof R<?> r) {
parseResult(r.getResult());
}
if (result instanceof IPage<?> page) {
parseResult(page.getRecords());
}
parseDict(result);
}
/**
* 解析带有Dict注解的对象字典。 该方法通过反射,获取对象类中所有带有Dict注解的字段,并对这些字段进行处理。
*
* @param obj 需要解析的对象。
*/
private void parseDict(Object obj) {
// 使用反射缓存获取对象类的所有带有Dict注解的字段
var annotatedFields = ReflectionCache.getAnnotatedFields(obj.getClass());
annotatedFields.forEach(field -> handleDict(obj, field));
}
/**
* 处理字典映射,将一个字段的值映射到另一个字段上。
*
* @param obj 要处理的对象实例。
* @param field 需要被映射的字段。
*/
private void handleDict(Object obj, Field field) {
try {
var dict = field.getAnnotation(Dict.class);
var target = dict.target(); // 目标字段名
var targetField = ReflectionCache.getCachedDeclaredField(obj.getClass(), target);
if (targetField == null) {
return;
}
// 允许当前字段和目标字段可访问
field.setAccessible(true);
var key = field.get(obj);
if (ObjectUtil.isEmpty(key)) {
return;
}
if (StringUtil.isBlank(key.toString())) {
return;
}
targetField.setAccessible(true);
// 根据字典类型和当前字段的值,更新目标字段的值
var dictItemEntities = dictService.getListByDictType(dict.dictType());
for (var item : dictItemEntities) {
if (Objects.equals(item.getLabel(), key.toString())) {
targetField.set(obj, item.getValue());
}
}
// 重置字段访问权限
targetField.setAccessible(false);
field.setAccessible(false);
} catch (IllegalAccessException e) {
LOGGER.error("Failed to handle dictionary field: " + field.getName(), e);
}
}
private boolean ignoreType(Object returnVar) {
return returnVar == null || returnVar instanceof CharSequence || returnVar instanceof Number;
}
}
注入 bean
java
/**
* 创建并返回一个注释类型的Advisor Bean,它基于DictionaryTranslation注解提供拦截器功能。
* 这个方法的作用是启用对标注了DictionaryTranslation注解的方法的拦截,具体的拦截逻辑由传入的DictMethodInterceptor实现。
*
* @param dictMethodInterceptor 拦截器实例,负责处理注解方法的拦截逻辑。
* @return 返回一个配置好的Advisor实例,它包含了注解拦截器的配置信息。
*/
@Bean
@Role(BeanDefinition.ROLE_SUPPORT)
@ConditionalOnBean(DictMethodInterceptor.class)
public Advisor dictionaryAnnotationAdvisor(DictMethodInterceptor dictMethodInterceptor) {
// 创建AnnotationAdvisor,将拦截器和注解绑定
return new AnnotationAdvisor(dictMethodInterceptor, DictionaryTranslation.class);
}
使用
java
package org.elsfs.cloud.dict.entity;
import lombok.Data;
import org.elsfs.cloud.core.annotation.Dict;
/**
* 测试翻译实体
*
* @author zeng
*/
@Data
public class DictionaryTranslationEntityTest {
/** 字典翻译原始值 性别 */
@Dict(target = "sexText", dictType = "sexType")
Integer sex;
/** 翻译性别值 */
String sexText;
@Dict(target = "statusText", dictType = "status")
private String status;
private String statusText;
}
package org.elsfs.cloud.dict.service.impl;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import java.util.ArrayList;
import java.util.List;
import org.elsfs.cloud.core.annotation.DictionaryTranslation;
import org.elsfs.cloud.core.vo.R;
import org.elsfs.cloud.dict.entity.DictionaryTranslationEntityTest;
import org.springframework.stereotype.Service;
/**
* 测试的服务
*
* @author zeng
*/
@Service
public class DictionaryTranslationEntityServiceTest {
private static final List<DictionaryTranslationEntityTest> LIST = new ArrayList<>();
static {
var entityText = new DictionaryTranslationEntityTest();
entityText.setSex(1);
entityText.setStatus("ENABLE");
LIST.add(entityText);
var entityText2 = new DictionaryTranslationEntityTest();
entityText2.setSex(2);
entityText2.setStatus("DISABLE");
LIST.add(entityText2);
}
/**
* list
*
* @return list
*/
@DictionaryTranslation
public List<DictionaryTranslationEntityTest> list() {
return LIST;
}
/**
* entity
*
* @return entity
*/
@DictionaryTranslation
public DictionaryTranslationEntityTest entity() {
return LIST.get(0);
}
/**
* returnNull
*
* @return returnNull
*/
@DictionaryTranslation
public DictionaryTranslationEntityTest returnNull() {
return null;
}
/**
* entityNull
*
* @return entityNull
*/
@DictionaryTranslation
public DictionaryTranslationEntityTest entityNull() {
return new DictionaryTranslationEntityTest();
}
/**
* page
*
* @return page
*/
@DictionaryTranslation
public IPage<DictionaryTranslationEntityTest> page() {
Page<DictionaryTranslationEntityTest> page = new Page<>();
page.setRecords(LIST);
return page;
}
/**
* resR
*
* @return resR
*/
@DictionaryTranslation
public R<IPage<DictionaryTranslationEntityTest>> resR() {
Page<DictionaryTranslationEntityTest> page = new Page<>();
page.setRecords(LIST);
return R.success(page);
}
}
package org.elsfs.cloud.dict;
import org.elsfs.cloud.core.annotation.DictionaryTranslation;
import org.elsfs.cloud.core.aop.AnnotationAdvisor;
import org.elsfs.cloud.dict.service.impl.DictionaryTranslationEntityServiceTest;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.ComponentScan;
/**
* 测试{@link DictionaryTranslation} 和{@link AnnotationAdvisor}搭配使用
*
* @author zeng
*/
@SpringBootTest
@ComponentScan("org.elsfs.cloud")
public class DictionaryTranslationTest {
@Autowired DictionaryTranslationEntityServiceTest serviceTest;
@Test
void list() {
var list = serviceTest.list();
Assertions.assertNotNull(list);
Assertions.assertDoesNotThrow(
() ->
list.forEach(
item -> {
Assertions.assertNotNull(item.getSexText());
Assertions.assertNotNull(item.getStatusText());
}));
}
@Test
void entity() {
var entity = serviceTest.entity();
Assertions.assertNotNull(entity);
Assertions.assertNotNull(entity.getSexText());
Assertions.assertNotNull(entity.getStatusText());
}
@Test
void returnNull() {
var entity = serviceTest.returnNull();
Assertions.assertNull(entity);
}
@Test
void entityNull() {
var entity = serviceTest.entityNull();
Assertions.assertNotNull(entity);
Assertions.assertNull(entity.getSexText());
Assertions.assertNull(entity.getStatusText());
}
@Test
void page() {
var page = serviceTest.page();
System.out.println(page.getRecords());
Assertions.assertNotNull(page);
Assertions.assertDoesNotThrow(
() ->
page.getRecords()
.forEach(
item -> {
Assertions.assertNotNull(item.getSexText());
Assertions.assertNotNull(item.getStatusText());
}));
}
@Test
void resR() {
var r = serviceTest.resR();
System.out.println(r.getResult().getRecords());
Assertions.assertNotNull(r.getResult());
Assertions.assertFalse(r.getResult().getRecords().isEmpty());
}
}