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)会影响性能,谨慎使用
- 避免在频繁调用的方法上使用复杂的注解
- 考虑异步处理耗时的切面逻辑