Java (Spring Boot) 反射完整学习笔记

Java (Spring Boot) 反射完整学习笔记


目录

  1. 什么是反射(Reflection)
  2. 为什么需要反射
  3. [Java 反射核心 API](#Java 反射核心 API)
  4. 反射的高级应用
  5. [Spring Boot 中的反射应用](#Spring Boot 中的反射应用)
  6. 实战案例
  7. 性能与安全
  8. 常见误区与最佳实践
  9. 扩展阅读

1. 什么是反射(Reflection)

1.1 定义与核心概念

反射(Reflection) 是 Java 提供的一种机制,允许程序在运行时

  • 检查类的结构(类名、父类、接口、字段、方法、构造器)
  • 创建对象实例
  • 访问修改字段的值
  • 调用方法

核心思想:将类的元数据(metadata)作为数据进行操作。

1.2 Java 反射的特点

特性 说明
运行时类型识别 在运行时获取类的完整信息
动态创建对象 无需 new 关键字即可创建实例
访问私有成员 通过 setAccessible(true) 突破访问控制
框架基础 Spring、Hibernate、MyBatis 等框架的核心技术

1.3 反射 vs 正常调用

正常调用(编译时确定):

java 复制代码
User user = new User();
user.setName("Alice");
String name = user.getName();

反射调用(运行时确定):

java 复制代码
Class<?> clazz = Class.forName("com.example.User");
Object user = clazz.getDeclaredConstructor().newInstance();
Method setName = clazz.getMethod("setName", String.class);
setName.invoke(user, "Alice");
Method getName = clazz.getMethod("getName");
String name = (String) getName.invoke(user);

对比

维度 正常调用 反射调用
性能 慢(10-100 倍)
类型安全 编译期检查 运行时检查
代码可读性
灵活性

2. 为什么需要反射

2.1 动态性的价值

反射使 Java 具备了运行时的动态能力

java 复制代码
// 场景:根据配置文件决定创建哪个类的实例
String className = config.getProperty("database.driver");
Class<?> clazz = Class.forName(className);
Driver driver = (Driver) clazz.getDeclaredConstructor().newInstance();

优势

  • 无需修改代码即可切换实现
  • 支持插件化架构
  • 框架可以在不知道具体类的情况下操作对象

2.2 典型应用场景

场景 说明 示例
框架开发 IoC 容器、ORM 框架 Spring、Hibernate
动态代理 AOP 切面编程 Spring AOP、MyBatis
注解处理 运行时解析注解 @Autowired@RequestMapping
序列化/反序列化 对象 ↔ JSON/XML Jackson、Gson
工具类 对象拷贝、属性比较 BeanUtils、MapStruct
单元测试 访问私有方法 JUnit、Mockito

3. Java 反射核心 API

3.1 Class 类的获取

Class是反射的入口,代表一个类的元数据。

3.1.1 获取 Class 对象的三种方式
java 复制代码
// 方式 1: 类名.class
Class<String> clazz1 = String.class;

// 方式 2: 对象.getClass()
String str = "Hello";
Class<? extends String> clazz2 = str.getClass();

// 方式 3: Class.forName("全限定类名")
Class<?> clazz3 = Class.forName("java.lang.String");

对比

方式 使用场景 是否触发类初始化
类名.class 编译时已知类型
对象.getClass() 已有实例对象 是(对象已存在)
Class.forName() 运行时动态加载 是(默认)
3.1.2 Class 类的常用方法
java 复制代码
Class<?> clazz = User.class;

// 获取类的基本信息
String name = clazz.getName();              // 全限定类名:com.example.User
String simpleName = clazz.getSimpleName();  // 简单类名:User
Package pkg = clazz.getPackage();           // 包信息

// 获取修饰符
int modifiers = clazz.getModifiers();
boolean isPublic = Modifier.isPublic(modifiers);
boolean isAbstract = Modifier.isAbstract(modifiers);

// 获取父类和接口
Class<?> superClass = clazz.getSuperclass();
Class<?>[] interfaces = clazz.getInterfaces();

// 判断类型
boolean isInterface = clazz.isInterface();
boolean isEnum = clazz.isEnum();
boolean isAnnotation = clazz.isAnnotation();
boolean isArray = clazz.isArray();

3.2 Constructor(构造器反射)

3.2.1 获取构造器
java 复制代码
Class<?> clazz = User.class;

// 获取所有 public 构造器
Constructor<?>[] constructors = clazz.getConstructors();

// 获取所有构造器(包括 private)
Constructor<?>[] allConstructors = clazz.getDeclaredConstructors();

// 获取指定参数的 public 构造器
Constructor<?> constructor = clazz.getConstructor(String.class, int.class);

// 获取指定参数的构造器(包括 private)
Constructor<?> privateConstructor = clazz.getDeclaredConstructor(String.class);
3.2.2 创建对象实例
java 复制代码
// 方式 1: 使用无参构造器
Class<?> clazz = User.class;
Object user1 = clazz.getDeclaredConstructor().newInstance();

// 方式 2: 使用有参构造器
Constructor<?> constructor = clazz.getConstructor(String.class, int.class);
Object user2 = constructor.newInstance("Alice", 25);

// 访问私有构造器
Constructor<?> privateConstructor = clazz.getDeclaredConstructor(String.class);
privateConstructor.setAccessible(true);  // 关键:突破访问控制
Object user3 = privateConstructor.newInstance("Bob");

完整示例

java 复制代码
public class User {
    private String name;
    private int age;

    // 私有构造器
    private User(String name) {
        this.name = name;
    }

    // 公共构造器
    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{name='" + name + "', age=" + age + "}";
    }
}

// 测试
public class ReflectionTest {
    public static void main(String[] args) throws Exception {
        Class<?> clazz = User.class;

        // 调用公共构造器
        Constructor<?> publicConstructor = clazz.getConstructor(String.class, int.class);
        Object user1 = publicConstructor.newInstance("Alice", 30);
        System.out.println(user1);  // User{name='Alice', age=30}

        // 调用私有构造器
        Constructor<?> privateConstructor = clazz.getDeclaredConstructor(String.class);
        privateConstructor.setAccessible(true);
        Object user2 = privateConstructor.newInstance("Bob");
        System.out.println(user2);  // User{name='Bob', age=0}
    }
}

3.3 Field(字段反射)

3.3.1 获取字段
java 复制代码
Class<?> clazz = User.class;

// 获取所有 public 字段(包括继承的)
Field[] fields = clazz.getFields();

// 获取所有字段(包括 private,不包括继承的)
Field[] allFields = clazz.getDeclaredFields();

// 获取指定名称的 public 字段
Field nameField = clazz.getField("name");

// 获取指定名称的字段(包括 private)
Field ageField = clazz.getDeclaredField("age");
3.3.2 读取和修改字段值
java 复制代码
public class User {
    public String name;
    private int age;

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

// 测试
public class FieldTest {
    public static void main(String[] args) throws Exception {
        User user = new User("Alice", 25);

        // 操作 public 字段
        Field nameField = User.class.getField("name");
        String name = (String) nameField.get(user);
        System.out.println("原始 name: " + name);  // Alice
        nameField.set(user, "Bob");
        System.out.println("修改后 name: " + user.name);  // Bob

        // 操作 private 字段
        Field ageField = User.class.getDeclaredField("age");
        ageField.setAccessible(true);  // 关键:突破访问控制
        int age = (int) ageField.get(user);
        System.out.println("原始 age: " + age);  // 25
        ageField.set(user, 30);
        System.out.println("修改后 age: " + ageField.get(user));  // 30
    }
}
3.3.3 字段的其他信息
java 复制代码
Field field = User.class.getDeclaredField("age");

// 字段名称
String fieldName = field.getName();

// 字段类型
Class<?> fieldType = field.getType();
System.out.println(fieldType);  // int

// 泛型类型(如 List<String>)
Type genericType = field.getGenericType();

// 修饰符
int modifiers = field.getModifiers();
boolean isPrivate = Modifier.isPrivate(modifiers);
boolean isStatic = Modifier.isStatic(modifiers);
boolean isFinal = Modifier.isFinal(modifiers);

3.4 Method(方法反射)

3.4.1 获取方法
java 复制代码
Class<?> clazz = User.class;

// 获取所有 public 方法(包括继承的)
Method[] methods = clazz.getMethods();

// 获取所有方法(包括 private,不包括继承的)
Method[] allMethods = clazz.getDeclaredMethods();

// 获取指定名称和参数的 public 方法
Method setName = clazz.getMethod("setName", String.class);

// 获取指定名称和参数的方法(包括 private)
Method privateMethod = clazz.getDeclaredMethod("privateMethod");
3.4.2 调用方法
java 复制代码
public class User {
    private String name;

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    private String privateMethod() {
        return "私有方法被调用";
    }

    public static String staticMethod(String msg) {
        return "静态方法: " + msg;
    }
}

// 测试
public class MethodTest {
    public static void main(String[] args) throws Exception {
        User user = new User();

        // 调用 public 方法
        Method setName = User.class.getMethod("setName", String.class);
        setName.invoke(user, "Alice");

        Method getName = User.class.getMethod("getName");
        String name = (String) getName.invoke(user);
        System.out.println("name: " + name);  // Alice

        // 调用 private 方法
        Method privateMethod = User.class.getDeclaredMethod("privateMethod");
        privateMethod.setAccessible(true);
        String result = (String) privateMethod.invoke(user);
        System.out.println(result);  // 私有方法被调用

        // 调用 static 方法
        Method staticMethod = User.class.getMethod("staticMethod", String.class);
        String staticResult = (String) staticMethod.invoke(null, "Hello");
        System.out.println(staticResult);  // 静态方法: Hello
    }
}
3.4.3 方法的其他信息
java 复制代码
Method method = User.class.getMethod("setName", String.class);

// 方法名称
String methodName = method.getName();

// 返回类型
Class<?> returnType = method.getReturnType();

// 参数类型
Class<?>[] parameterTypes = method.getParameterTypes();

// 异常类型
Class<?>[] exceptionTypes = method.getExceptionTypes();

// 修饰符
int modifiers = method.getModifiers();
boolean isPublic = Modifier.isPublic(modifiers);
boolean isStatic = Modifier.isStatic(modifiers);

3.5 Array(数组反射)

java 复制代码
import java.lang.reflect.Array;

public class ArrayTest {
    public static void main(String[] args) {
        // 创建数组:int[10]
        Object arr = Array.newInstance(int.class, 10);

        // 设置数组元素
        Array.set(arr, 0, 100);
        Array.set(arr, 1, 200);

        // 获取数组元素
        int value = (int) Array.get(arr, 0);
        System.out.println(value);  // 100

        // 获取数组长度
        int length = Array.getLength(arr);
        System.out.println(length);  // 10

        // 创建多维数组:int[5][10]
        Object multiArr = Array.newInstance(int.class, 5, 10);
    }
}

4. 反射的高级应用

4.1 动态代理

动态代理是 Java 反射的重要应用,分为 JDK 动态代理CGLib 动态代理

4.1.1 JDK 动态代理

特点

  • 基于接口实现
  • 使用 java.lang.reflect.Proxy
  • 被代理类必须实现接口

示例

java 复制代码
// 接口
public interface UserService {
    void addUser(String name);
    void deleteUser(String name);
}

// 实现类
public class UserServiceImpl implements UserService {
    @Override
    public void addUser(String name) {
        System.out.println("添加用户: " + name);
    }

    @Override
    public void deleteUser(String name) {
        System.out.println("删除用户: " + name);
    }
}

// 代理处理器
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class LogHandler implements InvocationHandler {
    private Object target;  // 被代理对象

    public LogHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("[日志] 方法开始: " + method.getName());
        Object result = method.invoke(target, args);  // 调用真实对象的方法
        System.out.println("[日志] 方法结束: " + method.getName());
        return result;
    }
}

// 测试
import java.lang.reflect.Proxy;

public class ProxyTest {
    public static void main(String[] args) {
        UserService target = new UserServiceImpl();

        UserService proxy = (UserService) Proxy.newProxyInstance(
            target.getClass().getClassLoader(),       // 类加载器
            target.getClass().getInterfaces(),        // 接口列表
            new LogHandler(target)                    // InvocationHandler
        );

        proxy.addUser("Alice");
        // 输出:
        // [日志] 方法开始: addUser
        // 添加用户: Alice
        // [日志] 方法结束: addUser
    }
}
4.1.2 CGLib 动态代理

特点

  • 基于子类实现
  • 使用字节码技术(ASM)
  • 被代理类不需要实现接口
  • 不能代理 final 类和方法

Maven 依赖

xml 复制代码
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>

示例

java 复制代码
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;

// 目标类(无需实现接口)
public class UserDao {
    public void save(String name) {
        System.out.println("保存用户: " + name);
    }
}

// 拦截器
public class CglibInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("[CGLib] 方法开始: " + method.getName());
        Object result = proxy.invokeSuper(obj, args);  // 调用父类方法
        System.out.println("[CGLib] 方法结束: " + method.getName());
        return result;
    }
}

// 测试
public class CglibTest {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(UserDao.class);           // 设置父类
        enhancer.setCallback(new CglibInterceptor());    // 设置拦截器

        UserDao proxy = (UserDao) enhancer.create();
        proxy.save("Bob");
        // 输出:
        // [CGLib] 方法开始: save
        // 保存用户: Bob
        // [CGLib] 方法结束: save
    }
}
4.1.3 JDK Proxy vs CGLib
对比维度 JDK Proxy CGLib
实现方式 基于接口(反射) 基于子类(字节码)
要求 必须有接口 无需接口
性能 调用稍慢 调用稍快
限制 不能代理 final 类/方法
Spring 默认 有接口时使用 无接口时使用

4.2 注解处理

4.2.1 定义注解
java 复制代码
import java.lang.annotation.*;

@Target(ElementType.METHOD)           // 作用于方法
@Retention(RetentionPolicy.RUNTIME)   // 运行时可获取
public @interface Log {
    String value() default "";        // 注解参数
    String level() default "INFO";
}
4.2.2 使用注解
java 复制代码
public class UserController {
    @Log(value = "用户登录", level = "INFO")
    public void login(String username) {
        System.out.println(username + " 登录成功");
    }

    @Log(value = "用户登出", level = "WARN")
    public void logout(String username) {
        System.out.println(username + " 已登出");
    }
}
4.2.3 解析注解
java 复制代码
import java.lang.reflect.Method;

public class AnnotationParser {
    public static void main(String[] args) throws Exception {
        Class<?> clazz = UserController.class;

        for (Method method : clazz.getDeclaredMethods()) {
            if (method.isAnnotationPresent(Log.class)) {
                Log log = method.getAnnotation(Log.class);
                System.out.println("方法: " + method.getName());
                System.out.println("描述: " + log.value());
                System.out.println("级别: " + log.level());
                System.out.println("---");
            }
        }
    }
}

// 输出:
// 方法: login
// 描述: 用户登录
// 级别: INFO
// ---
// 方法: logout
// 描述: 用户登出
// 级别: WARN

4.3 泛型信息获取

Java 的泛型在编译后会被擦除,但可以通过反射获取部分泛型信息。

java 复制代码
import java.lang.reflect.*;
import java.util.*;

public class GenericTest {
    // 字段的泛型信息
    private List<String> stringList;
    private Map<String, Integer> map;

    // 方法的泛型信息
    public List<String> getList(Map<String, Integer> param) {
        return null;
    }

    public static void main(String[] args) throws Exception {
        // 获取字段的泛型类型
        Field field = GenericTest.class.getDeclaredField("stringList");
        Type genericType = field.getGenericType();
        if (genericType instanceof ParameterizedType) {
            ParameterizedType pt = (ParameterizedType) genericType;
            System.out.println("原始类型: " + pt.getRawType());  // interface java.util.List
            Type[] actualTypes = pt.getActualTypeArguments();
            System.out.println("泛型参数: " + actualTypes[0]);  // class java.lang.String
        }

        // 获取方法的泛型类型
        Method method = GenericTest.class.getMethod("getList", Map.class);
        Type returnType = method.getGenericReturnType();
        if (returnType instanceof ParameterizedType) {
            ParameterizedType pt = (ParameterizedType) returnType;
            System.out.println("返回类型泛型参数: " + pt.getActualTypeArguments()[0]);
        }
    }
}

5. Spring Boot 中的反射应用

5.1 IoC 容器与依赖注入

5.1.1 Spring Bean 的创建过程

Spring 使用反射创建和管理 Bean:

java 复制代码
// 伪代码:Spring 内部的 Bean 创建流程
public class SimpleBeanFactory {
    private Map<String, Object> singletonObjects = new ConcurrentHashMap<>();

    public Object getBean(String beanName, Class<?> beanClass) throws Exception {
        // 检查单例缓存
        if (singletonObjects.containsKey(beanName)) {
            return singletonObjects.get(beanName);
        }

        // 使用反射创建实例
        Object instance = beanClass.getDeclaredConstructor().newInstance();

        // 依赖注入(字段注入)
        for (Field field : beanClass.getDeclaredFields()) {
            if (field.isAnnotationPresent(Autowired.class)) {
                field.setAccessible(true);
                Object dependency = getBean(field.getName(), field.getType());
                field.set(instance, dependency);
            }
        }

        // 缓存单例 Bean
        singletonObjects.put(beanName, instance);
        return instance;
    }
}
5.1.2 @Autowired 的实现原理
java 复制代码
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;  // Spring 通过反射注入

    public void saveUser(User user) {
        userRepository.save(user);
    }
}

原理

  1. Spring 扫描所有类,找到带 @Service@Component 等注解的类
  2. 使用反射创建实例:clazz.getDeclaredConstructor().newInstance()
  3. 查找带 @Autowired 的字段/方法
  4. 使用反射注入依赖:field.set(instance, dependency)

5.2 AOP 切面编程

5.2.1 AOP 的底层实现

Spring AOP 基于动态代理实现:

  • 有接口:使用 JDK 动态代理
  • 无接口:使用 CGLib 动态代理

示例

java 复制代码
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class LogAspect {
    @Pointcut("execution(* com.example.service.*.*(..))")
    public void serviceLayer() {}

    @Before("serviceLayer()")
    public void beforeAdvice() {
        System.out.println("[AOP] 方法执行前");
    }

    @Around("serviceLayer()")
    public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("[AOP] 环绕通知 - 开始");
        Object result = joinPoint.proceed();  // 执行目标方法
        System.out.println("[AOP] 环绕通知 - 结束");
        return result;
    }
}

原理

  1. Spring 为带 @Service 的类生成代理对象
  2. 代理对象的 InvocationHandlerMethodInterceptor 拦截方法调用
  3. 在方法执行前后插入切面逻辑

5.3 自动配置原理

5.3.1 @SpringBootApplication 注解
java 复制代码
@SpringBootApplication
public class MyApplication {
    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }
}

等价于

java 复制代码
@SpringBootConfiguration  // 标记为配置类
@EnableAutoConfiguration  // 启用自动配置
@ComponentScan            // 组件扫描
public class MyApplication {
    // ...
}
5.3.2 自动配置的反射机制

核心流程

  1. @EnableAutoConfiguration 触发自动配置
  2. 读取 META-INF/spring.factories 文件
  3. 加载所有自动配置类
  4. 使用反射创建 Bean

spring.factories 示例

properties 复制代码
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.autoconfigure.MyAutoConfiguration

自动配置类

java 复制代码
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@ConditionalOnClass(MyService.class)  // 条件:MyService 类存在
public class MyAutoConfiguration {
    @Bean
    public MyService myService() {
        return new MyService();
    }
}

反射实现

java 复制代码
// 伪代码:Spring Boot 自动配置加载
public class AutoConfigurationLoader {
    public void loadAutoConfigurations() throws Exception {
        // 读取 spring.factories
        List<String> classNames = loadFactoryNames();

        for (String className : classNames) {
            // 使用反射加载类
            Class<?> configClass = Class.forName(className);

            // 检查条件注解
            if (configClass.isAnnotationPresent(ConditionalOnClass.class)) {
                ConditionalOnClass condition = configClass.getAnnotation(ConditionalOnClass.class);
                if (!isClassPresent(condition.value())) {
                    continue;  // 条件不满足,跳过
                }
            }

            // 创建配置类实例并注册 Bean
            Object configInstance = configClass.getDeclaredConstructor().newInstance();
            registerBeans(configInstance);
        }
    }
}

5.4 注解驱动开发

5.4.1 @RequestMapping 的实现
java 复制代码
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/users")
public class UserController {
    @GetMapping("/{id}")
    public User getUser(@PathVariable Long id) {
        return new User(id, "Alice");
    }

    @PostMapping
    public void createUser(@RequestBody User user) {
        System.out.println("创建用户: " + user);
    }
}

原理

  1. Spring 扫描所有类,找到 @RestController 注解的类
  2. 遍历类的所有方法,查找 @RequestMapping@GetMapping 等注解
  3. 解析注解的路径、HTTP 方法等信息
  4. 使用反射调用对应的方法

简化实现

java 复制代码
import java.lang.reflect.Method;
import java.util.*;

public class SimpleDispatcher {
    private Map<String, HandlerMethod> handlerMethods = new HashMap<>();

    public void registerController(Class<?> controllerClass) throws Exception {
        RequestMapping classMapping = controllerClass.getAnnotation(RequestMapping.class);
        String basePath = classMapping != null ? classMapping.value()[0] : "";

        for (Method method : controllerClass.getDeclaredMethods()) {
            if (method.isAnnotationPresent(GetMapping.class)) {
                GetMapping mapping = method.getAnnotation(GetMapping.class);
                String fullPath = basePath + mapping.value()[0];
                handlerMethods.put(fullPath, new HandlerMethod(controllerClass, method));
            }
        }
    }

    public Object handleRequest(String path, Object... args) throws Exception {
        HandlerMethod handler = handlerMethods.get(path);
        if (handler == null) {
            throw new IllegalArgumentException("No handler for path: " + path);
        }

        Object controller = handler.controllerClass.getDeclaredConstructor().newInstance();
        return handler.method.invoke(controller, args);
    }

    static class HandlerMethod {
        Class<?> controllerClass;
        Method method;

        HandlerMethod(Class<?> controllerClass, Method method) {
            this.controllerClass = controllerClass;
            this.method = method;
        }
    }
}

5.5 Bean 生命周期中的反射

Spring Bean 的完整生命周期

复制代码
1. 实例化(Instantiation)         ← 反射创建对象
   ↓
2. 属性赋值(Populate Properties)  ← 反射注入依赖
   ↓
3. 初始化前(BeanPostProcessor.before)
   ↓
4. 初始化(InitializingBean / @PostConstruct)
   ↓
5. 初始化后(BeanPostProcessor.after) ← AOP 代理创建
   ↓
6. 使用
   ↓
7. 销毁(DisposableBean / @PreDestroy)

代码示例

java 复制代码
import org.springframework.beans.factory.annotation.*;
import org.springframework.stereotype.Component;
import javax.annotation.*;

@Component
public class LifecycleBean {
    @Autowired
    private UserService userService;  // 2. 反射注入

    public LifecycleBean() {
        System.out.println("1. 构造器执行");  // 1. 反射调用
    }

    @PostConstruct
    public void init() {
        System.out.println("4. 初始化方法执行");
    }

    @PreDestroy
    public void destroy() {
        System.out.println("7. 销毁方法执行");
    }
}

6. 实战案例

6.1 自定义注解 + 反射处理

需求:实现一个日志注解,自动记录方法的执行时间。

6.1.1 定义注解
java 复制代码
import java.lang.annotation.*;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TimeLog {
    String value() default "";
}
6.1.2 AOP 切面处理
java 复制代码
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;

@Aspect
@Component
public class TimeLogAspect {
    @Around("@annotation(com.example.annotation.TimeLog)")
    public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
        // 使用反射获取方法信息
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        TimeLog timeLog = method.getAnnotation(TimeLog.class);

        long startTime = System.currentTimeMillis();
        Object result = joinPoint.proceed();  // 执行目标方法
        long endTime = System.currentTimeMillis();

        System.out.println(String.format("[%s] %s 执行时间: %dms",
                timeLog.value(), method.getName(), endTime - startTime));
        return result;
    }
}
6.1.3 使用注解
java 复制代码
import org.springframework.stereotype.Service;

@Service
public class OrderService {
    @TimeLog("订单服务")
    public void processOrder(Long orderId) throws InterruptedException {
        Thread.sleep(500);  // 模拟耗时操作
        System.out.println("处理订单: " + orderId);
    }
}

6.2 动态 Bean 注册

需求:根据配置文件动态注册不同的数据源 Bean。

java 复制代码
import org.springframework.beans.factory.support.*;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import javax.sql.DataSource;

public class DynamicBeanRegistrar {
    public static void main(String[] args) throws Exception {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();

        // 获取 Bean 定义注册器
        BeanDefinitionRegistry registry = (BeanDefinitionRegistry) context.getBeanFactory();

        // 动态创建 Bean 定义
        String dataSourceClass = "com.zaxxer.hikari.HikariDataSource";  // 从配置读取
        Class<?> clazz = Class.forName(dataSourceClass);

        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(clazz);
        builder.addPropertyValue("jdbcUrl", "jdbc:mysql://localhost:3306/test");
        builder.addPropertyValue("username", "root");
        builder.addPropertyValue("password", "123456");

        // 注册 Bean
        registry.registerBeanDefinition("dynamicDataSource", builder.getBeanDefinition());

        context.refresh();

        // 获取动态注册的 Bean
        DataSource dataSource = context.getBean("dynamicDataSource", DataSource.class);
        System.out.println("动态 Bean: " + dataSource);
    }
}

6.3 简易 ORM 框架

需求:通过反射将对象映射到 SQL。

java 复制代码
import java.lang.annotation.*;
import java.lang.reflect.Field;
import java.util.*;

// 表名注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Table {
    String value();
}

// 列名注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface Column {
    String value();
}

// 实体类
@Table("t_user")
class User {
    @Column("id")
    private Long id;

    @Column("username")
    private String username;

    @Column("email")
    private String email;

    public User(Long id, String username, String email) {
        this.id = id;
        this.username = username;
        this.email = email;
    }

    // Getters...
}

// 简易 ORM
class SimpleORM {
    public static String generateInsertSQL(Object entity) throws Exception {
        Class<?> clazz = entity.getClass();

        // 获取表名
        Table table = clazz.getAnnotation(Table.class);
        String tableName = table.value();

        // 获取字段和值
        List<String> columns = new ArrayList<>();
        List<String> values = new ArrayList<>();

        for (Field field : clazz.getDeclaredFields()) {
            if (field.isAnnotationPresent(Column.class)) {
                Column column = field.getAnnotation(Column.class);
                columns.add(column.value());

                field.setAccessible(true);
                Object value = field.get(entity);
                values.add(value instanceof String ? "'" + value + "'" : String.valueOf(value));
            }
        }

        return String.format("INSERT INTO %s (%s) VALUES (%s)",
                tableName,
                String.join(", ", columns),
                String.join(", ", values));
    }

    public static void main(String[] args) throws Exception {
        User user = new User(1L, "alice", "alice@example.com");
        String sql = generateInsertSQL(user);
        System.out.println(sql);
        // INSERT INTO t_user (id, username, email) VALUES (1, 'alice', 'alice@example.com')
    }
}

6.4 自定义 AOP 切面(无 Spring)

需求:不使用 Spring,纯手工实现 AOP。

java 复制代码
import java.lang.reflect.*;

// 切面接口
interface MethodInterceptor {
    void before(Method method, Object[] args);
    void after(Method method, Object result);
}

// 代理工厂
class ProxyFactory {
    public static <T> T createProxy(T target, MethodInterceptor interceptor) {
        return (T) Proxy.newProxyInstance(
            target.getClass().getClassLoader(),
            target.getClass().getInterfaces(),
            (proxy, method, args) -> {
                interceptor.before(method, args);
                Object result = method.invoke(target, args);
                interceptor.after(method, result);
                return result;
            }
        );
    }
}

// 测试
interface Calculator {
    int add(int a, int b);
}

class CalculatorImpl implements Calculator {
    @Override
    public int add(int a, int b) {
        return a + b;
    }
}

public class AopTest {
    public static void main(String[] args) {
        Calculator calculator = new CalculatorImpl();

        Calculator proxy = ProxyFactory.createProxy(calculator, new MethodInterceptor() {
            @Override
            public void before(Method method, Object[] args) {
                System.out.println("[AOP] 执行方法: " + method.getName());
            }

            @Override
            public void after(Method method, Object result) {
                System.out.println("[AOP] 返回结果: " + result);
            }
        });

        int result = proxy.add(10, 20);
        System.out.println("最终结果: " + result);

        // 输出:
        // [AOP] 执行方法: add
        // [AOP] 返回结果: 30
        // 最终结果: 30
    }
}

7. 性能与安全

7.1 反射的性能开销

7.1.1 性能测试
java 复制代码
public class ReflectionPerformanceTest {
    static class User {
        private String name;
        public void setName(String name) { this.name = name; }
        public String getName() { return name; }
    }

    public static void main(String[] args) throws Exception {
        User user = new User();
        Method setName = User.class.getMethod("setName", String.class);

        int iterations = 10_000_000;

        // 1. 直接调用
        long start1 = System.currentTimeMillis();
        for (int i = 0; i < iterations; i++) {
            user.setName("Alice");
        }
        long time1 = System.currentTimeMillis() - start1;

        // 2. 反射调用(未优化)
        long start2 = System.currentTimeMillis();
        for (int i = 0; i < iterations; i++) {
            setName.invoke(user, "Alice");
        }
        long time2 = System.currentTimeMillis() - start2;

        // 3. 反射调用(关闭访问检查)
        setName.setAccessible(true);
        long start3 = System.currentTimeMillis();
        for (int i = 0; i < iterations; i++) {
            setName.invoke(user, "Alice");
        }
        long time3 = System.currentTimeMillis() - start3;

        System.out.println("直接调用: " + time1 + "ms");
        System.out.println("反射调用(未优化): " + time2 + "ms (慢 " + (time2 / time1) + "x)");
        System.out.println("反射调用(setAccessible): " + time3 + "ms (慢 " + (time3 / time1) + "x)");
    }
}

// 典型输出:
// 直接调用: 5ms
// 反射调用(未优化): 150ms (慢 30x)
// 反射调用(setAccessible): 80ms (慢 16x)
7.1.2 性能优化方案
优化方案 说明 效果
缓存反射对象 缓存 MethodField 对象 避免重复查找
setAccessible(true) 关闭访问检查 提升 50% 性能
使用 MethodHandle Java 7+ 的高性能 API 接近直接调用
编译优化 JIT 编译器优化 长期运行后性能改善

缓存示例

java 复制代码
public class ReflectionCache {
    private static final Map<String, Method> methodCache = new ConcurrentHashMap<>();

    public static Method getMethod(Class<?> clazz, String methodName, Class<?>... paramTypes) 
            throws NoSuchMethodException {
        String key = clazz.getName() + "#" + methodName;
        return methodCache.computeIfAbsent(key, k -> {
            try {
                Method method = clazz.getMethod(methodName, paramTypes);
                method.setAccessible(true);
                return method;
            } catch (NoSuchMethodException e) {
                throw new RuntimeException(e);
            }
        });
    }
}

7.2 setAccessible 与安全管理器

7.2.1 访问私有成员
java 复制代码
public class SecurityTest {
    private String secret = "敏感数据";

    public static void main(String[] args) throws Exception {
        SecurityTest obj = new SecurityTest();

        Field field = SecurityTest.class.getDeclaredField("secret");
        // field.get(obj);  // IllegalAccessException

        field.setAccessible(true);  // 绕过访问控制
        String value = (String) field.get(obj);
        System.out.println("私有字段: " + value);  // 敏感数据
    }
}
7.2.2 安全管理器限制

Java 9+ 开始限制反射访问内部 API:

java 复制代码
// Java 9+ 报警告
Field field = String.class.getDeclaredField("value");
field.setAccessible(true);  // 警告: Illegal reflective access

解决方案

  • 使用 --add-opens JVM 参数
  • 使用模块系统的 opens 声明

7.3 反射的安全风险

7.3.1 反序列化攻击
java 复制代码
// 危险代码示例
public class DeserializeAttack {
    public static void main(String[] args) throws Exception {
        String maliciousClassName = "java.lang.Runtime";  // 恶意类名
        Class<?> clazz = Class.forName(maliciousClassName);
        Method exec = clazz.getMethod("exec", String.class);
        Object runtime = clazz.getMethod("getRuntime").invoke(null);
        exec.invoke(runtime, "rm -rf /");  // 危险操作!
    }
}

防御措施

  • 白名单机制:只允许加载特定的类
  • 禁用危险类 :如 RuntimeProcessBuilder
  • 使用安全框架:如 Apache Commons Collections 的安全版本

8. 常见误区与最佳实践

8.1 何时使用/不使用反射

✅ 适合使用反射的场景
场景 原因
框架开发 需要操作未知类型的对象
插件系统 动态加载第三方代码
ORM 框架 对象与数据库映射
序列化 通用的对象转换
单元测试 访问私有方法进行测试
❌ 不应使用反射的场景
场景 原因 替代方案
性能关键路径 反射慢 10-100 倍 直接调用
类型固定 增加复杂度 硬编码
简单逻辑 过度设计 if-else
安全敏感 可能被攻击 白名单 + 验证

8.2 Spring 中的最佳实践

8.2.1 依赖注入方式选择
方式 优点 缺点 推荐度
构造器注入 不可变、易测试 构造器参数多时冗长 ⭐⭐⭐⭐⭐
Setter 注入 灵活 可能为 null ⭐⭐⭐
字段注入 简洁 难以测试、耦合框架 ⭐⭐

推荐:构造器注入

java 复制代码
@Service
public class UserService {
    private final UserRepository userRepository;

    // 推荐:构造器注入
    @Autowired
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
}
8.2.2 避免循环依赖
java 复制代码
// 错误示例
@Service
class ServiceA {
    @Autowired
    private ServiceB serviceB;
}

@Service
class ServiceB {
    @Autowired
    private ServiceA serviceA;  // 循环依赖
}

解决方案

  1. 重新设计类结构
  2. 使用 @Lazy 注解
  3. 使用 Setter 注入

8.3 反射代码的可维护性

错误示例
java 复制代码
// ❌ 硬编码字符串,容易拼写错误
Method method = clazz.getMethod("getName");
正确示例
java 复制代码
// ✅ 使用常量
public interface MethodNames {
    String GET_NAME = "getName";
}

Method method = clazz.getMethod(MethodNames.GET_NAME);

9. 扩展阅读

9.1 相关主题

  • Java Agent:字节码增强技术(Instrumentation API)
  • ASM / Javassist:字节码操作框架
  • Spring 源码分析BeanFactoryApplicationContext 实现
  • 设计模式:工厂模式、代理模式、装饰器模式

9.2 进阶学习资源

官方文档
书籍推荐
  • 《深入理解 Java 虚拟机》(周志明)
  • 《Spring 源码深度解析》(郝佳)
  • 《Effective Java》(Joshua Bloch)
源码阅读
  • java.lang.reflect
  • Spring org.springframework.beans.factory
  • MyBatis org.apache.ibatis.reflection

9.3 实战项目

  • 自定义 IoC 容器:实现依赖注入功能
  • 简易 AOP 框架:基于动态代理
  • ORM 框架:对象与数据库映射
  • JSON 序列化库:通过反射实现

总结

核心要点

  1. 反射是什么:运行时操作类、对象、方法、字段的能力
  2. 核心 APIClassConstructorFieldMethod
  3. 高级应用:动态代理(JDK/CGLib)、注解处理、泛型信息
  4. Spring 应用:IoC、AOP、自动配置、注解驱动
  5. 注意事项:性能开销、安全风险、可维护性

学习检查清单

  • 理解反射的基本概念和应用场景
  • 掌握 ClassConstructorFieldMethod 的使用
  • 理解 JDK Proxy 和 CGLib 的区别
  • 了解 Spring IoC 和 AOP 的反射实现
  • 能够实现自定义注解 + 反射处理
  • 知道反射的性能和安全问题
  • 明白何时使用/不使用反射

实践建议

  1. 动手实践:实现一个简单的 IoC 容器
  2. 阅读源码 :研究 Spring AnnotationConfigApplicationContext
  3. 性能测试:对比反射与直接调用的性能差异
  4. 安全审计 :检查项目中是否存在 setAccessible 滥用

附录:快速参考表

反射 API 速查

java 复制代码
// 获取 Class 对象
Class<?> clazz = String.class;
Class<?> clazz = obj.getClass();
Class<?> clazz = Class.forName("java.lang.String");

// 创建对象
Object obj = clazz.getDeclaredConstructor().newInstance();
Object obj = constructor.newInstance(args);

// 获取字段
Field field = clazz.getDeclaredField("name");
field.setAccessible(true);
Object value = field.get(obj);
field.set(obj, newValue);

// 获取方法
Method method = clazz.getMethod("setName", String.class);
method.setAccessible(true);
Object result = method.invoke(obj, args);

// 动态代理
Object proxy = Proxy.newProxyInstance(
    classLoader, interfaces, invocationHandler
);

Spring 注解速查

java 复制代码
// Bean 定义
@Component / @Service / @Repository / @Controller

// 依赖注入
@Autowired / @Resource / @Inject

// 配置
@Configuration / @Bean / @ComponentScan

// AOP
@Aspect / @Pointcut / @Before / @After / @Around

// Web
@RestController / @RequestMapping / @GetMapping / @PostMapping

// 自动配置
@SpringBootApplication / @EnableAutoConfiguration
@ConditionalOnClass / @ConditionalOnBean
相关推荐
andwhataboutit?2 小时前
LLAMAINDEX框架
学习
小许好楠2 小时前
java开发工程师-学习方式
java·开发语言·学习
小帅学编程2 小时前
JDBC学习
数据库·学习·oracle
Halo_tjn2 小时前
基于 IO 流实现文件操作的专项实验
java·开发语言
姓蔡小朋友2 小时前
MySQL事务、InnoDB存储引擎
java·数据库·mysql
业精于勤的牙2 小时前
最长特殊序列(二)
java·开发语言·算法
其美杰布-富贵-李3 小时前
Python 反射完整学习笔记
笔记·python·学习·反射
林shir3 小时前
Java基础1.3-Java基础语法
java
charlie1145141913 小时前
快速在WSL上开发一般的C++上位机程序
开发语言·c++·笔记·学习·环境配置·工程