目录

反射

反射的概念

反射的应用场景

代理

两种动态代理

JDK 动态代理和 CGLIB 动态代理的区别

动态代理在框架中的实际应用场景

静态代理和动态代理的区别


反射

反射的概念

反射机制是在运行状态中,对于任意一个类,都能够知道这个类中的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法和属性。

  1. 运行时类信息访问:反射机制允许程序在运行时获取类的完整结构信息,包括类名、包名、父类、实现的接口、构造函数、方法和字段等。

  2. 动态对象创建:可以使用反射API动态地创建对象实例,即使在编译时不知道具体的类名。这是通过Class类的newInstance()方法或Constructor对象的newInstance()方法实现的。

  3. 动态方法调用:可以在运行时动态地调用对象的方法,包括私有方法。这通过Method类的invoke()方法实现,允许你传入对象实例和参数值来执行方法。

  4. 访问和修改字段值:反射还允许程序在运行时访问和修改对象的字段值,即使是私有的。这是通过Field类的get()和set()方法完成的。

反射的应用场景

1. 依赖注入控制反转IoC

以 Spring/Spring Boot 为代表的 IoC 框架,会在启动时扫描带有特定注解(如 @Component, @Service, @Repository, @Controller)的类,利用反射实例化对象(Bean),并通过反射注入依赖(如 @Autowired、构造器注入等)。

2. 注解处理

注解本身只是个“标记”,得有人去读这个标记才知道要做什么。反射就是那个“读取器”。框架通过反射检查类、方法、字段上有没有特定的注解,然后根据注解信息执行相应的逻辑。比如,看到 @Value,就用反射读取注解内容,去配置文件找对应的值,再用反射把值设置给字段。

3. 动态代理与AOP

想在调用某个方法前后自动加点料(比如打日志、开事务、做权限检查)?AOP(面向切面编程)就是干这个的,而动态代理是实现 AOP 的常用手段。JDK 自带的动态代理(Proxy 和 InvocationHandler)就离不开反射。代理对象在内部调用真实对象的方法时,就是通过反射的 Method.invoke 来完成的。

public class DebugInvocationHandler implements InvocationHandler {
    private final Object target; // 真实对象

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

    // proxy: 代理对象, method: 被调用的方法, args: 方法参数
    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;
    }
}

4。 对象关系映射(ORM)

像 MyBatis、Hibernate 这种框架,能帮你把数据库查出来的一行行数据,自动变成一个个 Java 对象。它是怎么知道数据库字段对应哪个 Java 属性的?还是靠反射。它通过反射获取 Java 类的属性列表,然后把查询结果按名字或配置对应起来,再用反射调用 setter 或直接修改字段值。反过来,保存对象到数据库时,也是用反射读取属性值来拼 SQL。

代理

两种动态代理

动态代理是一种非常强大的设计模式,它允许我们在不修改源代码的情况下,对一个类或对象的方法进行功能增强动态代理是一种非常强大的设计模式,它允许我们在不修改源代码的情况下,对一个类或对象的方法进行功能增强。

1. JDK 动态代理

核心原理:

基于 Java 反射机制实现,由Proxy 类和InvocationHandler 接口共同完成。

  • Proxy 类:通过 newProxyInstance() 方法在运行时动态生成代理类的实例,该方法需要传入类加载器目标对象实现的接口数组InvocationHandler 实现类

  • InvocationHandler 接口:是方法调用的处理器,核心方法 invoke(Object proxy, Method method, Object[] args) 会在代理对象调用方法时被触发,在这个方法里可以实现方法增强逻辑目标方法的调用

实现条件:

目标对象必须实现至少一个接口,否则 JDK 动态代理无法生成代理类。

优点:

  基于 JDK 原生 API 实现,无需依赖第三方 Jar 包。

  生成的代理类是目标接口的实现类,符合面向接口编程思想。

缺点:

  局限性强,只能代理实现了接口的类。

  基于反射机制,在高频调用场景下性能略低。

/**
 * 目标接口:JDK动态代理的前提是目标类必须实现接口
 * 定义核心业务方法规范,代理类会实现该接口
 */
public interface UserService {
    // 核心业务方法:添加用户
    void addUser();
}

/**
 * 目标类(被代理类):真正执行业务逻辑的类
 * 实现UserService接口,包含核心的业务代码,无任何增强逻辑
 */
public class UserServiceImpl implements UserService {
    /**
     * 原始业务方法:仅包含添加用户的核心逻辑
     */
    @Override
    public void addUser() {
        // 核心业务逻辑:实际开发中可能是操作数据库添加用户
        System.out.println("执行添加用户逻辑");
    }
}

/**
 * 自定义InvocationHandler:JDK动态代理的核心处理器
 * 实现InvocationHandler接口,用于拦截代理对象的方法调用,添加增强逻辑
 */
public class MyInvocationHandler implements InvocationHandler {
    // 被代理的目标对象(用Object类型适配任意类,提高通用性)
    private final Object target;

    /**
     * 构造方法:绑定目标对象,让处理器知道要增强哪个对象
     * @param target 被代理的目标对象(如UserServiceImpl实例)
     */
    public MyInvocationHandler(Object target) {
        this.target = target;
    }

    /**
     * 核心方法:代理对象调用任何方法时,都会触发此方法执行
     * 相当于方法拦截器,在此实现前置/后置增强逻辑 + 目标方法调用
     * @param proxy 生成的代理对象本身(一般无需使用,避免循环调用)
     * @param method 被调用的目标方法(如UserService的addUser方法)
     * @param args 目标方法的入参
     * @return 目标方法的返回值(当前addUser无返回值,返回null)
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 前置增强逻辑:方法执行前的操作(如日志记录、权限校验、事务开启等)
        System.out.println("方法执行前:记录日志 - " + method.getName() + "方法开始执行");
        
        // 核心:通过反射调用目标对象的原始方法,执行真正的业务逻辑
        // method.invoke(目标对象, 方法参数):触发target的对应方法执行
        Object result = method.invoke(target, args);
        
        // 后置增强逻辑:方法执行后的操作(如日志记录、事务提交、数据缓存等)
        System.out.println("方法执行后:记录日志 - " + method.getName() + "方法执行完成");
        
        // 返回目标方法的执行结果(无返回值则返回null)
        return result;
    }
}

/**
 * 测试类:演示JDK动态代理的完整使用流程
 * 核心是通过Proxy.newProxyInstance()生成代理对象,并测试增强效果
 */
public class JdkProxyTest {
    public static void main(String[] args) {     以下这部分逻辑一般是由Spring框架替我们自动完成的
        // 1. 创建真实的目标对象(被增强的业务对象)
        UserService target = new UserServiceImpl();
        
        // 2. 生成代理对象:JDK动态代理的核心API - Proxy.newProxyInstance()
        // 该方法会在运行时动态生成一个实现了目标接口的代理类实例
        UserService proxy = (UserService) Proxy.newProxyInstance(
            // 参数1:类加载器 - 与目标对象使用相同的类加载器,保证类加载机制一致
            target.getClass().getClassLoader(),
            // 参数2:目标对象实现的接口数组 - JDK代理需要知道代理哪些接口的方法
            // 生成的代理类会实现这些接口,因此可以强转为UserService类型
            target.getClass().getInterfaces(),
            // 参数3:方法调用处理器 - 绑定自定义的增强逻辑处理器
            new MyInvocationHandler(target)
        );
        
        // 3. 调用代理对象的方法(而非直接调用目标对象)
        // 此时不会直接执行target的addUser(),而是先触发MyInvocationHandler的invoke()方法
        proxy.addUser();
    }
}

2.CGLIB 动态代理

核心原理

基于 ASM 字节码操作框架实现,通过在运行时动态生成目标类的子类来创建代理对象,无需目标类实现接口。核心依赖Enhancer 类和MethodInterceptor 接口:

  • Enhancer 类:是 CGLIB 的核心增强器,通过设置父类(目标类)回调函数(MethodInterceptor 实现类),调用 create() 方法生成代理类实例。

  • MethodInterceptor 接口:类似 JDK 动态代理的 InvocationHandler,核心方法 intercept(Object obj, Method method, Object[] args, MethodProxy proxy) 用于处理方法调用,实现增强逻辑。

实现条件:

需要引入 CGLIB 的依赖包(Spring 等框架已内置)。

目标类不能是 final 修饰的类,否则无法生成子类;目标方法不能是 final 修饰的方法,否则无法被重写增强。

优点:

无需目标类实现接口,适用范围更广。

基于字节码生成,性能优于 JDK 动态代理(反射的性能损耗更低)。

缺点:

依赖第三方 Jar 包(CGLIB 和 ASM)。

由于是继承目标类生成代理,无法代理 final 类和 final 方法。

/**
 * 目标类(被代理类):无需实现任何接口
 * CGLIB动态代理的核心是生成目标类的子类,因此不需要接口约束
 * 注意:该类不能被final修饰(否则无法生成子类),方法也不能被final修饰(否则无法重写增强)
 */
public class OrderService {
    /**
     * 核心业务方法:创建订单
     * 无接口依赖,是纯业务类,体现CGLIB代理“无接口也能代理”的优势
     */
    public void createOrder() {
        // 核心业务逻辑:实际开发中可能是操作数据库创建订单
        System.out.println("执行创建订单逻辑");
    }
}

/**
 * 自定义MethodInterceptor:CGLIB动态代理的核心拦截器
 * 替代JDK代理的InvocationHandler,用于拦截代理对象的方法调用,添加增强逻辑
 */
public class MyMethodInterceptor implements MethodInterceptor {
    /**
     * 核心方法:代理对象调用任何方法时,都会触发此方法执行
     * 在此实现前置/后置增强逻辑 + 目标方法调用(字节码层面,非反射)
     * @param obj 生成的代理对象(目标类的子类实例)
     * @param method 被调用的目标方法(如OrderService的createOrder方法)
     * @param args 目标方法的入参(当前createOrder无参,args为null)
     * @param proxy MethodProxy对象:CGLIB生成的方法代理,用于调用目标类的原始方法
     * @return 目标方法的返回值(当前createOrder无返回值,返回null)
     */
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        // 前置增强逻辑:方法执行前的操作(如权限校验、日志、事务开启等)
        System.out.println("方法执行前:权限校验通过");
        
        // 核心:调用目标类的原始方法(推荐用proxy.invokeSuper,而非反射)
        // proxy.invokeSuper(obj, args):通过字节码直接调用父类(目标类)的方法,性能远高于反射
        // 注意:不能用method.invoke(obj, args),否则会递归调用intercept方法(因为obj是代理对象)
        Object result = proxy.invokeSuper(obj, args);
        
        // 后置增强逻辑:方法执行后的操作(如事务提交、日志、缓存更新等)
        System.out.println("方法执行后:事务提交成功");
        
        // 返回目标方法的执行结果(无返回值则返回null)
        return result;
    }
}

/**
 * 测试类:演示CGLIB动态代理的完整使用流程
 * 核心是通过Enhancer(CGLIB增强器)生成目标类的子类(代理对象),并测试增强效果
 */
public class CglibProxyTest {
    public static void main(String[] args) {           以下这部分逻辑一般是由Spring框架替我们自动完成的
        // 1. 创建CGLIB核心增强器:用于生成目标类的子类(代理类)
        Enhancer enhancer = new Enhancer();
        
        // 2. 设置增强器的父类:指定要代理的目标类(CGLIB通过继承实现代理)
        enhancer.setSuperclass(OrderService.class);  指定目标类
        
        // 3. 设置回调函数:绑定自定义的方法拦截器(增强逻辑)
        // 代理对象的方法调用会被MyMethodInterceptor的intercept方法拦截
        enhancer.setCallback(new MyMethodInterceptor());   绑定增强的方法
        
        // 4. 生成代理对象:Enhancer.create()会在运行时动态生成目标类的子类实例
        // 该实例就是代理对象,包含增强逻辑 + 目标类的原始方法
        OrderService proxy = (OrderService) enhancer.create();  生成子类的实例
        
        // 5. 调用代理对象的方法(而非直接调用目标对象)
        // 此时不会直接执行OrderService的createOrder(),而是先触发intercept方法
        proxy.createOrder();
    }
}

JDK 动态代理和 CGLIB 动态代理的区别

  1. JDK 动态代理是官方的,它要求被代理的类必须实现接口。它的原理是动态生成一个接口的实现类来作为代理。CGLIB 是第三方的,它不需要接口。它的原理是动态生成一个被代理类的子类来作为代理。但也正因为是继承,所以它不能代理 final 的类,被代理的方法也不能是 finalprivate

  2. 就二者的效率来说,大部分情况都是 JDK 动态代理更优秀,随着 JDK 版本的升级,这个优势更加明显。

动态代理在框架中的实际应用场景

动态代理最典型的应用场景就是Spring AOP。

AOP(Aspect-Oriented Programming:面向切面编程)能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性。

Spring AOP 就是基于动态代理的,如果要代理的对象,实现了某个接口,那么 Spring AOP 会使用 JDK Proxy,去创建代理对象,而对于没有实现接口的对象,就无法使用 JDK Proxy 去进行代理了,这时候 Spring AOP 会使用 Cglib 生成一个被代理对象的子类来作为代理,如下图所示:

静态代理和动态代理的区别

静态代理和动态代理的核心差异在于 代理关系的确定时机、实现灵活性及维护成本 。

对比维度 静态代理 (Static Proxy) 动态代理 (Dynamic Proxy)
代理关系确定时机 编译期(编译后生成固定的 .class 字节码文件) 运行时(动态生成代理类字节码并加载到 JVM)
实现方式 手动编写代理类,需与目标类实现同一接口,一对一绑定 无需手动编写代理类,通过 Handler/Interceptor 封装增强逻辑,一对多复用
接口依赖 必须实现接口(代理类与目标类遵循同一接口规范) 支持代理接口或直接代理实现类
代码量与维护性 代码量大(目标类越多,代理类越多),维护成本高;接口新增方法时,目标类与代理类需同步修改 代码量极少(通用增强逻辑可复用),维护性好;与接口解耦,接口变更不影响代理逻辑
核心优势 实现简单、逻辑直观,无额外框架依赖 灵活性强、复用性高,降低重复编码,适配复杂场景
典型应用场景 简单的装饰器模式、少量固定类的增强需求 Spring AOP、RPC 框架(如 Dubbo)、ORM 框架

上述内容也同步在我的飞书,欢迎访问

https://my.feishu.cn/wiki/QLauws6lWif1pnkhB8IcAvkhncc?from=from_copylink

如果我的内容对你有帮助,请点赞,评论,收藏。创作不易,你们的支持就是我坚持下去的动力!

Logo

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

更多推荐