一、💡 一句话理解
核心结论
注解就是贴在代码上的标签。它用来说明“这段代码有什么特点或规则”,但标签本身通常不会执行任何事情。
例如,@Override 表示“这是重写的方法”。自定义注解则是我们自己设计一种标签,再由程序读取标签并做对应处理。
二、🧭 注解是什么
2.1 Java 自带的注解
@Override
public String toString() {
return "示例";
}
@Deprecated
public void oldMethod() {
// 表示这个方法不建议继续使用
}| 注解 | 含义 |
|---|---|
@Override | 要求编译器检查该方法是否真的重写了父类或接口的方法。 |
@Deprecated | 提醒调用者:这段代码已经过时,不建议再使用。 |
@SuppressWarnings | 让编译器忽略指定类型的警告。 |
可以把注解看作“附在代码上的说明书”。它让编译器、框架或我们自己写的程序知道该如何对待这段代码。
2.2 注解本身不等于功能
下面的 @NeedLogin 是本篇示例自行定义的自定义注解,不是 Java 自带注解。它标在方法上:
@NeedLogin
public void viewProfile() {
System.out.println("查看个人资料");
}这行代码不会自动完成登录校验。@NeedLogin 只是一个标签;还需要一个专门的类读取它,并在调用方法前检查用户是否已登录。
写上 @NeedLogin → 程序读取 @NeedLogin → 程序决定是否校验登录
标签 读取标签 执行规则三、⚙️ 怎样定义一个自定义注解
3.1 最简单的写法
创建文件 NeedLogin.java:
// public @interface 表示:定义一个名为 NeedLogin 的注解类型。
public @interface NeedLogin {
}使用它:
public class UserService {
@NeedLogin
public void viewProfile() {
System.out.println("查看个人资料");
}
}3.2 用 @Target 限制标签贴在哪里
注解可以写在类、方法、字段等不同位置。@Target 用来限制位置。
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
// 只允许 @NeedLogin 标在方法上。
@Target(ElementType.METHOD)
public @interface NeedLogin {
}常用位置:
| 写法 | 可标注的位置 |
|---|---|
ElementType.TYPE | 类、接口、枚举。 |
ElementType.METHOD | 方法。 |
ElementType.FIELD | 字段。 |
ElementType.PARAMETER | 方法参数。 |
3.3 用 @Retention 决定何时还能读到标签
如果希望程序在运行时读取注解,要使用 RetentionPolicy.RUNTIME。
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME) // 程序运行时仍可通过反射读取
public @interface NeedLogin {
}| 保留策略 | 适用情况 |
|---|---|
SOURCE | 只在源码阶段使用,例如编译器检查。 |
CLASS | 写入编译后的 class 文件;这是默认值。 |
RUNTIME | 程序运行时可读取,常用于框架和自定义规则。 |
四、🚀 完整小例子:读取注解并执行规则
下面用三个类演示完整过程:标签声明规则,LoginChecker 读取标签,Demo 验证结果。
4.1 前置准备
新建一个空目录,将以下三个文件放在同一目录。需要 JDK 8 或更高版本。
4.2 定义标签和业务类
文件 NeedLogin.java:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
// 这个标签只能用于方法,并且运行时可以被读取。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface NeedLogin {
}文件 UserService.java:
public class UserService {
// 这表示:调用该方法前,检查程序应要求用户已登录。
@NeedLogin
public void viewProfile() {
System.out.println("正在查看个人资料");
}
// 没有标签的方法,不需要登录校验。
public void viewHelp() {
System.out.println("正在查看帮助页面");
}
}4.3 写一个读取标签的类
文件 LoginChecker.java:
import java.lang.reflect.Method;
public class LoginChecker {
/**
* 调用指定方法前,检查该方法是否有 @NeedLogin 标签。
*
* @param target 业务对象
* @param methodName 要调用的方法名
* @param loggedIn 当前用户是否已登录
*/
public void invoke(Object target, String methodName, boolean loggedIn)
throws ReflectiveOperationException {
// 从业务对象中找到对应的公开、无参数方法。
Method method = target.getClass().getMethod(methodName);
// isAnnotationPresent 用来判断方法是否标了指定注解。
boolean needLogin = method.isAnnotationPresent(NeedLogin.class);
// 标了标签且未登录:直接拒绝,不执行业务方法。
if (needLogin && !loggedIn) {
System.out.println("请先登录");
return;
}
// 其他情况:通过反射执行真实业务方法。
method.invoke(target);
}
}4.4 启动和验证
文件 Demo.java:
public class Demo {
public static void main(String[] args) throws ReflectiveOperationException {
UserService userService = new UserService();
LoginChecker checker = new LoginChecker();
// 未登录访问需要登录的方法:被拦截。
checker.invoke(userService, "viewProfile", false);
// 已登录访问同一方法:允许执行。
checker.invoke(userService, "viewProfile", true);
// 未登录访问没有标签的方法:直接执行。
checker.invoke(userService, "viewHelp", false);
}
}在该目录运行:
javac *.java
java Demo预期输出:
请先登录
正在查看个人资料
正在查看帮助页面这里真正规定“@NeedLogin 有什么效果”的,是 LoginChecker 中的判断代码,而不是注解定义本身。
五、📌 注解一般用于什么
| 用途 | 常见例子 | 说明 |
|---|---|---|
| 编译检查 | @Override | 让编译器帮忙发现错误。 |
| 框架配置 | @Component、@Service | 告诉框架如何管理一个类。 |
| 接口映射 | @RequestMapping | 声明一个方法对应哪个请求地址。 |
| 参数校验 | @NotNull、@NotBlank | 声明参数不能为空。 |
| 自定义规则 | @NeedLogin、@Audit | 标记需要登录、记录日志等规则。 |
六、📝 总结
- 注解是标签,不是自动执行的代码。
- 用
public @interface定义自定义注解。 @Target限制标签能贴在哪里;@Retention(RUNTIME)让程序运行时能读取标签。- 注解的具体含义由读取它的类决定。
记忆句:注解负责说明,程序负责执行。