# 深入理解Java注解,从使用到自定义

Java注解(Annotation)无处不在------@Override、@Test、@Autowired、@RequestMapping......天天在用,但注解到底是怎么工作的?怎么自定义一个?

今天从基础到实战,把注解讲透。

注解的本质

注解本身就是一个接口,继承自java.lang.annotation.Annotation。它不直接影响代码逻辑,只是标记信息,由编译器或框架在编译期/运行时读取。

java 复制代码
// @Override的定义(源码)
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

四个元注解

定义注解时用到的注解叫"元注解":

java 复制代码
@Target(ElementType.METHOD)          // 1. 注解能加在哪里
@Retention(RetentionPolicy.RUNTIME)  // 2. 注解的生命周期
@Documented                          // 3. 是否出现在Javadoc中
@Inherited                           // 4. 是否被子类继承
public @interface MyAnnotation {
    String value() default "";
    int count() default 0;
}

@Target:注解可以修饰的目标

值 含义
TYPE 类/接口/枚举
FIELD 字段
METHOD 方法
PARAMETER 方法参数
CONSTRUCTOR 构造器
LOCAL_VARIABLE 局部变量

@Retention:注解的生命周期

值 含义 能否反射读取
SOURCE 只在源码中 否
CLASS 源码和class文件中 否
RUNTIME 运行时保留 是(最常用)

自定义注解实战

场景:自定义一个权限校验注解

java 复制代码
// 定义注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequirePermission {
    String value();                    // 权限标识
    boolean required() default true;   // 是否必须
}

// 使用注解
@RestController
public class AdminController {

    @RequirePermission("user:delete")
    @DeleteMapping("/user/{id}")
    public Result deleteUser(@PathVariable Long id) {
        userService.delete(id);
        return Result.ok();
    }

    @RequirePermission(value = "report:view", required = false)
    @GetMapping("/report")
    public Result viewReport() {
        return Result.ok(reportService.getReport());
    }
}

用AOP处理注解逻辑:

java 复制代码
@Aspect
@Component
public class PermissionAspect {

    @Autowired
    private HttpServletRequest request;

    @Around("@annotation(annotation)")
    public Object checkPermission(ProceedingJoinPoint point,
                                   RequirePermission annotation) throws Throwable {
        String permission = annotation.value();

        // 获取当前用户
        String token = request.getHeader("Authorization");
        User currentUser = TokenUtil.parseUser(token);

        // 检查权限
        boolean hasPermission = permissionService.hasPermission(
            currentUser.getId(), permission);

        if (!hasPermission) {
            if (annotation.required()) {
                throw new BusinessException(403, "没有权限: " + permission);
            }
            return Result.ok(); // 非必须,无权限返回空
        }

        return point.proceed(); // 有权限,继续执行
    }
}

通过反射读取注解

java 复制代码
// 读取类上的注解
Class<?> clazz = UserController.class;
RequestMapping classMapping = clazz.getAnnotation(RequestMapping.class);
if (classMapping != null) {
    System.out.println("路径前缀: " + classMapping.value()[0]);
}

// 读取方法上的注解
Method method = clazz.getMethod("getUser", Long.class);
GetMapping getMapping = method.getAnnotation(GetMapping.class);
if (getMapping != null) {
    System.out.println("GET路径: " + getMapping.value()[0]);
}

// 读取参数上的注解
Parameter[] params = method.getParameters();
for (Parameter param : params) {
    PathVariable pv = param.getAnnotation(PathVariable.class);
    if (pv != null) {
        System.out.println("路径参数: " + pv.value());
    }
}

注解和配置的区别

对比 注解 配置文件
位置 代码上 外部文件
修改 需要重新编译 运行时可改
适合 代码级标记(权限、路由) 环境相关配置(数据库、密钥)
灵活性 低 高

实际项目中两者配合使用:注解标记代码逻辑,配置文件管理环境变量。


注解是Java元编程的基础。Spring之所以能做到"约定大于配置",背后大量依赖注解和反射。

理解了注解的原理,看框架源码时就不会一头雾水了。

你们项目中自定义过什么注解?

相关推荐
愿得一猪蹄2 小时前
# Java线程池实战,别再new Thread了
java
霸道流氓气质3 小时前
AI智能博客平台实战:RAG增强写作、多Agent协作与语义检索的Vue3 + Spring AI全栈实现
java·人工智能·spring
打工仔折腾 AI3 小时前
LangChain 工具调用实战:从 Tool 定义到 Agent 执行链路拆解
java·前端·人工智能·后端·python·langchain·ai agent 实战
哦虎!3 小时前
【JavaEE】网络原理 - TCP/IP
java
东风微鸣3 小时前
把 Tailscale 接进 Prometheus 监控
java·数据库·prometheus
看浪的路人3 小时前
第9讲:大模型应用监控与可观测性——OpenTelemetry + Prometheus + Grafana 全链路追踪
java·前端·spring
大侠归来3 小时前
事务能回滚吗?Pipeline 算事务吗?
java·数据库·oracle
蜗牛互联网3 小时前
Python调用Gemini Structured Outputs实现工单路由门禁
java·开发语言·人工智能·python
Wang's Blog3 小时前
Java 项目实战: 交友项目 - 产品定位、功能模块与客户端原型解读
java·开发语言·交友