Java自定义注解案例怎么开发

wen java案例 28

Java自定义注解案例开发:从零到实战的完整指南

目录导读

  1. Java注解基础认知 – 什么是注解?元注解有哪些?
  2. 自定义注解开发步骤 – 四个关键步骤详解
  3. 实战案例一:日志记录注解 – 自动记录方法调用日志
  4. 实战案例二:权限校验注解 – 拦截未授权访问
  5. 注解与AOP结合 – 如何让注解真正生效
  6. 常见问题与问答 – 你可能遇到的坑与解决方案

Java注解基础认知

注解(Annotation)是Java 5引入的一种元数据机制,它不直接影响程序逻辑,但可以被编译器或运行时框架解析,从而控制程序行为,理解自定义注解前,必须掌握四个元注解

Java自定义注解案例怎么开发

  • @Target:注解作用范围(方法、类、字段等)
  • @Retention:注解生命周期(源码、编译时、运行时)
  • @Inherited:是否允许子类继承
  • @Documented:是否包含在Javadoc中

问答:为什么开发自定义注解必须设置@Retention? 答:若不设置RetentionPolicy.RUNTIME,注解仅在编译前或编译时存在,运行时无法通过反射获取,导致AOP切面无法识别,绝大多数业务场景都需要RUNTIME


自定义注解开发四个核心步骤

步骤1:定义注解接口

使用@interface关键字定义,与定义接口类似,但可以设置默认值。

步骤2:配置元注解

根据用途选择@Target@Retention,建议大多数业务注解都设为@Retention(RUNTIME)

步骤3:添加注解属性

定义属性时需注意:属性类型必须为基本类型、String、Class、Enum、Annotation或它们的数组。

步骤4:编写解析逻辑

通过反射获取注解实例,然后执行对应的业务逻辑,通常与AOP(如Spring AOP)结合使用。

核心语法示例:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyLog {
    String value() default "";  // 属性可以设置默认值
}

问答:注解属性可以定义复杂对象吗? 答:不可以,属性只能使用基本类型、String、Class、枚举、注解或它们的数组,如果需要传递复杂对象,建议使用String属性配合JSON序列化。


实战案例一:日志记录注解

需求:自动记录每个方法的调用时间、参数和返回值,用于监控系统性能。

1 定义注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MethodLog {
    String description() default "";
}

2 使用AOP解析注解

在Spring项目中添加依赖后,编写切面类:

@Aspect
@Component
public class LogAspect {
    @Around("@annotation(methodLog)")
    public Object logExecution(ProceedingJoinPoint joinPoint, MethodLog methodLog) throws Throwable {
        long start = System.currentTimeMillis();
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        System.out.println("调用方法:" + methodName + ",描述:" + methodLog.description());
        Object result = joinPoint.proceed();
        long duration = System.currentTimeMillis() - start;
        System.out.println("方法执行耗时:" + duration + "ms");
        return result;
    }
}

3 使用示例

@RestController
public class UserController {
    @MethodLog(description = "查询用户信息")
    @GetMapping("/user/{id}")
    public User getUser(@PathVariable Long id) {
        // 业务逻辑
    }
}

问答:日志注解在分布式系统中如何避免性能损耗? 答:可以使用异步日志记录,例如将日志发送到消息队列(如Kafka),或利用Spring的@Async异步处理,同时建议在@Around中仅记录关键链路。


实战案例二:权限校验注解

需求:对特定接口进行角色校验,无权限时返回403。

1 定义权限注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequiresRole {
    String[] value() default {};
    String message() default "无访问权限";
}

2 编写校验切面

@Aspect
@Component
public class AuthAspect {
    @Before("@annotation(requiresRole)")
    public void checkAuthority(JoinPoint joinPoint, RequiresRole requiresRole) {
        String[] requiredRoles = requiresRole.value();
        // 从安全上下文获取当前用户角色(伪代码)
        String currentUserRole = SecurityContextHolder.getContext().getUserRole();
        boolean authenticated = Arrays.asList(requiredRoles).contains(currentUserRole);
        if (!authenticated) {
            throw new SecurityException(requiresRole.message());
        }
    }
}

3 在Controller中使用

@PostMapping("/admin/delete")
@RequiresRole({"ADMIN","SUPER_ADMIN"})
public void deleteUser(@RequestParam Long userId) {
    // 删除逻辑
}

问答:权限注解和Spring Security的@PreAuthorize有什么区别? 答:@PreAuthorize是Spring Security内置的表达式驱动注解,更灵活但依赖Security框架,自定义权限注解更轻量,适合不需要完整安全框架的微服务场景,且可以自定义错误消息与返回格式。


注解与AOP结合:最佳实践

1 为什么需要AOP?

自定义注解本身只是标记,必须有代码解析,AOP提供了一种非侵入式的方式,在方法执行前后自动执行切面逻辑,使注解真正“活”起来。

2 常见切面类型选择

  • @Around:最强大,可控制方法执行全过程
  • @Before / @After:仅做前置或后置处理
  • @AfterThrowing:处理异常场景

3 注意事项

  • 确保Spring AOP的切面类已被Spring管理(添加@Component@Service
  • 切点表达式要精确,避免影响无关方法
  • 注解属性尽量提供默认值,提高易用性

问答:如果自定义注解需要支持多个参数,如何优化代码可读性? 答:可以使用@AliasFor注解定义别名,或者将多个关联属性封装到一个@Repeatable注解中,Spring的@RequestMapping就是很好的范例。


常见问题与问答总结

Q1:自定义注解在编译时可以生效吗?

  • 可以,但需设置@Retention(RetentionPolicy.SOURCE),例如Lombok的@Data就是源代码级注解,通过APT(注解处理器)在编译期生成代码。

Q2:反射获取注解时性能不好怎么办?

  • 对于高频调用的注解,可以使用缓存:将方法与其注解映射关系保存在Map中,Spring AOP默认已做了这类优化。

Q3:自定义注解能用在字段上吗?

  • 可以,只需将@Target(ElementType.FIELD),常用于字段校验(如非空)、数据脱敏等场景。

Q4:多个注解如何同时生效?

  • Spring AOP支持多个切面,且每个注解可以对应一个单独的切面,建议将不同职责的注解拆分成独立的切面类,维护性更好。

Q5:自定义注解能影响序列化吗?

  • 不能直接,但可以通过Jackson的@JsonSerialize配合自定义注解实现,自定义一个@Sensitive注解,然后在序列化时根据注解脱敏数据。

自定义注解开发三要素

  1. 定义清晰:注解命名、属性和默认值要符合业务语义
  2. 解析准确:通过AOP或APT实现逻辑与标记的绑定
  3. 性能可控:避免在每次方法调用时都通过反射消耗性能,利用缓存或静态代理优化

掌握自定义注解,你就能构建出如日志、权限、缓存、限流等可复用的基础设施,大幅提升代码的可维护性和扩展性,下一次当你遇到重复的样板代码时,不妨思考:能否用一个注解来解决?

抱歉,评论功能暂时关闭!