基于 SpringBoot 的@Dict注解

一、代码示例

此示例实现包含四个核心部分:

  1. Dict注解:用于标记需要转换的字段
  2. DictAspect切面:自动拦截 Controller 返回结果并进行字典值转换
  3. User实体类:演示如何使用注解
  4. UserController:提供测试接口

 使用时,只需在需要进行字典转换的字段上添加@Dict注解,并指定字典类型。切面会自动将数值转换为对应的文本描述,并存储在指定的目标字段中。

//Dict.java

import java.lang.annotation.*;

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Dict {
    /**
     * 字典类型
     */
    String type();

    /**
     * 字典项文本存储字段
     */
    String target() default "";
}    
//DictAspect.java

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import java.lang.reflect.Field;
import java.util.*;

@Aspect
@Component
public class DictAspect {

    // 定义切点:拦截所有Controller类中的方法
    @Pointcut("execution(* com.example.demo.controller.*.*(..))")
    public void controllerPointcut() {}

    @Around("controllerPointcut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        // 执行原方法获取返回值
        Object result = point.proceed();
        
        // 处理返回值,进行字典值转换
        processDict(result);
        
        return result;
    }

    // 处理字典转换的核心方法
    private void processDict(Object obj) {
        if (obj == null) {
            return;
        }
        
        // 处理集合类型
        if (obj instanceof Collection) {
            for (Object item : (Collection<?>) obj) {
                if (item != null) {
                    processObjectDict(item);
                }
            }
        } 
        // 处理数组类型
        else if (obj.getClass().isArray()) {
            Object[] array = (Object[]) obj;
            for (Object item : array) {
                if (item != null) {
                    processObjectDict(item);
                }
            }
        } 
        // 处理单个对象
        else {
            processObjectDict(obj);
        }
    }

    // 处理单个对象的字典转换
    private void processObjectDict(Object obj) {
        Class<?> clazz = obj.getClass();
        
        // 遍历对象的所有字段
        for (Field field : clazz.getDeclaredFields()) {
            // 判断字段是否有@Dict注解
            Dict dictAnnotation = field.getAnnotation(Dict.class);
            if (dictAnnotation != null) {
                try {
                    field.setAccessible(true);
                    Object fieldValue = field.get(obj);
                    
                    if (fieldValue == null) {
                        continue;
                    }
                    
                    // 获取字典类型和目标字段
                    String dictType = dictAnnotation.dictType();
                    String targetField = dictAnnotation.targetField();
                    
                    // 根据字典类型获取对应的文本描述
                    String dictText = getDictText(dictType, fieldValue.toString());
                    
                    // 设置转换后的文本到目标字段
                    if (dictText != null && !"".equals(dictText)) {
                        String targetFieldName = targetField;
                        if ("".equals(targetFieldName)) {
                            targetFieldName = field.getName() + "Text";
                        }
                        
                        // 使用反射设置目标字段的值
                        setFieldValue(obj, targetFieldName, dictText);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        
        // 递归处理对象的嵌套属性
        for (Field field : clazz.getDeclaredFields()) {
            try {
                field.setAccessible(true);
                Object fieldValue = field.get(obj);
                if (fieldValue != null) {
                    // 排除基本数据类型和字符串
                    if (!isPrimitiveOrString(field.getType())) {
                        processDict(fieldValue);
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    // 根据字典类型和值获取对应的文本描述
    private String getDictText(String dictType, String dictValue) {
        // 这里应该从数据库或缓存中获取字典数据
        // 为简化示例,我们使用硬编码的字典数据
        
        Map<String, Map<String, String>> dictMap = new HashMap<>();
        
        // 性别字典
        Map<String, String> genderDict = new HashMap<>();
        genderDict.put("1", "男");
        genderDict.put("2", "女");
        genderDict.put("3", "未知");
        
        // 用户状态字典
        Map<String, String> statusDict = new HashMap<>();
        statusDict.put("0", "禁用");
        statusDict.put("1", "启用");
        statusDict.put("2", "锁定");
        
        dictMap.put("gender", genderDict);
        dictMap.put("status", statusDict);
        
        // 根据字典类型获取对应的字典项
        Map<String, String> dictItems = dictMap.get(dictType);
        if (dictItems != null) {
            return dictItems.get(dictValue);
        }
        
        return null;
    }

    // 使用反射设置对象字段的值
    private void setFieldValue(Object obj, String fieldName, Object value) {
        Class<?> clazz = obj.getClass();
        try {
            // 查找目标字段
            Field targetField = null;
            try {
                targetField = clazz.getDeclaredField(fieldName);
            } catch (NoSuchFieldException e) {
                // 如果找不到字段,则尝试查找父类
                Class<?> superClass = clazz.getSuperclass();
                if (superClass != null && !superClass.equals(Object.class)) {
                    targetField = superClass.getDeclaredField(fieldName);
                }
            }
            
            if (targetField != null) {
                targetField.setAccessible(true);
                // 类型转换
                if (value instanceof String) {
                    if (targetField.getType() == Integer.class || targetField.getType() == int.class) {
                        value = Integer.valueOf((String) value);
                    } else if (targetField.getType() == Long.class || targetField.getType() == long.class) {
                        value = Long.valueOf((String) value);
                    }
                }
                targetField.set(obj, value);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // 判断是否为基本数据类型或字符串
    private boolean isPrimitiveOrString(Class<?> clazz) {
        return clazz.isPrimitive() || 
               clazz == String.class || 
               clazz == Integer.class || 
               clazz == Long.class || 
               clazz == Boolean.class || 
               clazz == Double.class || 
               clazz == Float.class || 
               clazz == Short.class || 
               clazz == Byte.class;
    }
}
//User.java

public class User {
    private String id;
    private String name;
    
    @Dict(type = "gender")
    private String gender;
    private String genderText; // 存储性别文本
    
    @Dict(type = "status", target = "statusDesc")
    private String status;
    private String statusDesc; // 存储状态描述

    // getters and setters
    public String getId() { return id; }
    public void setId(String id) { this.id = id; }
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public String getGender() { return gender; }
    public void setGender(String gender) { this.gender = gender; }
    public String getGenderText() { return genderText; }
    public void setGenderText(String genderText) { this.genderText = genderText; }
    public String getStatus() { return status; }
    public void setStatus(String status) { this.status = status; }
    public String getStatusDesc() { return statusDesc; }
    public void setStatusDesc(String statusDesc) { this.statusDesc = statusDesc; }
}    
//UserController.java

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;

@RestController
@RequestMapping("/api")
public class UserController {

    @GetMapping("/users")
    public List<User> getUsers() {
        List<User> userList = new ArrayList<>();
        
        User user1 = new User();
        user1.setId("1");
        user1.setName("张三");
        user1.setGender("1");
        user1.setStatus("1");
        
        User user2 = new User();
        user2.setId("2");
        user2.setName("李四");
        user2.setGender("2");
        user2.setStatus("0");
        
        userList.add(user1);
        userList.add(user2);
        
        return userList;
    }
}    

二、DictAspect.java 核心功能

1. 切面定义与切点

@Pointcut("execution(* com.example.demo.controller.*.*(..))")
public void controllerPointcut() {}
  • 使用@Pointcut定义了一个切点,拦截所有 Controller 包下的方法
  • 这意味着所有 Controller 返回的数据都会被这个切面处理

2. 环绕通知处理

@Around("controllerPointcut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
    Object result = point.proceed();
    processDict(result);
    return result;
}
  • @Around注解定义了环绕通知,在目标方法执行前后进行增强
  • 先执行原方法获取返回结果,然后调用processDict方法处理字典转换

3. 字典处理核心逻辑

processDict方法负责处理不同类型的返回值:

  • 集合类型:遍历集合中的每个元素进行处理
  • 数组类型:遍历数组中的每个元素进行处理
  • 单个对象:直接处理对象

processObjectDict方法处理单个对象的字典转换:

  • 遍历对象的所有字段,查找带有@Dict注解的字段
  • 获取字段的值,并根据字典类型获取对应的文本描述
  • 使用反射将文本描述设置到目标字段中

4.反射工具方法

  • setFieldValue:使用反射设置对象字段的值,支持基本类型的自动转换
  • isPrimitiveOrString:判断是否为基本数据类型或字符串,用于递归处理嵌套对象

 三、硬编码

硬编码是指在程序代码中直接写入固定值或常量,而不是通过变量、配置文件、用户输入或外部数据源动态获取这些值的编程方式。这些值在代码中被直接设置,并且在程序运行期间不会发生变化。如果需要修改这些值,就必须直接修改源代码并重新编译或部署程序。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值