java自定义注解

我来详细介绍一下 Java 后端自定义注解的创建和使用。

1. 注解的基本概念

注解(Annotation)是 Java 提供的一种元数据机制,用于为代码添加信息。

2. 创建自定义注解

2.1 基本注解定义

import java.lang.annotation.*;

// 元注解:定义注解的生命周期和目标
@Target(ElementType.METHOD) // 注解可以用在方法上
@Retention(RetentionPolicy.RUNTIME) // 注解在运行时保留
@Documented // 包含在 JavaDoc 中
public @interface LogAnnotation {
    String value() default ""; // 注解属性
    String module() default ""; // 模块名称
    String operation() default ""; // 操作类型
}

2.2 常用元注解

  • @Target:指定注解的使用范围

    • ElementType.METHOD:方法
    • ElementType.TYPE:类、接口
    • ElementType.FIELD:字段
    • ElementType.PARAMETER:参数
  • @Retention:指定注解的保留策略

    • RetentionPolicy.SOURCE:仅源码级别
    • RetentionPolicy.CLASS:编译期保留
    • RetentionPolicy.RUNTIME:运行时保留(可通过反射获取)

3. 常用自定义注解示例

3.1 日志注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface OperateLog {
    String module() default "";
    String type() default "";
    String desc() default "";
}

3.2 权限注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequirePermission {
    String[] value(); // 需要的权限数组
    Logical logical() default Logical.AND; // 权限逻辑:AND 或 OR
}

// 权限逻辑枚举
enum Logical {
    AND, OR
}

3.3 缓存注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Cacheable {
    String key(); // 缓存key
    long expire() default 300; // 过期时间,默认5分钟
    TimeUnit timeUnit() default TimeUnit.SECONDS;
}

3.4 参数校验注解

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface NotEmpty {
    String message() default "参数不能为空";
}

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface Range {
    long min() default Long.MIN_VALUE;
    long max() default Long.MAX_VALUE;
    String message() default "参数超出范围";
}

3.5 限流注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RateLimit {
    String key() default ""; // 限流key
    int limit() default 100; // 限制次数
    int period() default 60; // 时间周期(秒)
    String message() default "系统繁忙,请稍后再试";
}

4. 注解处理器

4.1 AOP 方式处理注解

@Aspect
@Component
@Slf4j
public class AnnotationAspect {
    
    // 处理日志注解
    @Around("@annotation(operateLog)")
    public Object handleOperateLog(ProceedingJoinPoint joinPoint, OperateLog operateLog) throws Throwable {
        String methodName = joinPoint.getSignature().getName();
        String module = operateLog.module();
        String type = operateLog.type();
        String desc = operateLog.desc();
        
        long startTime = System.currentTimeMillis();
        try {
            log.info("【操作日志】模块:{},类型:{},方法:{},描述:{}", 
                    module, type, methodName, desc);
            
            Object result = joinPoint.proceed();
            
            long endTime = System.currentTimeMillis();
            log.info("【操作完成】方法:{},耗时:{}ms", methodName, (endTime - startTime));
            
            return result;
        } catch (Exception e) {
            log.error("【操作异常】方法:{},异常:{}", methodName, e.getMessage());
            throw e;
        }
    }
    
    // 处理权限注解
    @Around("@annotation(requirePermission)")
    public Object checkPermission(ProceedingJoinPoint joinPoint, RequirePermission requirePermission) throws Throwable {
        String[] permissions = requirePermission.value();
        Logical logical = requirePermission.logical();
        
        // 获取当前用户权限(从Session、Token等)
        Set<String> userPermissions = getCurrentUserPermissions();
        
        boolean hasPermission;
        if (logical == Logical.AND) {
            // 需要拥有所有权限
            hasPermission = Arrays.stream(permissions)
                    .allMatch(userPermissions::contains);
        } else {
            // 只需要拥有其中一个权限
            hasPermission = Arrays.stream(permissions)
                    .anyMatch(userPermissions::contains);
        }
        
        if (!hasPermission) {
            throw new RuntimeException("权限不足");
        }
        
        return joinPoint.proceed();
    }
    
    // 处理限流注解
    @Around("@annotation(rateLimit)")
    public Object handleRateLimit(ProceedingJoinPoint joinPoint, RateLimit rateLimit) throws Throwable {
        String key = rateLimit.key();
        int limit = rateLimit.limit();
        int period = rateLimit.period();
        
        // 使用Redis实现限流
        String redisKey = "rate_limit:" + key;
        Long current = redisTemplate.opsForValue().increment(redisKey, 1);
        
        if (current == 1) {
            // 第一次设置过期时间
            redisTemplate.expire(redisKey, period, TimeUnit.SECONDS);
        }
        
        if (current > limit) {
            throw new RuntimeException(rateLimit.message());
        }
        
        return joinPoint.proceed();
    }
    
    private Set<String> getCurrentUserPermissions() {
        // 实现获取当前用户权限的逻辑
        return new HashSet<>();
    }
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
}

4.2 拦截器方式处理注解

@Component
public class AuthInterceptor implements HandlerInterceptor {
    
    @Override
    public boolean preHandle(HttpServletRequest request, 
                           HttpServletResponse response, 
                           Object handler) throws Exception {
        
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }
        
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Method method = handlerMethod.getMethod();
        
        // 检查权限注解
        if (method.isAnnotationPresent(RequirePermission.class)) {
            RequirePermission annotation = method.getAnnotation(RequirePermission.class);
            String[] permissions = annotation.value();
            
            // 权限校验逻辑
            if (!checkPermissions(permissions)) {
                response.setStatus(403);
                response.getWriter().write("权限不足");
                return false;
            }
        }
        
        // 检查其他注解...
        return true;
    }
    
    private boolean checkPermissions(String[] requiredPermissions) {
        // 实现权限检查逻辑
        return true;
    }
}

5. 实际使用示例

5.1 在 Controller 中使用

@RestController
@RequestMapping("/user")
@Slf4j
public class UserController {
    
    @Autowired
    private UserService userService;
    
    @PostMapping("/create")
    @RequirePermission({"user:create"})
    @OperateLog(module = "用户管理", type = "新增", desc = "创建用户")
    @RateLimit(key = "user_create", limit = 10, period = 60)
    public Result createUser(@RequestBody @Valid UserDTO userDTO) {
        return Result.success(userService.createUser(userDTO));
    }
    
    @GetMapping("/{id}")
    @RequirePermission({"user:query"})
    @Cacheable(key = "'user:' + #id", expire = 600)
    public Result getUser(@PathVariable Long id) {
        return Result.success(userService.getUserById(id));
    }
    
    @PutMapping("/{id}")
    @RequirePermission({"user:update"})
    @OperateLog(module = "用户管理", type = "修改", desc = "更新用户信息")
    public Result updateUser(@PathVariable Long id, @RequestBody UserDTO userDTO) {
        return Result.success(userService.updateUser(id, userDTO));
    }
    
    @DeleteMapping("/{id}")
    @RequirePermission({"user:delete"})
    @OperateLog(module = "用户管理", type = "删除", desc = "删除用户")
    public Result deleteUser(@PathVariable Long id) {
        userService.deleteUser(id);
        return Result.success();
    }
}

5.2 在 Service 中使用

@Service
@Slf4j
public class UserService {
    
    @Autowired
    private UserMapper userMapper;
    
    @OperateLog(module = "用户服务", type = "业务操作", desc = "创建用户")
    public User createUser(UserDTO userDTO) {
        // 业务逻辑
        User user = convertToUser(userDTO);
        userMapper.insert(user);
        return user;
    }
    
    @Cacheable(key = "'user:' + #id", expire = 600)
    public User getUserById(Long id) {
        return userMapper.selectById(id);
    }
}

6. 配置类

6.1 AOP 配置

@Configuration
@EnableAspectJAutoProxy
public class AopConfig {
}

6.2 拦截器配置

@Configuration
public class WebConfig implements WebMvcConfigurer {
    
    @Autowired
    private AuthInterceptor authInterceptor;
    
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(authInterceptor)
                .addPathPatterns("/**")
                .excludePathPatterns("/login", "/register");
    }
}

7. 总结

自定义注解在 Java 后端开发中非常有用,可以用于:

  1. 日志记录:自动记录操作日志
  2. 权限控制:统一权限校验
  3. 缓存管理:自动缓存方法结果
  4. 参数校验:自定义参数验证规则
  5. 限流控制:接口访问频率限制
  6. 事务管理:自定义事务控制
  7. 数据脱敏:自动处理敏感信息
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值