作为Java后端开发者,掌握反射与代理是理解Spring、MyBatis等框架底层原理的关键。下面我将用清晰结构+可运行示例+框架关联的方式为你系统讲解。


一、反射机制(Reflection)—— “运行时窥探与操控类”

🔍 核心概念

反射允许程序在运行时动态获取类信息、创建对象、调用方法、访问字段(包括私有成员),打破编译期限制。

📌 获取Class对象的3种方式

// 1. 通过Class.forName(会触发静态初始化)
Class<?> clazz1 = Class.forName("java.util.ArrayList");

// 2. 通过对象的getClass()
String str = "Hello";
Class<?> clazz2 = str.getClass();

// 3. 通过类字面量(最安全高效,推荐)
Class<ArrayList> clazz3 = ArrayList.class;

💡 完整实战示例:操作私有成员

// 定义测试类
class User {
    private String name;
    private int age;
    
    public User(String name) { this.name = name; } // 仅有一个构造参数
    
    private void introduce() {
        System.out.println("I'm " + name + ", " + age + " years old");
    }
}

// 反射操作
public class ReflectionDemo {
    public static void main(String[] args) throws Exception {
        Class<User> userClass = User.class;
        
        // 1. 创建对象(绕过private构造)
        Constructor<User> constructor = userClass.getDeclaredConstructor(String.class);
        constructor.setAccessible(true); // 禁用访问检查
        User user = constructor.newInstance("张三");
        
        // 2. 修改私有字段
        Field ageField = userClass.getDeclaredField("age");
        ageField.setAccessible(true);
        ageField.setInt(user, 25);
        
        // 3. 调用私有方法
        Method introMethod = userClass.getDeclaredMethod("introduce");
        introMethod.setAccessible(true);
        introMethod.invoke(user); 
        // 输出:I'm 张三, 25 years old
    }
}

✅ 应用场景

  • Spring IoC@Autowired 通过反射注入依赖
  • ORM框架:MyBatis通过反射将ResultSet映射为Java对象
  • JUnit:通过反射查找并执行@Test注解的方法
  • 序列化库:Gson/Jackson反射读取字段生成JSON

⚠️ 注意事项

问题

说明

性能开销

反射调用比直接调用慢2-3倍(现代JVM已优化,但高频场景仍需谨慎)

破坏封装

setAccessible(true)可访问私有成员,需评估安全性

异常风险

编译期无法检查,易抛NoSuchMethodException等运行时异常

泛型擦除

运行时无法直接获取泛型具体类型(需通过父类ParameterizedType技巧)


二、代理机制(Proxy)—— “为对象添加增强逻辑”

🌐 代理模式核心思想

为真实对象提供一个替身,在不修改原对象代码的前提下,增加额外功能(如日志、事务、权限校验)。

📐 两种代理实现方式对比

特性

静态代理

JDK动态代理

CGLIB动态代理

实现时机

编译期手写

运行时生成

运行时生成字节码

要求

需实现相同接口

必须有接口

可代理类(无需接口)

原理

手动编码

java.lang.reflect.Proxy

继承目标类+字节码增强

性能

高(无反射)

中(方法调用用反射)

高(FastClass机制)

限制

代码冗余

不能代理final类/方法

不能代理final类/方法

典型应用

简单场景

Spring AOP(默认)

Spring AOP(无接口时)


🧪 示例1:JDK动态代理(基于接口)

// 1. 定义接口
public interface PaymentService {
    boolean pay(String orderId, double amount);
}

// 2. 真实实现
public class AlipayService implements PaymentService {
    public boolean pay(String orderId, double amount) {
        System.out.println("Alipay processing: " + orderId + " ¥" + amount);
        return true;
    }
}

// 3. 代理处理器(核心!)
public class LogInvocationHandler implements InvocationHandler {
    private final Object target;
    
    public LogInvocationHandler(Object target) { this.target = target; }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("【前置】调用方法: " + method.getName() + ", 参数: " + Arrays.toString(args));
        
        long start = System.currentTimeMillis();
        Object result = method.invoke(target, args); // 反射调用真实方法
        long cost = System.currentTimeMillis() - start;
        
        System.out.println("【后置】方法返回: " + result + ", 耗时: " + cost + "ms");
        return result;
    }
}

// 4. 使用
public class JdkProxyDemo {
    public static void main(String[] args) {
        PaymentService realService = new AlipayService();
        PaymentService proxy = (PaymentService) Proxy.newProxyInstance(
            realService.getClass().getClassLoader(),
            realService.getClass().getInterfaces(),
            new LogInvocationHandler(realService)
        );
        
        proxy.pay("ORDER_2026", 99.9); 
        // 输出包含日志、耗时等增强逻辑
    }
}

🧪 示例2:CGLIB动态代理(无接口场景)

// 1. 无接口的类
public class UserService {
    public String getUserInfo(String userId) {
        return "User-" + userId;
    }
}

// 2. 方法拦截器
public class ServiceInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("CGLIB【拦截】: " + method.getName());
        return proxy.invokeSuper(obj, args); // 调用父类(真实对象)方法
    }
}

// 3. 使用(需添加Maven依赖:net.sf.cglib:cglib:3.3.0)
public class CglibDemo {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(UserService.class);
        enhancer.setCallback(new ServiceInterceptor());
        
        UserService proxy = (UserService) enhancer.create();
        System.out.println(proxy.getUserInfo("U1001")); 
        // 输出:CGLIB【拦截】: getUserInfo + User-U1001
    }
}

🔗 反射与代理的关系 & 框架实战

💡 核心关联

  • 动态代理的基石是反射:JDK代理中InvocationHandler.invoke()内部通过反射调用目标方法;CGLIB在生成字节码时也依赖反射获取方法信息。
  • 反射 ≠ 代理:反射是语言特性(能力),代理是设计模式(应用)。代理常借助反射实现动态性。

🌱 Spring框架中的经典应用

@Service
public class OrderService {
    @Autowired
    private PaymentService paymentService; // 1. 反射注入依赖
    
    @Transactional // 2. Spring通过动态代理(JDK/CGLIB)为该方法添加事务控制
    public void createOrder() {
        paymentService.pay(...);
    }
}
  • 依赖注入:Spring通过反射读取@Autowired注解,实例化并注入Bean
  • AOP实现
    • OrderService实现接口 → 使用JDK动态代理
    • 若未实现接口(或Spring Boot 2.0+)→ 默认使用CGLIB代理
    • 代理对象在方法执行前后插入事务、日志等逻辑

💡 关键总结:反射赋予Java“动态能力”,代理模式利用这种能力实现“关注点分离”。掌握二者,你便拿到了通往框架设计世界的大门钥匙。建议结合Spring源码调试加深理解

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐