Java案例怎么自定义注解?

wen java案例 8

Java自定义注解从入门到实战(附完整案例)

📖 目录导读

  1. 什么是Java注解?核心概念解析
  2. 自定义注解的语法与规则
  3. 实战案例一:日志记录注解
  4. 实战案例二:参数校验注解
  5. 注解的处理器实现(反射+动态代理)
  6. 常见问题与避坑指南(Q&A)
  7. 自定义注解的最佳实践

什么是Java注解?核心概念解析

在Java世界中,注解(Annotation)是一种元数据形式,它为代码提供额外信息而不直接改变程序行为,自Java 5引入以来,注解已成为Spring、MyBatis等框架的核心基石。

Java案例怎么自定义注解?

关键要点:

  • 注解以@interface声明,本质是特殊接口
  • 注解本身不执行逻辑,依赖处理器实现功能
  • 常见内置注解:@Override@Deprecated@SuppressWarnings

自定义注解的语法与规则

要创建自己的注解,需要掌握以下元注解:

元注解 作用
@Target 指定注解适用位置(方法、字段、类等)
@Retention 注解生命周期(源码、编译、运行时)
@Inherited 是否允许子类继承
@Documented 是否包含在JavaDoc中

示例:一个简单的自定义注解

import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LogExecutionTime {
    String value() default "default"; // 可配置参数
}

规则:

  • 注解方法不能有参数和异常声明
  • 返回值类型必须是基本类型、String、Class、枚举、注解或它们的数组
  • 可使用default设置默认值

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

很多开发者需要统计方法执行时间,通过自定义注解可以优雅实现。

步骤1:定义注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LogExecution {
    String module() default "System";
}

步骤2:编写处理器(AOP或代理)

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class LogAspect {
    @Around("@annotation(logExecution)")
    public Object logTime(ProceedingJoinPoint pjp, LogExecution logExecution) throws Throwable {
        long start = System.currentTimeMillis();
        Object result = pjp.proceed();
        long elapsed = System.currentTimeMillis() - start;
        System.out.println(logExecution.module() + " 执行耗时: " + elapsed + "ms");
        return result;
    }
}

步骤3:使用注解

@Service
public class UserService {
    @LogExecution(module = "用户服务")
    public User findUser(Long id) {
        // 业务逻辑...
    }
}

实战案例二:参数校验注解

很多业务需要对方法参数进行空值或范围校验,手动判断非常繁琐。

定义校验注解:

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

处理器实现:

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
public class ValidationUtil {
    public static void validate(Object[] args, Method method) {
        Parameter[] parameters = method.getParameters();
        for (int i = 0; i < parameters.length; i++) {
            if (parameters[i].isAnnotationPresent(NotNull.class) && args[i] == null) {
                NotNull annotation = parameters[i].getAnnotation(NotNull.class);
                throw new IllegalArgumentException(annotation.message());
            }
        }
    }
}

结合代理使用:

public class ProxyFactory {
    public static <T> T createProxy(T target) {
        return (T) Proxy.newProxyInstance(
            target.getClass().getClassLoader(),
            target.getClass().getInterfaces(),
            (proxy, method, args) -> {
                ValidationUtil.validate(args, method);
                return method.invoke(target, args);
            }
        );
    }
}

注解的处理器实现(反射+动态代理)

自定义注解的核心在于处理器,主要实现方式有:

  1. 运行时反射处理:最常用,通过getAnnotation()获取注解信息
  2. 编译时处理:使用APT(Annotation Processing Tool)生成代码
  3. 字节码增强:通过ASM或Javassist在类加载时修改字节码

反射处理核心代码:

Method method = obj.getClass().getMethod("someMethod");
if (method.isAnnotationPresent(MyAnnotation.class)) {
    MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);
    // 根据注解属性执行逻辑
}

注意: 只有RetentionPolicy.RUNTIME的注解才能通过反射获取。


常见问题与避坑指南(Q&A)

Q1:自定义注解为什么需要元注解?
A:元注解定义了注解的行为特性,没有@Retention(RUNTIME),运行时反射无法获取到注解信息。

Q2:注解可以有继承关系吗?
A:Java不支持注解继承,但可以通过组合多个元注解实现类似效果,但@Inherited仅对类上的注解生效,对方法不生效。

Q3:多个相同注解如何处理?
A:Java 8开始支持重复注解,需要定义容器注解,Spring框架对此有优雅支持。

Q4:为什么我的注解处理器不生效?
A:检查三点:①@Retention是否包含RUNTIME;②代理或AOP是否配置正确;③目标类是否被Spring容器管理(若使用AOP)。

Q5:自定义注解能替代接口吗?
A:不能,注解是元数据,接口是契约,两者用途不同,但可结合使用(如标记接口+注解处理)。


最佳实践建议

  1. 明确用途:注解应服务于特定横切关注点(日志、权限、缓存等)
  2. 合理选择生命周期:常规业务用RUNTIME,代码生成用SOURCE
  3. 保持简洁:一个注解只做一件事,避免参数过多
  4. 提供默认值:为注解属性设置合理的默认值
  5. 配套处理器:始终编写对应的处理器文档说明

自定义注解是提升框架开发效率和代码可读性的利器,掌握它,你将能构建出类似Spring Boot Starter的灵活组件,也能在团队中制定统一的代码规范,建议从简单的日志注解开始,逐步深入AOP和编译时处理技术。

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