反射和注解深度解析
Java 底层源码研究必备:涵盖反射核心 API、注解原理、工厂模式 + 反射实现 AOP
第一部分:反射(Reflection)
一、反射基础概念
1.1 什么是反射
反射是 Java 在运行时动态获取类信息(构造方法、成员变量、成员方法、注解等)并操作对象的机制。
核心思想:JVM 加载类时会在方法区生成对应的 Class 对象,该对象包含了类的全部结构信息。反射就是通过这个 Class 对象反向获取类的完整信息。
// 正常方式:已知类 → 创建对象
Person p = new Person();
// 反射方式:获取 Class → 动态创建对象
Class<?> clazz = Class.forName("com.example.Person");
Object obj = clazz.getDeclaredConstructor().newInstance();
1.2 反射的用途
|
场景 |
说明 |
|---|---|
|
框架底层 |
Spring IoC、MyBatis ORM、Hibernate 等 |
|
动态代理 |
JDK 动态代理、CGLIB |
|
AOP 切面编程 |
方法拦截、事务管理、日志记录 |
|
注解解析 |
运行时获取注解信息执行对应逻辑 |
|
配置化开发 |
通过配置文件驱动类加载与调用 |
|
IDE 智能提示 |
自动补全、类结构分析 |
二、获取 Class 对象的四种方式
// 方式1:Class.forName()(最常用,动态加载)
Class<?> clazz1 = Class.forName("com.example.Person");
// 方式2:类名.class(已知类名时使用)
Class<Person> clazz2 = Person.class;
// 方式3:对象.getClass()
Person p = new Person();
Class<? extends Person> clazz3 = p.getClass();
// 方式4:类加载器(高级用法)
ClassLoader loader = ClassLoader.getSystemClassLoader();
Class<?> clazz4 = loader.loadClass("com.example.Person");
// 四种方式获得的 Class 对象是同一个实例
System.out.println(clazz1 == clazz2); // true
类加载过程
.java 源文件 → 编译 → .class 字节码 → ClassLoader 加载 → JVM 方法区生成 Class 对象
三、反射操作构造方法(Constructor)
public class Person {
private String name;
private int age;
public Person() {}
public Person(String name) {
this.name = name;
}
private Person(String name, int age) {
this.name = name;
this.age = age;
}
}
// ========== 反射操作构造方法 ==========
Class<?> clazz = Class.forName("com.example.Person");
// 1. 获取所有 public 构造方法
Constructor<?>[] constructors = clazz.getConstructors();
// 2. 获取所有构造方法(含 private)
Constructor<?>[] declaredConstructors = clazz.getDeclaredConstructors();
// 3. 获取无参构造并创建实例
Constructor<?> noArgCtor = clazz.getConstructor();
Object obj = noArgCtor.newInstance();
// 4. 获取有参 public 构造
Constructor<?> ctor = clazz.getConstructor(String.class);
Object obj2 = ctor.newInstance("张三");
// 5. 获取 private 构造 —— 暴力反射
Constructor<?> privateCtor = clazz.getDeclaredConstructor(String.class, int.class);
privateCtor.setAccessible(true); // 解除访问限制
Object obj3 = privateCtor.newInstance("李四", 25);
四、反射操作成员变量(Field)
public class Person {
public String nickname;
private String name;
protected int age;
}
// ========== 反射操作 Field ==========
Class<?> clazz = Class.forName("com.example.Person");
Object obj = clazz.getDeclaredConstructor().newInstance();
// 1. 获取所有 public 字段(含继承的)
Field[] fields = clazz.getFields();
// 2. 获取本类所有字段(含 private)
Field[] declaredFields = clazz.getDeclaredFields();
// 3. 获取指定 public 字段并设值
Field nickField = clazz.getField("nickname");
nickField.set(obj, "小明");
// 4. 获取 private 字段并设值
Field nameField = clazz.getDeclaredField("name");
nameField.setAccessible(true); // 暴力反射
nameField.set(obj, "张三");
// 5. 获取字段值
String name = (String) nameField.get(obj);
System.out.println(name); // 张三
// 6. 获取字段类型、修饰符
Class<?> fieldType = nameField.getType(); // String.class
int modifiers = nameField.getModifiers(); // 修饰符(Modifier.toString()转为字符串)
五、反射操作成员方法(Method)
public class Person {
public void sayHello() {
System.out.println("Hello");
}
public String greet(String name) {
return "Hi, " + name;
}
private void secretMethod() {
System.out.println("This is private");
}
public static void staticMethod(String msg) {
System.out.println("Static: " + msg);
}
}
// ========== 反射操作 Method ==========
Class<?> clazz = Class.forName("com.example.Person");
Object obj = clazz.getDeclaredConstructor().newInstance();
// 1. 获取所有 public 方法(含继承自 Object 的)
Method[] methods = clazz.getMethods();
// 2. 获取本类所有方法(含 private,不含继承)
Method[] declaredMethods = clazz.getDeclaredMethods();
// 3. 调用无参 public 方法
Method sayHello = clazz.getMethod("sayHello");
sayHello.invoke(obj);
// 4. 调用有参 public 方法
Method greet = clazz.getMethod("greet", String.class);
Object result = greet.invoke(obj, "张三");
System.out.println(result); // Hi, 张三
// 5. 调用 private 方法
Method secret = clazz.getDeclaredMethod("secretMethod");
secret.setAccessible(true);
secret.invoke(obj);
// 6. 调用 static 方法(obj 传 null)
Method staticMethod = clazz.getMethod("staticMethod", String.class);
staticMethod.invoke(null, "测试");
// 7. 获取方法返回值类型、参数类型
Class<?> returnType = greet.getReturnType(); // String.class
Class<?>[] paramTypes = greet.getParameterTypes(); // [String.class]
六、反射操作数组
// 1. 动态创建数组
int[] arr = (int[]) Array.newInstance(int.class, 5);
// 等价于 int[] arr = new int[5];
// 2. 设置数组元素
Array.set(arr, 0, 100);
Array.set(arr, 1, 200);
// 3. 获取数组元素
int value = Array.getInt(arr, 0); // 100
// 4. 获取数组长度
int length = Array.getLength(arr); // 5
七、反射性能与优化
// 反射调用比直接调用慢的原因:
// 1. 需要检查访问权限
// 2. 需要做类型检查和包装
// 3. 无法被 JIT 编译器优化
// 优化方式:setAccessible(true)
// 关闭访问安全检查,可显著提升反射性能
Method method = clazz.getDeclaredMethod("sayHello");
method.setAccessible(true); // 减少安全检查开销
第二部分:注解(Annotation)
一、注解基础概念
注解是 JDK 5 引入的元数据标记,可以附加在类、方法、字段、参数等元素上,通过反射在运行时读取并执行相应逻辑。
// 常见内置注解
@Override // 标记方法重写
@Deprecated // 标记已过时
@SuppressWarnings // 抑制警告
@FunctionalInterface // 标记函数式接口
@SafeVarargs // 标记可变参数安全
二、元注解(注解的注解)
// 1. @Retention —— 指定注解的生命周期
@Retention(RetentionPolicy.SOURCE) // 仅保留在源码中,编译时丢弃(如 @Override)
@Retention(RetentionPolicy.CLASS) // 保留到 .class 文件,运行时不加载(默认)
@Retention(RetentionPolicy.RUNTIME) // 保留到运行时,可通过反射读取(最常用)
// 2. @Target —— 指定注解可以修饰的目标
@Target(ElementType.TYPE) // 类、接口、枚举
@Target(ElementType.FIELD) // 成员变量
@Target(ElementType.METHOD) // 方法
@Target(ElementType.PARAMETER) // 参数
@Target(ElementType.CONSTRUCTOR) // 构造方法
@Target(ElementType.ANNOTATION_TYPE) // 注解类型
@Target(ElementType.PACKAGE) // 包
// 3. @Documented —— 标记注解会被 javadoc 文档化
// 4. @Inherited —— 标记注解可被子类继承
// 父类有 @Inherited 注解时,子类默认也具有该注解
// 5. @Repeatable —— 标记注解可在同一位置重复使用(JDK 8+)
三、自定义注解
// ========== 定义一个运行时注解 ==========
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@Documented
public @interface MyAnnotation {
// 成员变量,定义时类似方法声明
String value() default ""; // 默认值
String name() default "default";
int age() default 0;
String[] tags() default {}; // 数组类型
}
// ========== 使用注解 ==========
@MyAnnotation(name = "用户控制器", tags = {"user", "api"})
public class UserController {
@MyAnnotation(value = "获取用户列表", age = 1)
public void getUserList() {
// ...
}
@MyAnnotation("保存用户") // value 可省略直接赋值
public void saveUser() {
// ...
}
}
四、反射解析注解
// ========== 解析类上的注解 ==========
Class<?> clazz = UserController.class;
// 判断是否有指定注解
if (clazz.isAnnotationPresent(MyAnnotation.class)) {
MyAnnotation annotation = clazz.getAnnotation(MyAnnotation.class);
System.out.println("类名: " + annotation.name());
System.out.println("标签: " + Arrays.toString(annotation.tags()));
}
// ========== 解析方法上的注解 ==========
for (Method method : clazz.getDeclaredMethods()) {
if (method.isAnnotationPresent(MyAnnotation.class)) {
MyAnnotation ann = method.getAnnotation(MyAnnotation.class);
System.out.println("方法: " + method.getName());
System.out.println("描述: " + ann.value());
System.out.println("---");
}
}
五、常见自定义注解实战示例
5.1 字段校验注解(类似 Bean Validation)
// 定义
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface NotNull {
String message() default "字段不能为空";
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Length {
int min() default 0;
int max() default Integer.MAX_VALUE;
String message() default "长度不符合要求";
}
// 使用
public class User {
@NotNull(message = "用户名不能为空")
private String username;
@Length(min = 6, max = 20, message = "密码长度6-20位")
private String password;
}
// 校验器
public class Validator {
public static void validate(Object obj) throws Exception {
Class<?> clazz = obj.getClass();
for (Field field : clazz.getDeclaredFields()) {
field.setAccessible(true);
Object value = field.get(obj);
// 处理 @NotNull
NotNull notNull = field.getAnnotation(NotNull.class);
if (notNull != null && value == null) {
throw new Exception(notNull.message());
}
// 处理 @Length
Length length = field.getAnnotation(Length.class);
if (length != null && value instanceof String) {
String str = (String) value;
if (str.length() < length.min() || str.length() > length.max()) {
throw new Exception(length.message());
}
}
}
}
}
5.2 路由映射注解(类似 Spring MVC)
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RequestMapping {
String path();
String method() default "GET";
}
public class DispatcherServlet {
public void dispatch(String uri) throws Exception {
// 模拟扫描 Controller
Class<?> clazz = UserController.class;
for (Method method : clazz.getDeclaredMethods()) {
RequestMapping mapping = method.getAnnotation(RequestMapping.class);
if (mapping != null && mapping.path().equals(uri)) {
method.setAccessible(true);
method.invoke(clazz.getDeclaredConstructor().newInstance());
}
}
}
}
第三部分:工厂模式 + 反射实现 AOP
一、传统工厂模式
// 接口
public interface Animal {
void speak();
}
// 实现类
public class Dog implements Animal {
public void speak() { System.out.println("汪汪汪"); }
}
public class Cat implements Animal {
public void speak() { System.out.println("喵喵喵"); }
}
// 传统工厂(每新增一个实现类就要修改代码)
public class AnimalFactory {
public static Animal createAnimal(String type) {
if ("Dog".equals(type)) return new Dog();
else if ("Cat".equals(type)) return new Cat();
else return null;
}
}
二、反射优化工厂(配置驱动,符合开闭原则)
// ========== 配置文件 animal.properties ==========
// com.example.Dog=dog
// com.example.Cat=cat
// ========== 反射工厂 ==========
public class ReflectAnimalFactory {
private static final Properties props = new Properties();
static {
try (InputStream is = ReflectAnimalFactory.class
.getResourceAsStream("/animal.properties")) {
props.load(is);
} catch (IOException e) {
e.printStackTrace();
}
}
public static Animal createAnimal(String type) throws Exception {
for (String className : props.stringPropertyNames()) {
if (props.getProperty(className).equals(type)) {
Class<?> clazz = Class.forName(className);
return (Animal) clazz.getDeclaredConstructor().newInstance();
}
}
return null;
}
}
// 新增实现类只需:1. 编写实现类 2. 配置文件加一行
// 无需修改工厂代码 —— 符合开闭原则
三、动态代理实现 AOP
// 切面逻辑:日志记录
public class LogAspect implements InvocationHandler {
private Object target; // 被代理的目标对象
public LogAspect(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("[日志] 开始执行: " + method.getName());
long start = System.currentTimeMillis();
Object result = method.invoke(target, args); // 执行目标方法
long end = System.currentTimeMillis();
System.out.println("[日志] 执行完成: " + method.getName()
+ ", 耗时: " + (end - start) + "ms");
return result;
}
}
// ========== AOP 代理工厂 ==========
public class AOPProxyFactory {
@SuppressWarnings("unchecked")
public static <T> T createProxy(T target, Class<?>[] interfaces) {
return (T) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
interfaces,
new LogAspect(target)
);
}
}
// ========== 使用 ==========
Animal dog = new Dog();
Animal proxyDog = AOPProxyFactory.createProxy(dog, new Class[]{Animal.class});
proxyDog.speak();
// 输出:
// [日志] 开始执行: speak
// 汪汪汪
// [日志] 执行完成: speak, 耗时: 0ms
四、注解驱动的 AOP 框架
// ========== 定义切面注解 ==========
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Loggable {
String value() default "";
}
// ========== 注解驱动的 InvocationHandler ==========
public class AnnotationAOPHandler implements InvocationHandler {
private Object target;
public AnnotationAOPHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 获取目标类中的同名方法(因为 proxy method 来自接口,可能没有注解)
Method targetMethod = target.getClass()
.getMethod(method.getName(), method.getParameterTypes());
if (targetMethod.isAnnotationPresent(Loggable.class)) {
Loggable log = targetMethod.getAnnotation(Loggable.class);
String desc = log.value().isEmpty() ? method.getName() : log.value();
System.out.println("[AOP日志] 进入: " + desc);
Object result = method.invoke(target, args);
System.out.println("[AOP日志] 退出: " + desc);
return result;
}
// 没有 @Loggable 的方法直接执行
return method.invoke(target, args);
}
}
// ========== 业务接口与实现 ==========
public interface UserService {
void login(String username);
void logout();
}
public class UserServiceImpl implements UserService {
@Loggable("用户登录")
public void login(String username) {
System.out.println(username + " 登录成功");
}
public void logout() {
System.out.println("退出登录");
}
}
// ========== 测试 ==========
public class Main {
public static void main(String[] args) {
UserService service = new UserServiceImpl();
UserService proxy = (UserService) Proxy.newProxyInstance(
service.getClass().getClassLoader(),
new Class[]{UserService.class},
new AnnotationAOPHandler(service)
);
proxy.login("admin");
// [AOP日志] 进入: 用户登录
// admin 登录成功
// [AOP日志] 退出: 用户登录
proxy.logout();
// 退出登录 (无日志输出,因为没加 @Loggable)
}
}
第四部分:反射在框架中的典型应用
一、简易 Spring IoC 容器
// ========== 依赖注入注解 ==========
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Autowired {}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Component {}
// ========== 简易 IoC 容器 ==========
public class SimpleApplicationContext {
private Map<Class<?>, Object> beanMap = new HashMap<>();
public SimpleApplicationContext(String packageName) throws Exception {
// 扫描包下所有 @Component 类(简化版)
Set<Class<?>> classes = scanPackage(packageName);
for (Class<?> clazz : classes) {
Object bean = clazz.getDeclaredConstructor().newInstance();
// 注入依赖
for (Field field : clazz.getDeclaredFields()) {
if (field.isAnnotationPresent(Autowired.class)) {
field.setAccessible(true);
Object dependency = beanMap.get(field.getType());
if (dependency != null) {
field.set(bean, dependency);
}
}
}
beanMap.put(clazz, bean);
}
}
@SuppressWarnings("unchecked")
public <T> T getBean(Class<T> clazz) {
return (T) beanMap.get(clazz);
}
private Set<Class<?>> scanPackage(String packageName) {
// 简化实现:实际可用 Reflections 库或 ClassLoader 扫描
return Collections.emptySet();
}
}
二、反射在 MyBatis 中的应用
// MyBatis 通过反射将 ResultSet 映射为 Java 对象
public static <T> T resultSetToBean(ResultSet rs, Class<T> clazz) throws Exception {
T bean = clazz.getDeclaredConstructor().newInstance();
ResultSetMetaData metaData = rs.getMetaData();
int columnCount = metaData.getColumnCount();
for (int i = 1; i <= columnCount; i++) {
String columnName = metaData.getColumnLabel(i); // 数据库列名
Object columnValue = rs.getObject(i);
// 通过反射找到对应的字段并赋值
try {
Field field = clazz.getDeclaredField(columnName);
field.setAccessible(true);
field.set(bean, columnValue);
} catch (NoSuchFieldException e) {
// 字段不存在时跳过(也可处理驼峰命名转换)
}
}
return bean;
}
第五部分:综合实战 —— 注解驱动的 ORM 工具
// ========== @Table 注解 ==========
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Table {
String value(); // 数据库表名
}
// ========== @Column 注解 ==========
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Column {
String value() default ""; // 数据库列名,默认取字段名
boolean isId() default false; // 是否主键
}
// ========== 实体类 ==========
@Table("tb_user")
public class User {
@Column(isId = true)
private Integer id;
@Column("username")
private String name;
private String password; // 没有 @Column 不参与映射
// getters/setters...
}
// ========== ORM 工具 ==========
public class SimpleORM {
// 生成 INSERT SQL
public static <T> String generateInsertSQL(T entity) throws Exception {
Class<?> clazz = entity.getClass();
if (!clazz.isAnnotationPresent(Table.class)) {
throw new RuntimeException("类未标注 @Table");
}
Table table = clazz.getAnnotation(Table.class);
StringBuilder columns = new StringBuilder();
StringBuilder values = new StringBuilder();
for (Field field : clazz.getDeclaredFields()) {
Column column = field.getAnnotation(Column.class);
if (column != null) {
field.setAccessible(true);
String colName = column.value().isEmpty() ? field.getName() : column.value();
columns.append(colName).append(", ");
values.append("'").append(field.get(entity)).append("', ");
}
}
String sql = String.format("INSERT INTO %s(%s) VALUES(%s)",
table.value(),
columns.substring(0, columns.length() - 2),
values.substring(0, values.length() - 2));
return sql;
}
}
核心总结
|
维度 |
反射 |
注解 |
|---|---|---|
|
本质 |
运行时获取类信息并操作 |
附加在代码上的元数据标记 |
|
核心 API |
|
|
|
关键操作 |
|
|
|
典型应用 |
IoC 容器、动态代理、ORM 映射 |
配置标记、AOP 切面、校验规则 |
|
结合使用 |
反射读取注解 → 根据注解执行不同逻辑 |
|
学习路径建议:
- 掌握
Class.forName()和.class两种获取方式 - 熟练使用
Field、Method、Constructor的 CRUD 操作 - 理解
setAccessible()和反射性能问题 - 能自定义运行时注解并完整解析
- 理解 JDK 动态代理
InvocationHandler+Proxy - 阅读 Spring/MyBatis 源码中反射与注解的实际应用
参考: