Spring Boot项目中常用自定义注解收集(持续更新)

1. 基础注解框架

1.1 元注解理解

java 复制代码
import java.lang.annotation.*;

// 所有元注解的说明
public @interface MetaAnnotations {
    
    // @Target - 定义注解可用的位置
    @Target(ElementType.TYPE)           // 类、接口、枚举
    @Target(ElementType.FIELD)          // 字段
    @Target(ElementType.METHOD)         // 方法
    @Target(ElementType.PARAMETER)      // 参数
    @Target(ElementType.CONSTRUCTOR)    // 构造函数
    @Target(ElementType.LOCAL_VARIABLE) // 局部变量
    @Target(ElementType.ANNOTATION_TYPE)// 注解类型
    @Target(ElementType.PACKAGE)        // 包
    // 可以组合多个
    @Target({ElementType.TYPE, ElementType.METHOD})
    
    // @Retention - 注解保留策略
    @Retention(RetentionPolicy.SOURCE)   // 仅源码
    @Retention(RetentionPolicy.CLASS)    // 字节码
    @Retention(RetentionPolicy.RUNTIME)  // 运行时(可反射获取)
    
    // @Documented - 是否包含在JavaDoc中
    @Documented
    
    // @Inherited - 是否可被继承
    @Inherited
    
    // @Repeatable - 是否可重复注解(Java 8+)
    @Repeatable
}

2. 验证类注解

2.1 参数验证注解

java 复制代码
/**
 * 手机号验证注解
 */
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = PhoneValidator.class)
@Documented
public @interface Phone {
    
    String message() default "手机号格式不正确";
    
    Class<?>[] groups() default {};
    
    Class<? extends Payload>[] payload() default {};
    
    // 自定义属性
    boolean required() default true;
    
    String region() default "CN"; // CN-中国, US-美国等
}

// 验证器实现
@Component
public class PhoneValidator implements ConstraintValidator<Phone, String> {
    
    private boolean required;
    private String region;
    
    @Override
    public void initialize(Phone phoneAnnotation) {
        this.required = phoneAnnotation.required();
        this.region = phoneAnnotation.region();
    }
    
    @Override
    public boolean isValid(String phone, ConstraintValidatorContext context) {
        if (!required && StringUtils.isBlank(phone)) {
            return true;
        }
        
        if (StringUtils.isBlank(phone)) {
            return false;
        }
        
        // 根据地区验证手机号
        switch (region) {
            case "CN":
                return phone.matches("^1[3-9]\\d{9}$");
            case "US":
                return phone.matches("^\\(?([0-9]{3})\\)?[-. ]?([0-9]{3})[-. ]?([0-9]{4})$");
            default:
                return phone.matches("^\\+?[1-9]\\d{1,14}$");
        }
    }
}

2.2 身份证验证注解

java 复制代码
/**
 * 身份证验证注解
 */
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = IdCardValidator.class)
@Documented
public @interface IdCard {
    
    String message() default "身份证号码格式不正确";
    
    Class<?>[] groups() default {};
    
    Class<? extends Payload>[] payload() default {};
    
    // 支持的类型:大陆身份证、香港、澳门、台湾等
    Type type() default Type.MAINLAND;
    
    enum Type {
        MAINLAND,   // 大陆
        HONGKONG,   // 香港
        MACAO,      // 澳门
        TAIWAN      // 台湾
    }
}

// 使用示例
@Data
public class UserDTO {
    
    @IdCard(type = IdCard.Type.MAINLAND)
    private String idCard;
    
    @Phone
    private String phone;
}

2.3 枚举值验证注解

java 复制代码
/**
 * 枚举值验证注解
 */
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = EnumValueValidator.class)
@Documented
public @interface EnumValue {
    
    String message() default "无效的枚举值";
    
    Class<?>[] groups() default {};
    
    Class<? extends Payload>[] payload() default {};
    
    // 枚举类
    Class<? extends Enum<?>> enumClass();
    
    // 是否允许为空
    boolean nullable() default false;
    
    // 指定要验证的方法名(默认toString)
    String method() default "name";
}

// 验证器实现
public class EnumValueValidator implements ConstraintValidator<EnumValue, Object> {
    
    private Class<? extends Enum<?>> enumClass;
    private boolean nullable;
    private String method;
    
    @Override
    public void initialize(EnumValue constraintAnnotation) {
        this.enumClass = constraintAnnotation.enumClass();
        this.nullable = constraintAnnotation.nullable();
        this.method = constraintAnnotation.method();
    }
    
    @Override
    public boolean isValid(Object value, ConstraintValidatorContext context) {
        if (value == null) {
            return nullable;
        }
        
        try {
            Object[] enumValues = enumClass.getEnumConstants();
            if (enumValues == null) {
                return false;
            }
            
            for (Object enumValue : enumValues) {
                Object enumFieldValue;
                if ("name".equals(method)) {
                    enumFieldValue = ((Enum<?>) enumValue).name();
                } else {
                    // 反射调用指定方法
                    Method m = enumClass.getMethod(method);
                    enumFieldValue = m.invoke(enumValue);
                }
                
                if (value.equals(enumFieldValue)) {
                    return true;
                }
            }
        } catch (Exception e) {
            return false;
        }
        
        return false;
    }
}

// 使用示例
public enum Gender {
    MALE("男"),
    FEMALE("女");
    
    private final String description;
    
    Gender(String description) {
        this.description = description;
    }
    
    public String getDescription() {
        return description;
    }
}

@Data
public class UserRequest {
    
    @EnumValue(enumClass = Gender.class, method = "name")
    private String gender;
    
    @EnumValue(enumClass = Gender.class, method = "getDescription")
    private String genderDesc;
}

3. AOP切面注解

3.1 日志记录注解

java 复制代码
/**
 * 操作日志注解
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface OperationLog {
    
    // 模块名称
    String module() default "";
    
    // 操作类型
    OperationType type() default OperationType.OTHER;
    
    // 操作描述
    String description() default "";
    
    // 是否记录参数
    boolean logParams() default true;
    
    // 是否记录结果
    boolean logResult() default false;
    
    // 是否异步记录
    boolean async() default true;
    
    enum OperationType {
        CREATE,     // 创建
        READ,       // 查询
        UPDATE,     // 更新
        DELETE,     // 删除
        IMPORT,     // 导入
        EXPORT,     // 导出
        LOGIN,      // 登录
        LOGOUT,     // 登出
        OTHER       // 其他
    }
}

/**
 * 系统日志切面
 */
@Aspect
@Component
@Slf4j
public class OperationLogAspect {
    
    @Autowired
    private OperationLogService logService;
    
    @Autowired
    private ObjectMapper objectMapper;
    
    @Around("@annotation(operationLog)")
    public Object around(ProceedingJoinPoint joinPoint, OperationLog operationLog) throws Throwable {
        // 记录开始时间
        long startTime = System.currentTimeMillis();
        
        // 构建日志对象
        SysLog sysLog = buildSysLog(joinPoint, operationLog);
        
        Object result;
        try {
            // 执行原方法
            result = joinPoint.proceed();
            
            // 记录执行时间
            sysLog.setExecuteTime(System.currentTimeMillis() - startTime);
            
            // 记录结果
            if (operationLog.logResult()) {
                sysLog.setResult(objectMapper.writeValueAsString(result));
            }
            
            sysLog.setStatus(1); // 成功
            
        } catch (Exception e) {
            sysLog.setExecuteTime(System.currentTimeMillis() - startTime);
            sysLog.setStatus(0); // 失败
            sysLog.setErrorMsg(e.getMessage());
            throw e;
        } finally {
            // 保存日志
            saveLog(sysLog, operationLog.async());
        }
        
        return result;
    }
    
    private SysLog buildSysLog(ProceedingJoinPoint joinPoint, OperationLog operationLog) {
        SysLog sysLog = new SysLog();
        
        // 设置基本信息
        sysLog.setModule(operationLog.module());
        sysLog.setType(operationLog.type().name());
        sysLog.setDescription(operationLog.description());
        
        // 获取方法信息
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        sysLog.setMethod(signature.getDeclaringTypeName() + "." + signature.getName());
        
        // 记录参数
        if (operationLog.logParams()) {
            Object[] args = joinPoint.getArgs();
            try {
                sysLog.setParams(objectMapper.writeValueAsString(args));
            } catch (JsonProcessingException e) {
                sysLog.setParams("参数序列化失败");
            }
        }
        
        // 获取当前用户
        sysLog.setOperator(getCurrentUsername());
        sysLog.setOperatorIp(getIpAddress());
        
        return sysLog;
    }
    
    private void saveLog(SysLog sysLog, boolean async) {
        if (async) {
            // 异步保存
            CompletableFuture.runAsync(() -> logService.save(sysLog));
        } else {
            // 同步保存
            logService.save(sysLog);
        }
    }
    
    private String getCurrentUsername() {
        // 从SecurityContext获取当前用户
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication != null && authentication.isAuthenticated()) {
            return authentication.getName();
        }
        return "system";
    }
    
    private String getIpAddress() {
        // 获取IP地址
        ServletRequestAttributes attributes = (ServletRequestAttributes) 
            RequestContextHolder.getRequestAttributes();
        if (attributes != null) {
            HttpServletRequest request = attributes.getRequest();
            return getClientIp(request);
        }
        return "0.0.0.0";
    }
    
    private String getClientIp(HttpServletRequest request) {
        // 获取客户端真实IP
        String ip = request.getHeader("X-Forwarded-For");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }
}

3.2 性能监控注解

java 复制代码
/**
 * 性能监控注解
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PerformanceMonitor {
    
    // 阈值(单位:毫秒)
    long threshold() default 1000L;
    
    // 是否记录慢查询
    boolean logSlow() default true;
    
    // 监控级别
    Level level() default Level.INFO;
    
    enum Level {
        DEBUG, INFO, WARN, ERROR
    }
}

/**
 * 性能监控切面
 */
@Aspect
@Component
@Slf4j
public class PerformanceMonitorAspect {
    
    @Around("@annotation(performanceMonitor)")
    public Object monitor(ProceedingJoinPoint joinPoint, PerformanceMonitor performanceMonitor) throws Throwable {
        long startTime = System.currentTimeMillis();
        
        Object result;
        try {
            result = joinPoint.proceed();
        } finally {
            long executionTime = System.currentTimeMillis() - startTime;
            
            // 记录执行时间
            String methodName = joinPoint.getSignature().toShortString();
            
            // 根据级别记录日志
            Level level = performanceMonitor.level();
            String message = String.format("方法 [%s] 执行耗时: %d ms", methodName, executionTime);
            
            switch (level) {
                case DEBUG:
                    log.debug(message);
                    break;
                case INFO:
                    log.info(message);
                    break;
                case WARN:
                    log.warn(message);
                    break;
                case ERROR:
                    log.error(message);
                    break;
            }
            
            // 记录慢查询
            if (performanceMonitor.logSlow() && executionTime > performanceMonitor.threshold()) {
                log.warn("慢查询警告: {} 执行耗时 {} ms,超过阈值 {} ms", 
                        methodName, executionTime, performanceMonitor.threshold());
                
                // 可以发送告警通知
                sendAlert(methodName, executionTime);
            }
            
            // 可以推送到监控系统
            pushToMetrics(methodName, executionTime);
        }
        
        return result;
    }
    
    private void sendAlert(String methodName, long executionTime) {
        // 发送告警通知,如邮件、短信、钉钉等
        // 实现略...
    }
    
    private void pushToMetrics(String methodName, long executionTime) {
        // 推送到监控系统,如Prometheus
        // 实现略...
    }
}

3.3 分布式锁注解

java 复制代码
/**
 * 分布式锁注解
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DistributedLock {
    
    // 锁的key,支持SpEL表达式
    String key();
    
    // 锁的前缀
    String prefix() default "lock:";
    
    // 等待时间(秒)
    long waitTime() default 30L;
    
    // 锁持有时间(秒)
    long leaseTime() default 10L;
    
    // 时间单位
    TimeUnit timeUnit() default TimeUnit.SECONDS;
    
    // 锁类型
    LockType type() default LockType.REENTRANT;
    
    // 获取锁失败时的处理策略
    LockFailStrategy failStrategy() default LockFailStrategy.FAIL_FAST;
    
    enum LockType {
        REENTRANT,     // 可重入锁
        FAIR,          // 公平锁
        READ,          // 读锁
        WRITE          // 写锁
    }
    
    enum LockFailStrategy {
        FAIL_FAST,     // 快速失败,抛出异常
        KEEP_RETRY,    // 持续重试
        IGNORE         // 忽略,继续执行
    }
}

/**
 * 分布式锁切面
 */
@Aspect
@Component
@Slf4j
public class DistributedLockAspect {
    
    @Autowired
    private RedissonClient redissonClient;
    
    @Autowired
    private ExpressionParser expressionParser;
    
    @Around("@annotation(distributedLock)")
    public Object around(ProceedingJoinPoint joinPoint, DistributedLock distributedLock) throws Throwable {
        // 解析锁的key
        String lockKey = parseLockKey(joinPoint, distributedLock);
        
        // 获取锁
        RLock lock = getLock(lockKey, distributedLock.type());
        
        boolean locked = false;
        try {
            // 尝试获取锁
            locked = tryLock(lock, distributedLock);
            
            if (!locked) {
                // 获取锁失败的处理
                return handleLockFailure(distributedLock.failStrategy(), joinPoint);
            }
            
            // 执行业务逻辑
            return joinPoint.proceed();
            
        } finally {
            // 释放锁
            if (locked && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }
    
    private String parseLockKey(ProceedingJoinPoint joinPoint, DistributedLock distributedLock) {
        String keyExpression = distributedLock.key();
        String prefix = distributedLock.prefix();
        
        // 支持SpEL表达式
        if (keyExpression.contains("#")) {
            try {
                Expression expression = expressionParser.parseExpression(keyExpression);
                EvaluationContext context = new StandardEvaluationContext();
                
                // 设置参数上下文
                MethodSignature signature = (MethodSignature) joinPoint.getSignature();
                String[] paramNames = signature.getParameterNames();
                Object[] args = joinPoint.getArgs();
                
                for (int i = 0; i < paramNames.length; i++) {
                    context.setVariable(paramNames[i], args[i]);
                }
                
                Object keyValue = expression.getValue(context);
                return prefix + keyValue;
                
            } catch (Exception e) {
                log.error("解析锁key表达式失败: {}", keyExpression, e);
                throw new RuntimeException("解析锁key表达式失败", e);
            }
        }
        
        return prefix + keyExpression;
    }
    
    private RLock getLock(String lockKey, DistributedLock.LockType lockType) {
        switch (lockType) {
            case FAIR:
                return redissonClient.getFairLock(lockKey);
            case READ:
                return redissonClient.getReadWriteLock(lockKey).readLock();
            case WRITE:
                return redissonClient.getReadWriteLock(lockKey).writeLock();
            case REENTRANT:
            default:
                return redissonClient.getLock(lockKey);
        }
    }
    
    private boolean tryLock(RLock lock, DistributedLock distributedLock) throws InterruptedException {
        long waitTime = distributedLock.waitTime();
        long leaseTime = distributedLock.leaseTime();
        TimeUnit timeUnit = distributedLock.timeUnit();
        
        if (waitTime > 0) {
            return lock.tryLock(waitTime, leaseTime, timeUnit);
        } else {
            lock.lock(leaseTime, timeUnit);
            return true;
        }
    }
    
    private Object handleLockFailure(DistributedLock.LockFailStrategy strategy, 
                                    ProceedingJoinPoint joinPoint) throws Throwable {
        switch (strategy) {
            case FAIL_FAST:
                throw new RuntimeException("获取分布式锁失败,请稍后重试");
                
            case KEEP_RETRY:
                // 可以在这里实现重试逻辑
                log.warn("获取锁失败,正在重试...");
                Thread.sleep(100); // 等待一段时间后重试
                // 这里可以递归调用或使用重试框架
                return joinPoint.proceed();
                
            case IGNORE:
                log.warn("获取锁失败,忽略锁继续执行");
                return joinPoint.proceed();
                
            default:
                throw new RuntimeException("未知的锁失败处理策略");
        }
    }
}

4. 缓存注解

4.1 自定义缓存注解

java 复制代码
/**
 * 双写缓存注解(同时写入本地缓存和Redis)
 */
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DoubleWriteCache {
    
    // 缓存名称(支持SpEL)
    String cacheName();
    
    // 缓存key(支持SpEL)
    String key();
    
    // 本地缓存过期时间(秒)
    long localExpire() default 300L;
    
    // Redis缓存过期时间(秒)
    long redisExpire() default 3600L;
    
    // 缓存条件(SpEL)
    String condition() default "";
    
    // 是否异步刷新缓存
    boolean asyncRefresh() default false;
    
    // 缓存类型
    CacheType cacheType() default CacheType.FULL;
    
    enum CacheType {
        LOCAL_ONLY,     // 仅本地缓存
        REDIS_ONLY,     // 仅Redis缓存
        FULL            // 全缓存(本地+Redis)
    }
}

/**
 * 缓存切面
 */
@Aspect
@Component
@Slf4j
public class DoubleWriteCacheAspect {
    
    @Autowired
    private CacheManager cacheManager; // Spring Cache
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    @Autowired
    private CaffeineCacheManager localCacheManager;
    
    @Autowired
    private ExpressionParser expressionParser;
    
    // 缓存查询
    @Around("@annotation(doubleWriteCache)")
    public Object cacheable(ProceedingJoinPoint joinPoint, DoubleWriteCache doubleWriteCache) throws Throwable {
        // 解析缓存名称和key
        String cacheName = parseExpression(doubleWriteCache.cacheName(), joinPoint);
        String key = parseExpression(doubleWriteCache.key(), joinPoint);
        
        // 检查条件
        if (!evaluateCondition(doubleWriteCache.condition(), joinPoint)) {
            return joinPoint.proceed();
        }
        
        // 先从本地缓存获取
        Object result = null;
        CacheType cacheType = doubleWriteCache.cacheType();
        
        if (cacheType == CacheType.LOCAL_ONLY || cacheType == CacheType.FULL) {
            result = getFromLocalCache(cacheName, key);
        }
        
        // 本地缓存未命中,从Redis获取
        if (result == null && (cacheType == CacheType.REDIS_ONLY || cacheType == CacheType.FULL)) {
            result = getFromRedisCache(cacheName, key);
            
            // Redis命中,回写到本地缓存
            if (result != null && cacheType == CacheType.FULL) {
                putToLocalCache(cacheName, key, result, doubleWriteCache.localExpire());
            }
        }
        
        // 缓存命中,直接返回
        if (result != null) {
            return result;
        }
        
        // 缓存未命中,执行业务逻辑
        result = joinPoint.proceed();
        
        // 写入缓存
        if (result != null) {
            if (cacheType == CacheType.REDIS_ONLY || cacheType == CacheType.FULL) {
                putToRedisCache(cacheName, key, result, doubleWriteCache.redisExpire());
            }
            if (cacheType == CacheType.LOCAL_ONLY || cacheType == CacheType.FULL) {
                putToLocalCache(cacheName, key, result, doubleWriteCache.localExpire());
            }
        }
        
        return result;
    }
    
    // 缓存更新
    @After("@annotation(doubleWriteCache)")
    public void cachePut(JoinPoint joinPoint, DoubleWriteCache doubleWriteCache) {
        // 解析缓存名称和key
        String cacheName = parseExpression(doubleWriteCache.cacheName(), joinPoint);
        String key = parseExpression(doubleWriteCache.key(), joinPoint);
        
        // 获取方法返回值
        Object result = ((MethodSignature) joinPoint.getSignature())
                .getReturnType().cast(((ProceedingJoinPoint) joinPoint).getThis());
        
        // 更新缓存
        CacheType cacheType = doubleWriteCache.cacheType();
        if (cacheType == CacheType.REDIS_ONLY || cacheType == CacheType.FULL) {
            putToRedisCache(cacheName, key, result, doubleWriteCache.redisExpire());
        }
        if (cacheType == CacheType.LOCAL_ONLY || cacheType == CacheType.FULL) {
            putToLocalCache(cacheName, key, result, doubleWriteCache.localExpire());
        }
    }
    
    // 缓存删除
    @After("@annotation(doubleWriteCache)")
    public void cacheEvict(JoinPoint joinPoint, DoubleWriteCache doubleWriteCache) {
        String cacheName = parseExpression(doubleWriteCache.cacheName(), joinPoint);
        String key = parseExpression(doubleWriteCache.key(), joinPoint);
        
        CacheType cacheType = doubleWriteCache.cacheType();
        if (cacheType == CacheType.REDIS_ONLY || cacheType == CacheType.FULL) {
            deleteFromRedisCache(cacheName, key);
        }
        if (cacheType == CacheType.LOCAL_ONLY || cacheType == CacheType.FULL) {
            deleteFromLocalCache(cacheName, key);
        }
    }
    
    private Object getFromLocalCache(String cacheName, String key) {
        try {
            com.github.benmanes.caffeine.cache.Cache<Object, Object> cache = 
                    localCacheManager.getCache(cacheName);
            return cache.getIfPresent(key);
        } catch (Exception e) {
            log.error("从本地缓存获取数据失败", e);
            return null;
        }
    }
    
    private Object getFromRedisCache(String cacheName, String key) {
        try {
            String redisKey = buildRedisKey(cacheName, key);
            return redisTemplate.opsForValue().get(redisKey);
        } catch (Exception e) {
            log.error("从Redis获取数据失败", e);
            return null;
        }
    }
    
    private void putToLocalCache(String cacheName, String key, Object value, long expire) {
        try {
            com.github.benmanes.caffeine.cache.Cache<Object, Object> cache = 
                    localCacheManager.getCache(cacheName);
            cache.put(key, value);
        } catch (Exception e) {
            log.error("写入本地缓存失败", e);
        }
    }
    
    private void putToRedisCache(String cacheName, String key, Object value, long expire) {
        try {
            String redisKey = buildRedisKey(cacheName, key);
            redisTemplate.opsForValue().set(redisKey, value, expire, TimeUnit.SECONDS);
        } catch (Exception e) {
            log.error("写入Redis缓存失败", e);
        }
    }
    
    private String buildRedisKey(String cacheName, String key) {
        return String.format("%s:%s", cacheName, key);
    }
    
    private String parseExpression(String expression, ProceedingJoinPoint joinPoint) {
        // SpEL表达式解析
        // 实现略...
        return expression;
    }
    
    private boolean evaluateCondition(String condition, ProceedingJoinPoint joinPoint) {
        if (StringUtils.isBlank(condition)) {
            return true;
        }
        // 解析条件表达式
        // 实现略...
        return true;
    }
}

5. 权限控制注解

5.1 角色权限注解

java 复制代码
/**
 * 角色权限注解
 */
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequiresRole {
    
    // 需要的角色
    String[] value();
    
    // 逻辑关系:AND-需要所有角色,OR-任意一个角色
    Logical logical() default Logical.AND;
    
    // 错误提示信息
    String message() default "没有访问权限";
    
    enum Logical {
        AND, OR
    }
}

/**
 * 权限切面
 */
@Aspect
@Component
@Slf4j
public class AuthorizationAspect {
    
    @Autowired
    private UserService userService;
    
    @Before("@annotation(requiresRole)")
    public void checkRole(JoinPoint joinPoint, RequiresRole requiresRole) {
        // 获取当前用户
        User currentUser = getCurrentUser();
        
        // 获取需要的角色
        String[] requiredRoles = requiresRole.value();
        Logical logical = requiresRole.logical();
        
        boolean hasPermission;
        if (logical == Logical.AND) {
            // 需要所有角色
            hasPermission = Arrays.stream(requiredRoles)
                    .allMatch(role -> hasRole(currentUser, role));
        } else {
            // 任意一个角色
            hasPermission = Arrays.stream(requiredRoles)
                    .anyMatch(role -> hasRole(currentUser, role));
        }
        
        if (!hasPermission) {
            throw new AccessDeniedException(requiresRole.message());
        }
    }
    
    @Before("@annotation(requiresPermission)")
    public void checkPermission(JoinPoint joinPoint, RequiresPermission requiresPermission) {
        User currentUser = getCurrentUser();
        String permission = requiresPermission.value();
        
        if (!hasPermission(currentUser, permission)) {
            throw new AccessDeniedException(requiresPermission.message());
        }
    }
    
    private boolean hasRole(User user, String role) {
        // 检查用户是否拥有指定角色
        return user.getRoles().stream()
                .anyMatch(userRole -> userRole.getCode().equals(role));
    }
    
    private boolean hasPermission(User user, String permission) {
        // 检查用户是否拥有指定权限
        return user.getPermissions().stream()
                .anyMatch(userPermission -> userPermission.getCode().equals(permission));
    }
    
    private User getCurrentUser() {
        // 从SecurityContext或ThreadLocal获取当前用户
        // 实现略...
        return new User();
    }
}

5.2 数据权限注解

java 复制代码
/**
 * 数据权限注解
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataPermission {
    
    // 数据权限类型
    Type type();
    
    // 数据权限字段(用于SQL拼接)
    String field() default "create_by";
    
    // 自定义处理类
    Class<? extends DataPermissionHandler> handler() default DefaultDataPermissionHandler.class;
    
    enum Type {
        SELF,           // 仅自己
        DEPARTMENT,     // 本部门
        DEPARTMENT_AND_CHILDREN, // 本部门及子部门
        ALL             // 所有数据
    }
}

/**
 * 数据权限处理器接口
 */
public interface DataPermissionHandler {
    
    /**
     * 获取数据权限SQL
     */
    String getPermissionSql(DataPermission dataPermission);
    
    /**
     * 获取数据权限参数
     */
    Map<String, Object> getPermissionParams();
}

/**
 * 数据权限切面
 */
@Aspect
@Component
@Slf4j
public class DataPermissionAspect {
    
    @Autowired
    private DataPermissionContext dataPermissionContext;
    
    @Around("@annotation(dataPermission)")
    public Object applyDataPermission(ProceedingJoinPoint joinPoint, 
                                     DataPermission dataPermission) throws Throwable {
        // 获取数据权限处理器
        DataPermissionHandler handler = getHandler(dataPermission);
        
        // 设置数据权限上下文
        dataPermissionContext.setPermissionSql(handler.getPermissionSql(dataPermission));
        dataPermissionContext.setPermissionParams(handler.getPermissionParams());
        
        try {
            // 执行原方法
            return joinPoint.proceed();
        } finally {
            // 清理上下文
            dataPermissionContext.clear();
        }
    }
    
    private DataPermissionHandler getHandler(DataPermission dataPermission) {
        try {
            return dataPermission.handler().newInstance();
        } catch (Exception e) {
            return new DefaultDataPermissionHandler();
        }
    }
}

/**
 * 数据权限上下文(ThreadLocal)
 */
@Component
public class DataPermissionContext {
    
    private static final ThreadLocal<String> PERMISSION_SQL = new ThreadLocal<>();
    private static final ThreadLocal<Map<String, Object>> PERMISSION_PARAMS = new ThreadLocal<>();
    
    public void setPermissionSql(String sql) {
        PERMISSION_SQL.set(sql);
    }
    
    public String getPermissionSql() {
        return PERMISSION_SQL.get();
    }
    
    public void setPermissionParams(Map<String, Object> params) {
        PERMISSION_PARAMS.set(params);
    }
    
    public Map<String, Object> getPermissionParams() {
        return PERMISSION_PARAMS.get();
    }
    
    public void clear() {
        PERMISSION_SQL.remove();
        PERMISSION_PARAMS.remove();
    }
}

/**
 * MyBatis拦截器(实现数据权限过滤)
 */
@Intercepts({
    @Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})
})
@Component
@Slf4j
public class DataPermissionInterceptor implements Interceptor {
    
    @Autowired
    private DataPermissionContext dataPermissionContext;
    
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
        MetaObject metaObject = SystemMetaObject.forObject(statementHandler);
        
        // 获取原始的SQL
        BoundSql boundSql = statementHandler.getBoundSql();
        String originalSql = boundSql.getSql();
        
        // 获取数据权限SQL
        String permissionSql = dataPermissionContext.getPermissionSql();
        Map<String, Object> permissionParams = dataPermissionContext.getPermissionParams();
        
        // 如果有数据权限,修改SQL
        if (StringUtils.isNotBlank(permissionSql)) {
            String newSql = applyDataPermission(originalSql, permissionSql);
            
            // 修改SQL
            metaObject.setValue("delegate.boundSql.sql", newSql);
            
            // 添加参数
            if (permissionParams != null) {
                permissionParams.forEach((key, value) -> {
                    metaObject.setValue("delegate.boundSql.additionalParameters." + key, value);
                });
            }
        }
        
        return invocation.proceed();
    }
    
    private String applyDataPermission(String originalSql, String permissionSql) {
        // 解析SQL,在WHERE条件中添加数据权限
        // 实现略...
        return originalSql + " AND " + permissionSql;
    }
    
    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }
    
    @Override
    public void setProperties(Properties properties) {
        // 设置属性
    }
}

6. 限流注解

6.1 接口限流注解

java 复制代码
/**
 * 限流注解
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RateLimit {
    
    // 限流key(支持SpEL)
    String key() default "";
    
    // 限流类型
    Type type() default Type.DEFAULT;
    
    // 时间窗口(秒)
    int window() default 60;
    
    // 最大请求次数
    int max() default 100;
    
    // 限流算法
    Algorithm algorithm() default Algorithm.TOKEN_BUCKET;
    
    // 限流提示信息
    String message() default "请求过于频繁,请稍后再试";
    
    // 是否启用
    boolean enabled() default true;
    
    // 限流后处理策略
    FallbackStrategy fallbackStrategy() default FallbackStrategy.THROW_EXCEPTION;
    
    // 降级方法名
    String fallbackMethod() default "";
    
    enum Type {
        IP,         // IP限流
        USER,       // 用户限流
        GLOBAL,     // 全局限流
        CUSTOM,     // 自定义限流
        DEFAULT     // 默认(方法级)
    }
    
    enum Algorithm {
        FIXED_WINDOW,   // 固定窗口
        SLIDING_WINDOW, // 滑动窗口
        TOKEN_BUCKET,   // 令牌桶
        LEAKY_BUCKET    // 漏桶
    }
    
    enum FallbackStrategy {
        THROW_EXCEPTION,    // 抛出异常
        RETURN_NULL,        // 返回null
        RETURN_DEFAULT,     // 返回默认值
        FALLBACK_METHOD     // 执行降级方法
    }
}

/**
 * 限流切面
 */
@Aspect
@Component
@Slf4j
public class RateLimitAspect {
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    @Autowired
    private ObjectMapper objectMapper;
    
    @Around("@annotation(rateLimit)")
    public Object rateLimit(ProceedingJoinPoint joinPoint, RateLimit rateLimit) throws Throwable {
        if (!rateLimit.enabled()) {
            return joinPoint.proceed();
        }
        
        // 解析限流key
        String limitKey = buildLimitKey(joinPoint, rateLimit);
        
        // 执行限流检查
        boolean allowed = checkRateLimit(limitKey, rateLimit);
        
        if (!allowed) {
            // 限流后的处理
            return handleRateLimit(joinPoint, rateLimit);
        }
        
        // 执行业务逻辑
        return joinPoint.proceed();
    }
    
    private String buildLimitKey(ProceedingJoinPoint joinPoint, RateLimit rateLimit) {
        StringBuilder keyBuilder = new StringBuilder("rate_limit:");
        
        // 添加限流类型
        keyBuilder.append(rateLimit.type().name().toLowerCase()).append(":");
        
        // 根据类型构建key
        switch (rateLimit.type()) {
            case IP:
                keyBuilder.append(getClientIp());
                break;
            case USER:
                keyBuilder.append(getCurrentUserId());
                break;
            case GLOBAL:
                keyBuilder.append("global");
                break;
            case CUSTOM:
                if (StringUtils.isNotBlank(rateLimit.key())) {
                    keyBuilder.append(parseExpression(rateLimit.key(), joinPoint));
                } else {
                    keyBuilder.append("custom");
                }
                break;
            case DEFAULT:
            default:
                MethodSignature signature = (MethodSignature) joinPoint.getSignature();
                String className = signature.getDeclaringTypeName();
                String methodName = signature.getMethod().getName();
                keyBuilder.append(className).append(".").append(methodName);
                break;
        }
        
        return keyBuilder.toString();
    }
    
    private boolean checkRateLimit(String key, RateLimit rateLimit) {
        switch (rateLimit.algorithm()) {
            case FIXED_WINDOW:
                return checkFixedWindow(key, rateLimit);
            case SLIDING_WINDOW:
                return checkSlidingWindow(key, rateLimit);
            case TOKEN_BUCKET:
                return checkTokenBucket(key, rateLimit);
            case LEAKY_BUCKET:
                return checkLeakyBucket(key, rateLimit);
            default:
                return checkFixedWindow(key, rateLimit);
        }
    }
    
    private boolean checkFixedWindow(String key, RateLimit rateLimit) {
        long currentTime = System.currentTimeMillis() / 1000;
        long window = rateLimit.window();
        long max = rateLimit.max();
        
        // 使用Redis实现固定窗口限流
        Long count = redisTemplate.opsForValue().increment(key, 1);
        
        if (count != null && count == 1) {
            // 第一次设置过期时间
            redisTemplate.expire(key, window, TimeUnit.SECONDS);
        }
        
        return count != null && count <= max;
    }
    
    private boolean checkSlidingWindow(String key, RateLimit rateLimit) {
        // 滑动窗口限流实现
        // 使用Redis ZSet实现
        long currentTime = System.currentTimeMillis();
        long window = rateLimit.window() * 1000;
        long max = rateLimit.max();
        
        String zsetKey = key + ":zset";
        String countKey = key + ":count";
        
        // 移除窗口外的数据
        redisTemplate.opsForZSet().removeRangeByScore(zsetKey, 0, currentTime - window);
        
        // 获取当前窗口内的请求数
        Long count = redisTemplate.opsForZSet().zCard(zsetKey);
        
        if (count != null && count < max) {
            // 添加当前请求
            redisTemplate.opsForZSet().add(zsetKey, UUID.randomUUID().toString(), currentTime);
            redisTemplate.expire(zsetKey, window * 2, TimeUnit.MILLISECONDS);
            return true;
        }
        
        return false;
    }
    
    private boolean checkTokenBucket(String key, RateLimit rateLimit) {
        // 令牌桶算法实现
        String tokenKey = key + ":tokens";
        String timestampKey = key + ":timestamp";
        
        long capacity = rateLimit.max();
        long rate = capacity / rateLimit.window(); // 每秒生成令牌数
        
        long now = System.currentTimeMillis();
        
        // 获取当前令牌数和上次更新时间
        Long lastTime = (Long) redisTemplate.opsForValue().get(timestampKey);
        Double currentTokens = (Double) redisTemplate.opsForValue().get(tokenKey);
        
        if (lastTime == null) {
            lastTime = now;
        }
        if (currentTokens == null) {
            currentTokens = (double) capacity;
        }
        
        // 计算新增的令牌
        long deltaTime = now - lastTime;
        double newTokens = deltaTime * rate / 1000.0;
        currentTokens = Math.min(capacity, currentTokens + newTokens);
        
        // 检查是否有足够的令牌
        if (currentTokens >= 1) {
            // 消耗一个令牌
            currentTokens -= 1;
            redisTemplate.opsForValue().set(tokenKey, currentTokens);
            redisTemplate.opsForValue().set(timestampKey, now);
            redisTemplate.expire(tokenKey, rateLimit.window() * 2, TimeUnit.SECONDS);
            redisTemplate.expire(timestampKey, rateLimit.window() * 2, TimeUnit.SECONDS);
            return true;
        }
        
        return false;
    }
    
    private Object handleRateLimit(ProceedingJoinPoint joinPoint, RateLimit rateLimit) throws Throwable {
        switch (rateLimit.fallbackStrategy()) {
            case THROW_EXCEPTION:
                throw new RateLimitException(rateLimit.message());
                
            case RETURN_NULL:
                return null;
                
            case RETURN_DEFAULT:
                return getDefaultValue(joinPoint);
                
            case FALLBACK_METHOD:
                return executeFallbackMethod(joinPoint, rateLimit.fallbackMethod());
                
            default:
                throw new RateLimitException(rateLimit.message());
        }
    }
    
    private Object getDefaultValue(ProceedingJoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Class<?> returnType = signature.getReturnType();
        
        // 根据返回类型返回默认值
        if (returnType.isPrimitive()) {
            if (returnType == boolean.class) return false;
            if (returnType == int.class) return 0;
            if (returnType == long.class) return 0L;
            if (returnType == double.class) return 0.0;
            if (returnType == float.class) return 0.0f;
            if (returnType == char.class) return '\0';
            if (returnType == byte.class) return (byte) 0;
            if (returnType == short.class) return (short) 0;
        }
        
        return null;
    }
    
    private Object executeFallbackMethod(ProceedingJoinPoint joinPoint, String fallbackMethod) throws Throwable {
        if (StringUtils.isBlank(fallbackMethod)) {
            throw new RateLimitException("限流降级方法未配置");
        }
        
        Object target = joinPoint.getTarget();
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        
        // 查找降级方法
        Method fallback = findFallbackMethod(target, method, fallbackMethod);
        
        if (fallback == null) {
            throw new NoSuchMethodException("降级方法未找到: " + fallbackMethod);
        }
        
        // 执行降级方法
        return fallback.invoke(target, joinPoint.getArgs());
    }
    
    private Method findFallbackMethod(Object target, Method originalMethod, String fallbackMethodName) {
        Class<?> targetClass = target.getClass();
        
        try {
            // 查找同名同参数的方法
            return targetClass.getMethod(fallbackMethodName, originalMethod.getParameterTypes());
        } catch (NoSuchMethodException e) {
            // 查找同名的方法(参数可能不同)
            return Arrays.stream(targetClass.getMethods())
                    .filter(m -> m.getName().equals(fallbackMethodName))
                    .findFirst()
                    .orElse(null);
        }
    }
    
    private String getClientIp() {
        // 获取客户端IP
        // 实现略...
        return "127.0.0.1";
    }
    
    private String getCurrentUserId() {
        // 获取当前用户ID
        // 实现略...
        return "anonymous";
    }
    
    private String parseExpression(String expression, ProceedingJoinPoint joinPoint) {
        // 解析SpEL表达式
        // 实现略...
        return expression;
    }
}

7. 事务注解增强

7.1 异步事务注解

java 复制代码
/**
 * 异步事务注解
 * 用于需要异步执行但又要保证事务的方法
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface AsyncTransactional {
    
    // 事务传播行为
    Propagation propagation() default Propagation.REQUIRED;
    
    // 事务隔离级别
    Isolation isolation() default Isolation.DEFAULT;
    
    // 超时时间(秒)
    int timeout() default -1;
    
    // 是否只读
    boolean readOnly() default false;
    
    // 需要回滚的异常类型
    Class<? extends Throwable>[] rollbackFor() default {Exception.class};
    
    // 不需要回滚的异常类型
    Class<? extends Throwable>[] noRollbackFor() default {};
    
    // 执行器名称(Spring Bean名称)
    String executor() default "asyncTransactionalExecutor";
}

/**
 * 异步事务执行器配置
 */
@Configuration
@EnableAsync
public class AsyncTransactionalConfig {
    
    @Bean("asyncTransactionalExecutor")
    public Executor asyncTransactionalExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(10);
        executor.setMaxPoolSize(50);
        executor.setQueueCapacity(100);
        executor.setThreadNamePrefix("async-transactional-");
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.setWaitForTasksToCompleteOnShutdown(true);
        executor.setAwaitTerminationSeconds(60);
        executor.initialize();
        return executor;
    }
    
    @Bean
    public AsyncTransactionalAspect asyncTransactionalAspect() {
        return new AsyncTransactionalAspect();
    }
}

/**
 * 异步事务切面
 */
@Aspect
@Component
@Slf4j
public class AsyncTransactionalAspect {
    
    @Autowired
    private PlatformTransactionManager transactionManager;
    
    @Autowired
    @Qualifier("asyncTransactionalExecutor")
    private Executor asyncExecutor;
    
    @Around("@annotation(asyncTransactional)")
    public Object asyncTransactional(ProceedingJoinPoint joinPoint, 
                                   AsyncTransactional asyncTransactional) throws Throwable {
        
        // 创建事务定义
        DefaultTransactionDefinition definition = new DefaultTransactionDefinition();
        definition.setPropagationBehavior(asyncTransactional.propagation().value());
        definition.setIsolationLevel(asyncTransactional.isolation().value());
        definition.setTimeout(asyncTransactional.timeout());
        definition.setReadOnly(asyncTransactional.readOnly());
        
        // 开启事务
        TransactionStatus status = transactionManager.getTransaction(definition);
        
        try {
            // 异步执行
            CompletableFuture<Object> future = CompletableFuture.supplyAsync(() -> {
                try {
                    // 在新线程中绑定事务
                    TransactionSynchronizationManager.bindResource(
                        transactionManager, status);
                    
                    return joinPoint.proceed();
                } catch (Throwable e) {
                    throw new CompletionException(e);
                } finally {
                    TransactionSynchronizationManager.unbindResource(transactionManager);
                }
            }, asyncExecutor);
            
            // 等待异步执行完成
            Object result = future.get();
            
            // 提交事务
            transactionManager.commit(status);
            
            return result;
            
        } catch (Exception e) {
            // 处理异常
            Throwable cause = e instanceof CompletionException ? e.getCause() : e;
            
            // 检查是否需要回滚
            if (shouldRollback(cause, asyncTransactional)) {
                transactionManager.rollback(status);
                log.error("异步事务执行失败,已回滚", cause);
            } else {
                transactionManager.commit(status);
                log.warn("异步事务执行异常,但未回滚", cause);
            }
            
            throw cause;
        }
    }
    
    private boolean shouldRollback(Throwable ex, AsyncTransactional asyncTransactional) {
        // 检查是否需要回滚
        if (ex == null) {
            return false;
        }
        
        // 检查noRollbackFor
        for (Class<? extends Throwable> noRollbackClass : asyncTransactional.noRollbackFor()) {
            if (noRollbackClass.isInstance(ex)) {
                return false;
            }
        }
        
        // 检查rollbackFor
        for (Class<? extends Throwable> rollbackClass : asyncTransactional.rollbackFor()) {
            if (rollbackClass.isInstance(ex)) {
                return true;
            }
        }
        
        // 默认回滚Exception
        return Exception.class.isInstance(ex);
    }
}

8. 其他实用注解

8.1 脱敏注解

java 复制代码
/**
 * 数据脱敏注解
 */
@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataMasking {
    
    // 脱敏类型
    MaskType type() default MaskType.DEFAULT;
    
    // 自定义正则表达式
    String pattern() default "";
    
    // 替换字符
    char maskChar() default '*';
    
    // 保留前几位
    int prefix() default 3;
    
    // 保留后几位
    int suffix() default 4;
    
    enum MaskType {
        NONE,           // 不脱敏
        NAME,           // 姓名
        PHONE,          // 手机号
        ID_CARD,        // 身份证
        EMAIL,          // 邮箱
        BANK_CARD,      // 银行卡
        ADDRESS,        // 地址
        PASSWORD,       // 密码
        CUSTOM,         // 自定义
        DEFAULT         // 默认(全部替换)
    }
}

/**
 * 脱敏序列化器
 */
@Component
public class DataMaskingSerializer extends JsonSerializer<String> {
    
    @Override
    public void serialize(String value, JsonGenerator gen, SerializerProvider provider) 
            throws IOException {
        
        if (value == null) {
            gen.writeNull();
            return;
        }
        
        // 获取脱敏注解
        DataMasking annotation = getDataMaskingAnnotation(gen);
        
        if (annotation == null || annotation.type() == MaskType.NONE) {
            gen.writeString(value);
            return;
        }
        
        // 执行脱敏
        String maskedValue = mask(value, annotation);
        gen.writeString(maskedValue);
    }
    
    private DataMasking getDataMaskingAnnotation(JsonGenerator gen) {
        // 从当前序列化上下文中获取注解
        // 实现略...
        return null;
    }
    
    private String mask(String value, DataMasking annotation) {
        if (StringUtils.isBlank(value)) {
            return value;
        }
        
        MaskType type = annotation.type();
        char maskChar = annotation.maskChar();
        
        switch (type) {
            case NAME:
                return maskName(value, maskChar);
                
            case PHONE:
                return maskPhone(value, maskChar);
                
            case ID_CARD:
                return maskIdCard(value, maskChar);
                
            case EMAIL:
                return maskEmail(value, maskChar);
                
            case BANK_CARD:
                return maskBankCard(value, maskChar);
                
            case ADDRESS:
                return maskAddress(value, maskChar);
                
            case PASSWORD:
                return "******";
                
            case CUSTOM:
                return maskWithPattern(value, annotation.pattern(), maskChar);
                
            case DEFAULT:
                return StringUtils.repeat(maskChar, value.length());
                
            default:
                return value;
        }
    }
    
    private String maskName(String name, char maskChar) {
        if (name.length() <= 1) {
            return name;
        }
        if (name.length() == 2) {
            return name.charAt(0) + String.valueOf(maskChar);
        }
        return name.charAt(0) + StringUtils.repeat(maskChar, name.length() - 2) + 
               name.charAt(name.length() - 1);
    }
    
    private String maskPhone(String phone, char maskChar) {
        if (phone.length() != 11) {
            return phone;
        }
        return phone.substring(0, 3) + StringUtils.repeat(maskChar, 4) + 
               phone.substring(7);
    }
    
    private String maskIdCard(String idCard, char maskChar) {
        if (idCard.length() != 18) {
            return idCard;
        }
        return idCard.substring(0, 6) + StringUtils.repeat(maskChar, 8) + 
               idCard.substring(14);
    }
    
    private String maskEmail(String email, char maskChar) {
        int atIndex = email.indexOf('@');
        if (atIndex <= 1) {
            return email;
        }
        String prefix = email.substring(0, atIndex);
        String suffix = email.substring(atIndex);
        
        if (prefix.length() <= 3) {
            return prefix.charAt(0) + "***" + suffix;
        }
        return prefix.substring(0, 3) + "***" + suffix;
    }
    
    private String maskBankCard(String cardNo, char maskChar) {
        if (cardNo.length() < 8) {
            return cardNo;
        }
        return cardNo.substring(0, 4) + StringUtils.repeat(maskChar, cardNo.length() - 8) + 
               cardNo.substring(cardNo.length() - 4);
    }
    
    private String maskAddress(String address, char maskChar) {
        if (address.length() <= 10) {
            return address;
        }
        int keepLength = address.length() / 3;
        return address.substring(0, keepLength) + 
               StringUtils.repeat(maskChar, address.length() - keepLength * 2) + 
               address.substring(address.length() - keepLength);
    }
    
    private String maskWithPattern(String value, String pattern, char maskChar) {
        if (StringUtils.isBlank(pattern)) {
            return value;
        }
        // 使用正则表达式进行脱敏
        return value.replaceAll(pattern, String.valueOf(maskChar));
    }
}

/**
 * 脱敏配置
 */
@Configuration
public class DataMaskingConfig {
    
    @Bean
    public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
        return builder -> {
            // 注册脱敏序列化器
            SimpleModule module = new SimpleModule();
            module.addSerializer(String.class, new DataMaskingSerializer());
            builder.modules(module);
        };
    }
}

// 使用示例
@Data
public class UserDTO {
    
    @DataMasking(type = DataMasking.MaskType.NAME)
    private String name;
    
    @DataMasking(type = DataMasking.MaskType.PHONE)
    private String phone;
    
    @DataMasking(type = DataMasking.MaskType.ID_CARD)
    private String idCard;
    
    @DataMasking(type = DataMasking.MaskType.EMAIL)
    private String email;
    
    @DataMasking(type = DataMasking.MaskType.BANK_CARD)
    private String bankCard;
    
    @DataMasking(type = DataMasking.MaskType.PASSWORD)
    private String password;
    
    @DataMasking(type = DataMasking.MaskType.CUSTOM, pattern = ".{4,8}", maskChar = '#')
    private String customField;
}

8.2 接口版本注解

java 复制代码
/**
 * API版本注解
 */
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ApiVersion {
    
    // 版本号,如:v1, v2
    String value() default "v1";
    
    // 是否弃用
    boolean deprecated() default false;
    
    // 弃用提示信息
    String deprecatedMessage() default "该版本已弃用,请使用最新版本";
    
    // 最低支持版本
    String minVersion() default "";
    
    // 最高支持版本
    String maxVersion() default "";
}

/**
 * API版本处理器
 */
@Component
public class ApiVersionHandlerMapping extends RequestMappingHandlerMapping {
    
    @Override
    protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
        RequestMappingInfo info = super.getMappingForMethod(method, handlerType);
        
        if (info == null) {
            return null;
        }
        
        // 获取方法上的版本注解
        ApiVersion methodAnnotation = AnnotationUtils.findAnnotation(method, ApiVersion.class);
        
        // 获取类上的版本注解
        ApiVersion classAnnotation = AnnotationUtils.findAnnotation(handlerType, ApiVersion.class);
        
        // 优先使用方法上的注解,如果没有则使用类上的注解
        String version = null;
        if (methodAnnotation != null) {
            version = methodAnnotation.value();
        } else if (classAnnotation != null) {
            version = classAnnotation.value();
        }
        
        // 如果有版本信息,添加到路径中
        if (version != null && !version.isEmpty()) {
            RequestMappingInfo versionInfo = RequestMappingInfo
                    .paths("/" + version)
                    .build();
            info = versionInfo.combine(info);
        }
        
        return info;
    }
}

/**
 * API版本配置
 */
@Configuration
public class ApiVersionConfig implements WebMvcRegistrations {
    
    @Override
    public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
        return new ApiVersionHandlerMapping();
    }
}

// 使用示例
@RestController
@RequestMapping("/api/user")
@ApiVersion("v1")
public class UserControllerV1 {
    
    @GetMapping("/{id}")
    @ApiVersion("v1")
    public ResponseEntity<User> getUserV1(@PathVariable Long id) {
        // V1版本实现
        return ResponseEntity.ok(new User());
    }
    
    @PostMapping
    @ApiVersion(value = "v1", deprecated = true, 
                deprecatedMessage = "请使用v2版本的创建接口")
    public ResponseEntity<User> createUserV1(@RequestBody User user) {
        // V1版本实现(已弃用)
        return ResponseEntity.ok(user);
    }
}

@RestController
@RequestMapping("/api/user")
@ApiVersion("v2")
public class UserControllerV2 {
    
    @GetMapping("/{id}")
    @ApiVersion("v2")
    public ResponseEntity<User> getUserV2(@PathVariable Long id) {
        // V2版本实现
        return ResponseEntity.ok(new User());
    }
    
    @PostMapping
    @ApiVersion(value = "v2", minVersion = "v2", maxVersion = "v3")
    public ResponseEntity<User> createUserV2(@RequestBody User user) {
        // V2版本实现
        return ResponseEntity.ok(user);
    }
}

9.使用规范

1.命名规范:

  • 注解名称使用名词或形容词+名词
  • 遵循驼峰命名法
  • 以功能命名,如@OperationLog、@RateLimit

2.属性设计:

  • 设置合理的默认值
  • 提供必要的配置项
  • 支持SpEL表达式增加灵活性

3.文档注释:

  • 每个注解都要有详细的JavaDoc
  • 说明使用场景和示例
  • 标注注意事项

4.性能考虑

  • 运行时注解(RetentionPolicy.RUNTIME)会影响性能,谨慎使用
  • 避免在频繁调用的方法上使用复杂的注解
  • 考虑异步处理耗时的切面逻辑
相关推荐
为所欲为、Lynn3 小时前
用FastJson的Filter自动映射枚举
java·spring boot
JIngJaneIL3 小时前
基于java+ vue学生成绩管理系统(源码+数据库+文档)
java·前端·数据库·vue.js·spring boot·后端
老华带你飞4 小时前
智能菜谱推荐|基于java + vue智能菜谱推荐系统(源码+数据库+文档)
java·开发语言·前端·数据库·vue.js·spring boot
h7ml5 小时前
在Spring Boot中集成企业微信API的统一异常处理与日志追踪方案
spring boot·企业微信
程序帝国5 小时前
SpringBoot整合RediSearch(完整,详细,连接池版本)
java·spring boot·redis·后端·redisearch
源码获取_wx:Fegn08955 小时前
基于springboot + vueOA工程项目管理系统
java·vue.js·spring boot·后端·spring
一 乐5 小时前
健康管理|基于springboot + vue健康管理系统(源码+数据库+文档)
java·前端·数据库·vue.js·spring boot·后端·学习
K_Men6 小时前
springboot 接入Elasticsearch的聚合查询
spring boot·elasticsearch·jenkins
bluetata6 小时前
在 Spring Boot 中使用 Amazon Textract 从图像中提取文本
java·spring boot·后端
Andy工程师8 小时前
Netty 与 Spring Boot + HTTP 客户端(如 RestTemplate、WebClient)应用场景区别
spring boot·后端·http