跳转到内容

反射和注解深度解析

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

Class, Field, Method, Constructor

@Retention, @Target, isAnnotationPresent()

关键操作

setAccessible(true) 暴力反射

getAnnotation() 获取注解实例

典型应用

IoC 容器、动态代理、ORM 映射

配置标记、AOP 切面、校验规则

结合使用

反射读取注解 → 根据注解执行不同逻辑

学习路径建议

  1. 掌握 Class.forName().class 两种获取方式
  2. 熟练使用 FieldMethodConstructor 的 CRUD 操作
  3. 理解 setAccessible() 和反射性能问题
  4. 能自定义运行时注解并完整解析
  5. 理解 JDK 动态代理 InvocationHandler + Proxy
  6. 阅读 Spring/MyBatis 源码中反射与注解的实际应用

参考: