静态代理、JDK和Cglib动态代理、回调

静态代理、JDK和Cglib动态代理、回调

  • [1. 静态代理:手工打造的"专属替身"](#1. 静态代理:手工打造的“专属替身”)
  • [2. JDK动态代理:基于接口的"通用中介"](#2. JDK动态代理:基于接口的“通用中介”)
  • [3. CGLIB代理:通过继承的"子类替身"](#3. CGLIB代理:通过继承的“子类替身”)
  • [5. 总结](#5. 总结)

本篇后续推荐文章:事务拦截器TransactionInterceptor与@Transactional


1. 静态代理:手工打造的"专属替身"

  • 核心概念 :在 编译期 ,由程序员 手动编写并创建 一个具体的代理类。这个代理类实现了与目标对象相同的接口(或继承自目标类),并在内部持有对目标对象的引用。它通过在方法调用前后添加代码来增强功能。

  • 关键特征

    • 硬编码 :一个代理类只服务于一个(或一类)特定的目标类,是 "一对一" 或 "一对有限" 的静态关系。
    • 提前存在 :代理类的 .java.class 文件在程序运行前就已存在。
    • 直接控制:增强逻辑直接、明确地写在代理类的方法中。
  • 设计哲学"定制化" 。像为某人量身定制一套西装,完全合身但制作成本高,且无法给另一个人穿。

  • 类比明星的专属经纪人。经纪人(代理)为某位特定明星(目标)处理所有对外事务(增强),合同是长期固定的。

  • 代码示例

    java 复制代码
    // 1. 定义接口(和动态代理一样)
    public interface PayService {
    	void payment();
    }
    
    // 2. 目标类(真实对象)
    public class AliPayServiceImpl implements PayService {
    	@Override
    	public void payment() {
    		System.out.println("支付宝支付");
    	}
    }
    
    // 3. 静态代理类:手动编写,在编译前就已存在
    public class PayStaticProxy implements PayService {
    
    	// 持有一个目标对象的引用
    	private PayService payServiceImplTarget;
    
    	PayStaticProxy(PayService payServiceImplTarget) {
    		this.payServiceImplTarget = payServiceImplTarget;
    	}
    
    	@Override
    	public void payment() {
    		System.out.println("【静态代理】开始执行...");
    		// 增强逻辑(可以在目标对象的方法前和后做一些增强逻辑处理)
    		long start = System.currentTimeMillis();
    
    		// 调用目标对象的方法
    		this.payServiceImplTarget.payment();
    
    		System.out.println("【静态代理】执行完毕...");
    		System.out.println("【静态代理】耗时:" + (end - start) + "ms");
    	}
    
    }
    
    // 4. 使用示例
    public class StaticProxyDemo {
    
    	public static void main(String[] args) {
    		// 创建目标对象
    		PayService payService = new AliPayServiceImpl();
    
    		// 创建代理对象(手动new出来的!)
    		PayStaticProxy payStaticProxy = new PayStaticProxy(payService);
    
    		System.out.println("代理类名: " + payStaticProxy.getClass().getName());
    		// 输出: PayStaticProxy(一个实实在在的类)
    
    		payStaticProxy.payment();
    	}
    
    }

2. JDK动态代理:基于接口的"通用中介"

  • 核心概念 :在 运行时 ,通过Java原生的java.lang.reflect.Proxy类,动态地在内存中生成 一个代理类。该代理类实现了目标对象的一组接口,并将所有方法调用 统一转发 到一个 调用处理器InvocationHandler)的invoke方法中。

  • 关键特征

    • 运行时生成 :代理类在程序运行时由JVM动态创建,其类名通常为$Proxy0$Proxy1等。默认生成的代理类 $Proxy0.class 在内存中,我们也可以保存JDK动态代理生成的.class文件System.setProperty("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");,这个设置就可以把动态代理类保存到.class文件中。对应源码java.lang.reflect.ProxyGenerator#saveGeneratedFiles
    • 面向接口强制要求 目标对象必须至少实现一个接口。代理的是接口中定义的行为契约,而非具体类。
    • 统一回调 :所有方法的增强逻辑都集中在InvocationHandler.invoke()这一个方法中处理,是 "一对多" 的。
  • 设计哲学"契约化" 。只要你遵守我的接口契约(合同),我就可以作为你的通用中介。核心是 "回调机制"

  • 类比标准化服务的呼叫中心 。用户(调用者)拨打标准号码(调用接口方法),呼叫中心(代理)根据来电类型,将请求路由给对应的处理专员(InvocationHandler)按照标准流程(invoke方法)处理。

  • 代码示例

    java 复制代码
    // 1. 定义接口(JDK代理必须要有接口)
    public interface PayService {
    	void payment();
    }
    
    // 2. 实现类
    public class WxPayServiceImpl implements PayService {
    	@Override
    	public void payment() {
    		System.out.println("微信支付");
    	}
    }
    
    // 3. 调用处理器(实现InvocationHandler)
    public class PayHandler implements InvocationHandler {
    
    	// 目标对象
    	private Object target;
    
    	public PayHandler(Object target) {
    		this.target = target;
    	}
    
    	// 回调方法[的具体实现]:当代理对象的方法被调用时,会自动回调此方法
    	@Override
    	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    		System.out.println("【jdk动态代理】类执行开始...");
    
    		// 回调中的回调:调用原始目标方法
    		Object result = method.invoke(this.target, args);
    
    		System.out.println("【jdk动态代理】类执行完毕...");
    		return result;
    	}
    
    }
    
    // 4. 使用示例
    public class JdkProxyDemo {
    
    	public static void main(String[] args) {
    		// 默认生成的代理类 $Proxy0.class 在内存中,实现了PayService接口。
    		// 当然,我们也可以保存JDK动态代理生成的.class文件
    		/** @see java.lang.reflect.ProxyGenerator#saveGeneratedFiles */
    		System.setProperty("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");
    
    		// 1).创建目标对象
    		PayService payService = new WxPayServiceImpl();
    		// 2).创建回调处理器
    		InvocationHandler callback = new PayHandler(payService);
    		// 3).创建代理对象(核心步骤)
    		PayService proxy = (PayService) Proxy.newProxyInstance(
    				payService.getClass().getClassLoader(), // 类加载器
    				payService.getClass().getInterfaces(), // 实现的接口
    				callback // 调用处理器
    		);
    		
    		// 4).使用代理对象
    		System.out.println("代理类名: " + proxy.getClass().getName());// 输出: jdk.proxy1.$Proxy0
    		// 调用代理方法时,会自动触发 callback.invoke()
    		proxy.payment();
    	}
    
    }

3. CGLIB代理:通过继承的"子类替身"

  • 核心概念 :在 运行时 ,通过第三方库(如CGLIB)的Enhancer类,动态地生成一个目标类的子类 作为代理。这个子类重写了目标类中所有 非final 的方法,并在其中插入拦截逻辑。

  • 关键特征

    • 运行时生成 :代理类也是在运行时生成,类名通常为...$$EnhancerByCGLIB$$...默认生成的代理类 ...$$EnhancerByCGLIB$$...class 在内存中,我们也可以保存CGLIB动态代理生成的.class文件System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\cglib");,这个设置就可以把动态代理类保存到.class文件中。对应源码org.springframework.cglib.core.DebuggingClassWriter#DEBUG_LOCATION_PROPERTY
    • 继承子类不需要目标对象实现接口 。它通过生成目标类的子类来实现代理,因此不能代理final类或final方法。
    • 方法拦截 :增强逻辑通过实现MethodInterceptor接口,在intercept方法中定义,可以灵活地调用原始父类方法。
  • 设计哲学"继承化" 。通过成为目标的"儿子"来获得代表它的权利,并可以在继承的行为基础上进行修改。

  • 类比公司的"太子"或接班人 。太子(代理)继承了父亲(目标)的所有职位和能力,但在处理某些公务时,会先咨询军师(MethodInterceptor)的意见,再决定是否亲自执行或如何执行。

  • 代码示例

    java 复制代码
    // 1. 目标类(不需要接口!)
    public class ProductService {
    
    	public void saveProduct() {
    		System.out.println("保存产品");
    	}
    
    	// final方法不能被CGLIB代理
    	public final void audit() {
    		System.out.println("审计日志");
    	}
    
    	// private方法不会被代理
    	private void internal() {
    		System.out.println("内部方法");
    	}
    
    }
    
    // 2. 方法拦截器(实现MethodInterceptor)
    public class CglibTransactionInterceptor 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代理】提交事务");
    		return result;
    	}
    }
    
    // 3. 使用示例
    public class CglibProxyDemo {
    
    	public static void main(String[] args) {
    		// 1).设置CGLIB代理类输出目录
    		System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\cglib");
    
    		// 2).创建Enhancer(CGLIB的核心类)
    		Enhancer enhancer = new Enhancer();
    		// 设置父类(被代理的类)
    		enhancer.setSuperclass(ProductService.class);
    		// 设置回调(拦截器)
    		enhancer.setCallback(new CglibTransactionInterceptor());
    
    		// 3).创建代理对象
    		ProductService proxy = (ProductService) enhancer.create();
    
    		// 4).使用代理对象
    		System.out.println("代理类名: " + proxy.getClass().getName());
    		// 输出: ProductService$$EnhancerByCGLIB$$xxxxxx
    
    		proxy.saveProduct();
    		System.out.println("---------------");
    		proxy.audit();  // final方法,不会被代理增强
    	}
    
    }

5. 总结

特性 静态代理 JDK动态代理 CGLIB代理
实现时期 编译期(.java文件已存在) 运行期(程序自动生成) 运行期(程序自动生成)
实现方式 手动编写代理类 Proxy类 + InvocationHandler接口 Enhancer类 + MethodInterceptor接口
与目标关系 实现相同接口或继承类 实现相同接口 继承目标类
目标要求 接口或类 必须实现接口 不能是final类
代理类来源 程序员手动编写 Proxy.newProxyInstance() 生成 Enhancer.create() 生成
类文件 存在于源码中(.java → .class) 运行时在内存中生成($Proxy0.class 运行时在内存中生成($$EnhancerBy...
性能 最好(直接调用) 反射调用,较慢 通过FastClass机制,比JDK代理快
灵活性 低(每增一目标需写一代理) 高(一个处理器可代理多类) 高(一个拦截器可代理多类)
本质 手工编写的"包装类" 基于接口契约的动态回调 通过继承的动态子类
框架应用 简单封装、适配器模式 Spring AOP(默认对接口) Spring AOP(默认对类,或proxyTargetClass=true时)

  • 保存JDK动态代理类
    • 设置方法System.setProperty("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");
    • 对应源码 :java.lang.reflect.ProxyGenerator#saveGeneratedFiles`
  • 保存CGLIB代理类
    • 设置方法System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\cglib");
    • 对应源码 :org.springframework.cglib.core.DebuggingClassWriter#DEBUG_LOCATION_PROPERTY

  • 静态代理是最基础、最原始的代理模式实现。它与JDK动态代理、CGLIB代理的核心区别在于:静态代理是在编译期间手动创建的代理类,而动态代理是在运行时程序自动生成的代理类

  • 理解这三种代理的关键在于把握它们 创建代理对象的时机(编译时 vs 运行时)与目标对象建立关系的方式(组合 vs 接口契约 vs 继承) 。动态代理(JDK/CGLIB)的强大之处在于将 "增强逻辑" 与 "代理对象创建" 解耦,通过 回调机制 实现了高度的灵活性和通用性。

相关推荐
2501_921649492 小时前
iTick 全球外汇、股票、期货、基金实时行情 API 接口文档详解
开发语言·python·websocket·金融·restful
万粉变现经纪人2 小时前
如何解决 pip install 代理报错 SOCKS5 握手失败 ReadTimeoutError 问题
java·python·pycharm·beautifulsoup·bug·pandas·pip
你怎么知道我是队长2 小时前
python---进程
开发语言·chrome·python
C++ 老炮儿的技术栈2 小时前
时序数据库 相对于关系型数据库,有什么区别
c语言·开发语言·c++·机器人·时序数据库·visual studio
风月歌2 小时前
2025-2026计算机毕业设计选题指导,java|springboot|ssm项目成品推荐
java·python·小程序·毕业设计·php·源码
heartbeat..2 小时前
Web 状态管理核心技术详解 + JWT 双 Token (Access/Refresh Token) 自动登录
java·网络·jwt·token
Seven972 小时前
剑指offer-57、二叉树的下一个节点
java
DYS_房东的猫2 小时前
Spring Boot集成华为云OBS实现文件上传与预览功能(含安全下载)
java·spring boot
前端不太难2 小时前
RN 列表里的局部状态和全局状态边界
开发语言·前端·harmonyos