Java 注解与反射实战:自定义注解从入门到精通

Java 注解与反射实战:自定义注解从入门到精通

前言:注解到底是什么?

        你是否经常在 Java 代码中看到@Override@Deprecated这样的标记?这些就是注解 —— 一种给代码 "贴标签" 的机制。注解本身不直接影响代码执行,但能通过工具(如编译器)或框架(如 Spring)赋予代码额外含义。

        自定义注解则是让我们根据业务需求创建专属 "标签",结合反射机制能实现强大的动态逻辑(比如日志记录、权限校验、ORM 映射等)。本文将从基础到实战,带你掌握自定义注解的定义、元注解的作用,以及如何通过反射让注解 "生效"。

一、自定义注解基础:@interface 关键字

        自定义注解使用 @interface 关键字定义,本质上是一种特殊的接口(编译后会生成继承 java.lang.annotation.Annotation 的接口)。

1.1 最简单的自定义注解

// 定义一个空注解 public @interface MyFirstAnnotation { } 

这个注解没有任何属性,仅作为标记使用。可以直接标注在类、方法等元素上:

@MyFirstAnnotation public class Demo { @MyFirstAnnotation public void test() {} } 

1.2 带属性的注解

注解可以包含 "属性"(类似接口的抽象方法),使用时需要为属性赋值(除非有默认值)。

public @interface UserInfo { // 字符串属性 String name(); // 整数属性,带默认值 int age() default 18; // 数组属性 String[] hobbies() default {"coding"}; } 

使用时的语法(属性名 = 值):

@UserInfo(name = "张三", age = 20, hobbies = {"篮球", "游戏"}) public class Person {} 

💡 特殊规则:

若属性名是 value,且只有这一个属性需要赋值,可省略属性名:@MyAnnotation("test")数组属性若只有一个元素,可省略大括号:hobbies = "足球"

二、元注解:注解的 "注解"

        元注解是用于修饰注解的注解,规定了自定义注解的使用范围生命周期等特性。Java 内置了 4 种元注解:@Target@Retention@Documented@Inherited

2.1 @Target:指定注解能修饰哪些元素

   @Target 限制注解可标注的目标(如类、方法、字段等),参数是 ElementType 枚举数组,常用值:

ElementType作用范围
TYPE类、接口、枚举
METHOD方法
FIELD成员变量(包括枚举常量)
PARAMETER方法参数
CONSTRUCTOR构造方法
LOCAL_VARIABLE局部变量

示例:限制注解只能用于类和方法

import java.lang.annotation.Target; import java.lang.annotation.ElementType; @Target({ElementType.TYPE, ElementType.METHOD}) // 可修饰类和方法 public @interface Log { } 

如果把 @Log 标注在字段上,编译器会直接报错:

public class Demo { @Log // 编译错误:@Log不适用于字段 private String name; } 

💡 图示:@Target 的作用范围限制

2.2 @Retention:指定注解的生命周期

@Retention 决定注解保留到哪个阶段(源码、字节码、运行时),参数是 RetentionPolicy 枚举,必须指定:

RetentionPolicy生命周期说明能否被反射获取
SOURCE仅存在于源码中,编译后丢弃(如@Override不能
CLASS保留到字节码中,但 JVM 运行时不加载(默认值)不能
RUNTIME保留到运行时,JVM 加载,可通过反射获取

示例:让注解在运行时可被反射获取

import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @Retention(RetentionPolicy.RUNTIME) // 关键:保留到运行时 public @interface Permission { String value(); } 

💡 为什么 RUNTIME 重要?反射是在程序运行时动态获取类信息的机制,只有 RUNTIME 级别的注解才能被反射读取,这是注解与反射结合的核心前提。

💡 图示:注解的生命周期流程

2.3 @Documented:让注解出现在 API 文档中

        默认情况下,javadoc 生成的文档不会包含注解信息。@Documented 修饰的注解会被包含在文档中。

示例

import java.lang.annotation.Documented; @Documented // 生成文档时包含该注解 public @interface Description { String value(); } /** * 测试类 * @Description 这是一个测试类 */ @Description("测试类") public class Test {} 

生成的 javadoc 中,Test 类的文档会显示 @Description("测试类")

2.4 @Inherited:让注解可被继承

@Inherited 表示注解具有继承性:如果父类被该注解标注,子类会自动继承该注解(仅对类注解有效,方法 / 字段注解不继承)。

示例

import java.lang.annotation.Inherited; @Inherited // 允许继承 @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface InheritedAnnotation {} // 父类标注注解 @InheritedAnnotation class Parent {} // 子类未标注,但会继承父类的@InheritedAnnotation class Child extends Parent {} 

通过反射验证:

public class Test { public static void main(String[] args) { System.out.println(Child.class.isAnnotationPresent(InheritedAnnotation.class)); // 输出:true } } 

💡 图示:@Inherited 的继承效果

三、注解 + 反射:让注解 "生效"

        注解本身只是标记,必须通过反射获取注解信息并执行逻辑,才能真正发挥作用。反射提供了以下核心方法(在 ClassMethodField 等类中):

方法作用
getAnnotation(Class)获取指定类型的注解实例
getAnnotations()获取所有注解(包括继承的)
isAnnotationPresent(Class)判断是否存在指定注解

实战案例:用注解实现方法权限校验

需求:定义 @RequiresPermission 注解,标记方法需要的权限;通过反射调用方法前检查当前用户是否有权限,无权限则抛出异常。

步骤 1:定义注解
import java.lang.annotation.*; @Target(ElementType.METHOD) // 仅用于方法 @Retention(RetentionPolicy.RUNTIME) // 运行时可反射获取 public @interface RequiresPermission { String[] value(); // 所需权限列表 } 
步骤 2:使用注解标注方法
public class UserService { // 需要"user:query"权限 @RequiresPermission("user:query") public void queryUser() { System.out.println("查询用户成功"); } // 需要"user:add"或"admin"权限 @RequiresPermission({"user:add", "admin"}) public void addUser() { System.out.println("新增用户成功"); } } 
步骤 3:反射 + 注解实现权限校验
import java.lang.reflect.Method; import java.util.Arrays; import java.util.HashSet; import java.util.Set; public class PermissionChecker { // 模拟当前用户拥有的权限 private static final Set<String> CURRENT_USER_PERMISSIONS = new HashSet<>(Arrays.asList("user:query")); // 反射调用方法并校验权限 public static void invokeWithCheck(Object obj, String methodName) throws Exception { // 1. 获取方法对象 Method method = obj.getClass().getMethod(methodName); // 2. 检查方法是否有@RequiresPermission注解 if (method.isAnnotationPresent(RequiresPermission.class)) { // 3. 获取注解实例 RequiresPermission annotation = method.getAnnotation(RequiresPermission.class); // 4. 获取注解的权限列表 String[] requiredPermissions = annotation.value(); // 5. 校验权限 boolean hasPermission = false; for (String permission : requiredPermissions) { if (CURRENT_USER_PERMISSIONS.contains(permission)) { hasPermission = true; break; } } if (!hasPermission) { throw new SecurityException("权限不足,需要:" + Arrays.toString(requiredPermissions)); } } // 6. 权限通过,调用方法 method.invoke(obj); } public static void main(String[] args) throws Exception { UserService service = new UserService(); invokeWithCheck(service, "queryUser"); // 成功:查询用户成功 invokeWithCheck(service, "addUser"); // 失败:抛出SecurityException } } 

执行结果:

查询用户成功 Exception in thread "main" java.lang.SecurityException: 权限不足,需要:[user:add, admin] 

四、底层原理:注解本质与反射获取机制

  1. 注解实例的生成:当 JVM 加载被注解的类时,会通过动态代理生成注解接口的实现类实例(保存注解属性值)。
  2. 反射获取注解的过程:反射通过 getAnnotation() 方法从类 / 方法的元数据中获取代理实例,从而读取属性值。

注解的本质@interface 编译后会生成一个继承 java.lang.annotation.Annotation 的接口,例如:

// 编译后自动生成的代码(简化) public interface MyAnnotation extends Annotation { String value(); int age() default 18; } 

五、应用场景总结

注解 + 反射的组合在框架中被广泛使用:

日志记录:通过注解标记需要记录日志的方法,反射拦截并打印日志(如 Spring 的@Log)。ORM 映射:用注解关联 Java 类与数据库表(如 JPA 的@Entity@Column)。依赖注入:标记需要注入的对象(如 Spring 的@Autowired)。AOP 切面:通过注解定义切入点(如 Spring 的@Before@After)。参数校验:验证方法参数合法性(如 Jakarta 的@NotNull@Size)。

结语

        自定义注解是 Java 中 "声明式编程" 的核心体现,结合反射能极大简化代码逻辑、提高灵活性。掌握元注解的作用(尤其是@Target@Retention)是定义有效注解的前提,而反射则是让注解从 "标记" 变为 "可执行逻辑" 的桥梁。

尝试在项目中用注解解决重复逻辑(如日志、权限),你会感受到它的强大!

互动问题:你在项目中用过哪些自定义注解场景?欢迎在评论区分享~

Read more

2026最新秋叶绘世Stable Diffusion整合包下载 秋叶ComfyUI整合包下载 ai生图必备 绘世启动器.exe 绘世2.8.13下载 绘世启动器2.8.13下载地址

2026最新秋叶绘世Stable Diffusion整合包下载 秋叶ComfyUI整合包下载 ai生图必备 绘世启动器.exe 绘世2.8.13下载 绘世启动器2.8.13下载地址

2026最新秋叶绘世Stable Diffusion整合包下载 秋叶ComfyUI整合包下载 ai生图必备 绘世启动器.exe 绘世2.8.13下载 绘世启动器2.8.13下载地址 绘世2.8.13下载 | 绘世2.8.12下载 | 绘世启动器2.8.13下载地址 秋叶绘世Stable Diffusion整合包# 解压密码:bilibili-秋葉aaaki 【下载链接】 https://pan.quark.cn/s/41f42720f1c7?pwd=ZhBP 链接:https://pan.quark.cn/s/41f42720f1c7?pwd=ZhBP 提取码:ZhBP 解压密码:bilibili-秋葉aaaki 一定要用网盘官方客户端下载,否则压缩包极有可能损坏无法解压。下载完毕一定要先测试压缩包是否完好再解压!

By Ne0inhk

HY-Motion 1.0效果展示:同一文本输入下HY-Motion-1.0与Lite版画质对比

HY-Motion 1.0效果展示:同一文本输入下HY-Motion-1.0与Lite版画质对比 HY-Motion 1.0的发布,标志着文本生成3D动作技术进入了一个新阶段。它首次将模型的参数规模推向了十亿级别,旨在解决复杂指令理解和动作连贯性的核心难题。为了满足不同开发者的需求,团队同时提供了标准版(1.0B参数)和轻量版(Lite,0.46B参数)两个版本。 那么,一个最直接的问题就来了:在相同的文本指令下,参数规模更大的标准版和更轻量的Lite版,生成的动作质量究竟有多大差别?是肉眼可见的“碾压”,还是各有千秋?本文将为你带来一次直观的对比展示,通过同一组文本提示词,让你亲眼见证两个版本在动作细节、流畅度和指令遵循能力上的真实表现。 1. 核心能力概览:十亿参数带来了什么? 在深入对比之前,我们先快速了解一下HY-Motion 1.0的核心技术特点。它并非简单的模型放大,而是“力大砖飞”与“精雕细琢”的结合。 1.1 技术融合:DiT与流匹配的强强联合 模型的核心是将Diffusion

By Ne0inhk

uni-app 之 设置 tabBar

tabBar 是移动应用中常见的导航模式,uni-app 提供了丰富的 API 来动态控制 tabBar 的外观和行为。 1. uni.setTabBarItem(object) 动态设置 tabBar 某一项的内容 参数说明 属性类型默认值必填说明indexnumber是tabBar 的哪一项,从左边算起textstring否tab 上的按钮文字iconPathstring否图片路径,icon 大小限制为 40kbselectedIconPathstring否选中时的图片路径,icon 大小限制为 40kbsuccessfunction否接口调用成功的回调函数failfunction否接口调用失败的回调函数completefunction否接口调用结束的回调函数 示例代码 uni.setTabBarItem({index:0,text:"首页",iconPath:"/static/icon/home.png",selectedIconPath:"/static/icon/home-active.png",}); 2.

By Ne0inhk

基于MATLAB的障碍感知无人机导航实现,建筑障碍物、采用26连通域的三维A星算法路径规划、路径平滑优化以及动态无人机轨迹可视化功能

✅作者简介:热爱科研的Matlab仿真开发者,擅长毕业设计辅导、数学建模、数据处理、建模仿真、程序设计、完整代码获取、论文复现及科研仿真。 🍎 往期回顾关注个人主页:Matlab科研工作室  👇 关注我领取海量matlab电子书和数学建模资料  🍊个人信条:格物致知,完整Matlab代码获取及仿真咨询内容私信。 🔥 内容介绍 一、背景 在复杂环境中,如城市建筑区域,无人机需要具备可靠的导航能力以避开障碍物,安全高效地到达目标地点。实现障碍感知的无人机导航涉及路径规划、路径优化以及可视化展示等关键技术,这些技术对于提升无人机在复杂场景下的自主飞行能力至关重要。三维 A 星算法结合 26 连通域能够有效处理三维空间中的路径搜索问题,而路径平滑优化可使规划出的路径更适合无人机飞行,动态轨迹可视化则有助于操作人员实时掌握无人机的飞行状态。 二、原理 (一)基于 26 连通域的三维 A 星算法路径规划 1. A 星算法基础:A 星算法是一种启发式搜索算法,用于在图或网格中寻找从起点到目标点的最短路径。其核心思想是通过一个估值函数 f(n)=g(

By Ne0inhk