当前位置: 首页 > backend >正文

java基础-注解和反射

注解的定义

Java 注解(Annotation)是 Java 5 引入的一种元数据机制,用于为代码提供额外的信息,这些信息可以被编译器、开发工具或运行时环境读取和处理。注解本身不直接影响代码逻辑,但能通过其他工具增强代码的功能。

注解的本质

注解通过 @interface 关键字定义,编译后会生成 extends java.lang.annotation.Annotation 的接口。

元注解(如 @Retention、@Target)决定了注解的行为:

@Retention:控制注解的生命周期(源码、编译后的类文件、运行时)。

@Target:限制注解可以标记的目标(类、方法、字段等)。

示例

@LogExecutionTime 注解

@Retention(RetentionPolicy.RUNTIME)  // 注解保留到运行时
@Target(ElementType.METHOD)          // 只能标记方法
public @interface LogExecutionTime {String value() default "默认描述";
}

作用:标记需要记录执行时间的方法。

处理方式:需要通过反射在运行时读取注解,并动态增强方法逻辑(例如使用 AOP 框架)。

@Override 注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)  // 注解仅在源码阶段保留
public @interface Override {}

作用:告诉编译器检查方法是否重写了父类方法。

处理方式:由编译器直接处理,编译后注解会被丢弃,不会保留到类文件中。

注解的处理方式

注解的“功能”由处理工具实现

注解本身没有逻辑:注解只是标记,其功能依赖外部工具解析并实现。

不同生命周期对应不同处理阶段:

SOURCE 级别(如 @Override):
由编译器直接处理,编译后丢弃。编译器会检查方法是否符合重写规则(方法名、参数、返回值是否与父类一致)。

RUNTIME 级别(如 @LogExecutionTime):
需通过反射在运行时读取注解,结合动态代理或 AOP 框架实现功能(例如记录方法耗时)。

@Override 的实现原理(SOURECE)

编译器内置规则:@Override 的检查逻辑直接内置于 Java 编译器(如 javac),不是通过反射或外部工具。

编译时检查:当编译器发现 @Override 注解时,会检查该方法是否真的重写了父类或接口的方法。若不符合,直接报错。

为什么用 SOURCE 保留策略:
因为检查在编译时完成,不需要保留到类文件或运行时。

如下
在这里插入图片描述
在这里插入图片描述
手动实现编译时注解流程:
定义注解 → 编写注解处理器(extends AbstractProcessor) → 注册处理器 → 编译时触发处理

这里看到@Transaction在class文件中还存在,意思是编译完了还存在
在这里插入图片描述
@Transactional 的事务管理由 Spring AOP 自动实现
手动实现如下:

@LogExecutionTime 的实现原理(RUNTIME)

运行时反射读取:通过反射 API(如 Method.getAnnotation())获取注解。

动态增强代码:结合动态代理或 AOP 框架(如 AspectJ、Spring AOP)在方法执行前后插入记录时间的逻辑。

示例代码:

public class LogAspect {public Object logMethod(ProceedingJoinPoint joinPoint) throws Throwable {Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();LogExecutionTime annotation = method.getAnnotation(LogExecutionTime.class);if (annotation != null) {long start = System.currentTimeMillis();Object result = joinPoint.proceed();long end = System.currentTimeMillis();System.out.println(annotation.value() + ",耗时:" + (end - start) + "ms");return result;}return joinPoint.proceed();}
}

总结

1.注解是元数据:定义时不包含逻辑,功能由外部工具实现。

2.元注解控制行为:@Retention 和 @Target 是注解的“元数据”。

3.处理方式分阶段:

编译时:由编译器注解处理器处理(如 @Override、Lombok)。

运行时:通过反射框架处理(如 Spring 的 @Autowired)。

内置注解的特殊性:如 @Override 由编译器直接支持,无需开发者编写处理代码。

反射

反射(Reflection) 是 Java 提供的一种在运行时(Runtime)动态获取和操作类、方法、字段、注解等信息的机制。通过反射,可以在程序运行期间直接操作类的结构(如创建对象、调用方法、修改字段值),而无需在编译时确定具体的类或方法名称。简单来说,反射是让代码能够“自我感知”并动态修改自身行为的能力。

例如上面运行时候注解就是靠反射来实现

反射的核心功能

动态获取类的信息

通过类的全限定名(如 java.lang.String)加载类,并获取其方法、字段、构造器、注解等元信息。

Class<?> clazz = Class.forName("com.example.User");
获取类对象的方式
方法特点适用场景
User.class静态获取,无需实例化已知类名,直接引用
user.getClass()动态获取,依赖对象实例已有对象实例时
Class.forName()动态加载,触发类初始化类名在运行时确定(如配置文件)
ClassLoader.loadClass动态加载,不触发初始化(默认行为)自定义类加载逻辑

动态创建对象

通过类的无参或有参构造器实例化对象。

Object obj = clazz.newInstance(); // 无参构造器
Constructor<?> constructor = clazz.getConstructor(String.class, int.class);
Object obj = constructor.newInstance("张三", 25); // 有参构造器

动态调用方法

通过方法名称和参数类型调用方法(包括私有方法)。

Method method = clazz.getMethod("setName", String.class);
method.invoke(obj, "李四"); // 调用 setName("李四")

动态访问或修改字段

访问或修改对象的字段值(包括私有字段)。

Field field = clazz.getDeclaredField("name");
field.setAccessible(true); // 解除私有字段的访问限制
field.set(obj, "王五"); // 修改字段值

动态读取注解信息

获取类、方法、字段上的注解,并根据注解内容执行逻辑。

if (clazz.isAnnotationPresent(MyAnnotation.class)) {MyAnnotation annotation = clazz.getAnnotation(MyAnnotation.class);System.out.println(annotation.value());
}

反射的使用场景

  1. 框架开发
    依赖注入:如 Spring 的 @Autowired、@Resource。

ORM 映射:如 Hibernate 通过反射将数据库字段映射到实体类。

API 路由:如 Spring MVC 根据 @RequestMapping 动态调用 Controller 方法。

  1. 动态代理与 AOP
    动态生成代理对象:如 JDK 动态代理基于接口,CGLIB 基于类。

AOP 切面编程:通过反射拦截方法调用,插入日志、事务等逻辑。

  1. 通用工具类
    JSON 序列化/反序列化:如 Jackson 通过反射解析对象字段。

单元测试工具:如 JUnit 通过反射调用 @Test 标记的方法。

  1. 插件化架构
    动态加载类:如通过 ClassLoader 加载外部 JAR 中的插件。

  2. 注解驱动开发
    运行时注解处理:如结合 @Transactional 实现事务管理。

示例(动态获取类型)

    protected T getData(MessageDTO dto) {try {ParameterizedType parameterizedType = (ParameterizedType) this.getClass().getGenericSuperclass();//子类获取继承的父类的泛型@SuppressWarnings("unchecked")Class<T> clazz = (Class<T>) parameterizedType.getActualTypeArguments()[0];//获取泛型具体类型if(clazz.isInstance(String.class)) {return (T) dto.getDataJson();}return JSON.parseObject(dto.getDataJson(), clazz); //转换相应类型}catch (Exception e) {log.error("异步执行流程 转换数据异常 数据:{}", dto, e);throw new RuntimeException("数据转换异常", e);}}

反射的作用范围

反射对实例对象的修改

​​可修改的范围​​

​​实例字段​​:

通过反射可读取或修改实例的字段值(包括私有字段),例如:

Field field = obj.getClass().getDeclaredField("fieldName");
field.setAccessible(true);
field.set(obj, newValue);  // 直接修改实例的字段值
实例方法​​:

可调用实例的私有方法,例如:

Method method = obj.getClass().getDeclaredMethod("methodName");
method.setAccessible(true);
method.invoke(obj, args);  // 调用实例的私有方法
生命周期限制​​

修改仅对当前实例有效,其他实例不受影响。
若实例被垃圾回收,修改也随之消失。

反射对类对象的修改

​​类加载前的修改​​

​​修改类的结构​​(如新增字段、方法):需在类加载前通过字节码增强工具(如 ASM、Byte Buddy)修改类的字节码,再通过自定义 ClassLoader 加载修改后的类。
​​示例场景​​:在 Spring Boot 中,通过 @Configuration 动态注册 Bean 时,底层依赖类加载前的结构修改。

​类加载后的限制​​

​​无法修改类的结构​​:标准反射 API 无法在类加载后新增/删除字段或方法。
​​可修改的类属性​​:
​​静态字段​​:通过反射可直接修改静态字段的值(包括 private static 字段),例如:

Field staticField = MyClass.class.getDeclaredField("staticField");
staticField.setAccessible(true);
staticField.set(null, newValue);  // 修改静态字段

静态代码块​​:无法修改已执行的静态代码块逻辑,但可通过反射调用静态方法间接影响行为。

总结

反射是 Java 在运行时动态获取类的元信息(如方法、字段、注解)并操作对象的能力,突破编译时类型限制,常用于框架开发(如 Spring 依赖注入、Hibernate ORM 映射)和动态代理等场景

http://www.xdnf.cn/news/5653.html

相关文章:

  • 9.0 C# 调用solidworks介绍1
  • 分词器工作流程和Ik分词器详解
  • SMT贴片加工技术解析与应用要点
  • 荣耀手机,系统MagicOS 9.0 USB配置没有音频来源后无法被adb检测到,无法真机调试的解决办法
  • vite项目使用i18n-ally未读取到文件
  • MongoDB 操作可能抛出哪些异常? 如何优雅的处理?
  • 精品可编辑PPT | 全面风险管理信息系统项目建设风控一体化标准方案
  • kotlin-协程(什么是一个协程)
  • 智能SQL优化工具集成:从概念到实践
  • 面试篇:Spring MVC
  • C++多态讲解
  • 【Redis】分布式锁的实现
  • Excel分组计算求和的两种实现方案
  • 【Python】Python常用数据类型判断方法详解
  • K8S中构建双架构镜像-从零到成功
  • Go语言爬虫系列教程(一) 爬虫基础入门
  • 新能源汽车电池加热技术:传统膜加热 vs. 脉冲自加热
  • Porting Linux to a new processor architecture, part 1: The basics
  • 异步FIFO的学习
  • Linux 上安装RabbitMQ
  • android14优化ntp时间同步
  • 全栈工程师实战手册:LuatOS日志系统开发指南!
  • Matlab 垂向七自由度轨道车辆开关型半主动控制
  • Spring Boot集成RabbitMQ高级篇:可靠性与性能提升
  • OpenHarmony Linux内核本地管理
  • 网络爬虫学习之正则表达式
  • Edge浏览器打开PDF文件显示空白(每次需要等上一会)
  • FPGA前瞻篇-计数器设计与实现实例
  • Spark SQL 运行架构详解(专业解释+番茄炒蛋例子解读)
  • Mosaic数据增强技术