一. 注解的基本概念

  • 注解(Annotation) 是 Java 1.5 引入的特性,用于为代码提供元数据(描述数据的数据)。
  • 自定义注解需使用 @interface 关键字定义,本质是一种特殊的接口。
  • 需通过元注解(描述注解的注解)指定自定义注解的行为(如作用范围、保留周期等)。

二. 常用元注解

定义自定义注解时,通常需要添加以下元注解:

  • @Target:指定注解可作用的位置(如类、方法、字段等),取值来自 ElementType 枚举:
    • TYPE(类 / 接口)、METHOD(方法)、FIELD(字段)、PARAMETER(参数)等。
  • @Retention:指定注解的保留周期,取值来自 RetentionPolicy 枚举:
    • SOURCE:仅在源码中保留(如 @Override)。
    • CLASS:保留到字节码(默认,类加载时会被丢弃)。
    • RUNTIME:保留到运行时(可通过反射解析,最常用)。
  • @Documented:指定注解是否在 Javadoc 中显示。
  • @Inherited:指定注解是否可被子类继承。

三.自定义注解的实现步骤

1.定义自定义注解

使用 @interface 关键字定义注解,并结合元注解指定行为:

import java.lang.annotation.*;

// 自定义注解:标记需要日志记录的方法
@Target(ElementType.METHOD) // 仅作用于方法
@Retention(RetentionPolicy.RUNTIME) // 运行时保留,可反射解析
@Documented // 生成Javadoc时包含该注解
public @interface Log {
    // 注解属性(类似接口方法,可指定默认值)
    String value() default "操作日志"; // 日志描述
    boolean needLogParams() default true; // 是否记录参数
}
  • 注解属性的定义格式:类型 属性名() [default 默认值]
  • 如果属性名是 value,使用时可省略属性名(如 @Log("保存用户"))。
2.使用注解

在目标方法上添加自定义注解:

public class UserService {

    // 使用自定义注解,指定日志描述
    @Log(value = "添加用户", needLogParams = true)
    public void addUser(String username, int age) {
        System.out.println("执行添加用户:" + username);
    }

    // 使用默认值
    @Log
    public void deleteUser(int id) {
        System.out.println("执行删除用户:" + id);
    }
}
3.解析注解

通过反射获取注解信息并处理:

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;

public class LogAnnotationProcessor {

    public static void process(Object service) throws Exception {
        // 获取类的Class对象
        Class<?> clazz = service.getClass();
        // 获取所有方法
        Method[] methods = clazz.getDeclaredMethods();

        for (Method method : methods) {
            // 判断方法是否被@Log注解标记
            if (method.isAnnotationPresent(Log.class)) {
                // 获取注解实例
                Log logAnnotation = method.getAnnotation(Log.class);
                
                // 解析注解属性
                String logDesc = logAnnotation.value();
                boolean needParams = logAnnotation.needLogParams();

                // 模拟日志记录逻辑
                System.out.println("\n===== 日志开始 =====");
                System.out.println("操作描述:" + logDesc);
                
                // 如果需要记录参数
                if (needParams) {
                    Parameter[] parameters = method.getParameters();
                    System.out.print("参数列表:");
                    for (Parameter param : parameters) {
                        System.out.print(param.getName() + "(" + param.getType().getSimpleName() + "), ");
                    }
                }
                System.out.println("\n===== 日志结束 =====");
            }
        }
    }

    public static void main(String[] args) throws Exception {
        // 测试:解析UserService中的@Log注解
        UserService userService = new UserService();
        process(userService);
    }
}

四、注意事项

  • 注解属性的类型只能是:基本类型、String、Class、枚举、注解或以上类型的数组。
  • 如果注解没有属性,称为 “标记注解”(如 @Override,标记该方法为重写)。
  • 解析注解时,需确保 @Retention 为 RUNTIME,否则反射无法获取。
  • 实际开发中,注解解析常结合 AOP(如 Spring AOP)实现无侵入式功能(如日志、权限校验)。

应用场景

  • 框架集成:Spring 的 @Transactional、MyBatis 的 @Select 等均基于自定义注解。
  • 代码简化:通过注解替代 XML 配置,提升可读性。
  • 运行时检查:如参数校验(@NotNull)、自动化测试等。
Logo

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

更多推荐