[Java]代理模式
【版权声明】未经博主同意,谢绝转载!(请尊重原创,博主保留追究权)
https://www.cnblogs.com/cnb-yuchen/p/18002823
出自【进步*于辰的博客】
参考笔记一,P83。
代理模式分为静态代理和动态代理,比较简单,直接上示例,一目了然。为降低大家阅读成本,所有示例的打印结果都相同:
打开事务 转账金额:100 关闭事务 1
大家可自行cp测试。
静态代理
1:面向接口、目标类与代理类实现于同一接口。
// 目标类和代理类的公共接口 interface IService { int transfer(int account); } // 目标类 class Target implements IService { public int transfer(int account) { System.out.println("转账金额:" + account); return 1; } } // 代理类 class Proxy implements IService { private Target tar; public Proxy(Target tar) { this.tar = tar; } public int transfer(int account) { System.out.println("打开事务"); int x = tar.transfer(account); System.out.println("关闭事务"); return x; } } class Test { public static void main(String[] args) { Proxy proxy = new Proxy(new Target()); int x = proxy.transfer(100); System.out.println(x); } }
2:面向继承、代理类继承于目标类。
class Target { public int transfer(int account) { System.out.println("转账金额:" + account); return 1; } } class Proxy extends Target { public int transfer(int account) { System.out.println("打开事务"); int x = super.transfer(account); System.out.println("关闭事务"); return x; } } class Test { public static void main(String[] args) { Proxy proxy = new Proxy(); int x = proxy.transfer(100); System.out.println(x); } }
动态代理
静态代理需要手动创建代理类,冗余。换个思路,反射可以根据Class信息创建实例,故可以通过反射为目标对象创建代理对象,则无需创建代理类,这就是“动态代理”。
JDK动态代理
面向接口,是JDK自带的。
interface IService { int transfer(int account); } class Target implements IService { public int transfer(int account) { System.out.println("转账金额:" + account); return 1; } } class Test { public static void main(String[] args) { Target tar = new Target(); /** * 通过 Proxy.newProxyInstance() 创建代理对象。 * 第一个参数是目标对象的类加载器,指定为哪个目标对象创建代理对象; * 第二个参数是目标对象实现的接口,指定目标对象和代理对象的公共接口; * 第三个参数是拦截器对象,指定用哪个拦截器来创建代理对象,需要实现 InvocationHandler 接口。 */ // 由于 proxyInstance 是通过反射创建,存储于JVM,匿名(文末说明),故上转为 IService IService proxyInstance = (IService) Proxy.newProxyInstance(tar.getClass().getClassLoader(), tar.getClass().getInterfaces(), new InvocationHandler() { /** * 代理(调用 transfer())时执行的方法 * @param proxy 代理对象,即 proxyInstance,暂不知如何使用 * @param method 目标对象的 Method 的 class 对象 * @param args 目标对象的 Method 的形参数组 */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("打开事务"); // invoke() 是反射中 Method 对象执行时调用的方法,故动态代理是通过反射调用目标对象被代理的方法 Object result = method.invoke(tar, args); System.out.println("关闭事务"); return result; } }); int x = proxyInstance.transfer(100); System.out.println(x); } }
可以使用Lambda表达式省略new InvocationHandler()
。
Cglib动态代理
面向继承,基于Spring。
class Target { public int transfer(int account) { System.out.println("转账金额:" + account); return 1; } } // 动态代理类(拦截器) class DynamicProxy implements MethodInterceptor { private Object tar; public DynamicProxy(Object tar) { this.tar = tar; } public Object createProxy() { Enhancer proxy = new Enhancer();// Enhancer 类是一种类生成器 proxy.setCallback(this);// 设置拦截器,即自身 proxy.setSuperclass(tar.getClass());// 设置父类,指定为哪个目标对象创建代理对象 return proxy.create();// 创建代理对象 } /** * 代理(调用 transfer())时执行的方法 * @param proxy 代理对象,即 proxyInstance,暂不知如何使用 * @param method 目标对象的 Method 的 class对象 * @param args 目标对象的 Method 的参数数组 * @param methodProxy 代理方法,即 Target.transfer(),暂不知如何使用 */ @Override public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { System.out.println("打开事务"); Object result = method.invoke(tar, args); System.out.println("关闭事务"); return result; } } class Test { public static void main(String[] args) { Target target = new Target(); Target proxyInstance = (Target) new DynamicProxy(target).createProxy(); int x = proxyInstance.transfer(100); System.out.println(x); } }
同样可以使用Lambda表达式优化,不过代理对象的创建(proxy.create()
)需要对Enhancer 类的属性进行一些设置,故进行了封装。
注意:两种动态代理都可以拦截所有方法,如:toString()
、hashcode()
。但不能拦截由final
修饰的方法,如:getClass()
。
扩展
JDK动态代理的核心是“面向接口”,实际上并不需要目标对象(公共接口的实现),也就是这样:
interface IService { int transfer(int account); } class Test { public static void main(String[] argx) { Class z1 = IService.class; IService proxyInstance = (IService) Proxy.newProxyInstance(z1.getClassLoader(), new Class[] { z1 }, (proxy, method, args) -> { System.out.println("打开事务"); System.out.println("关闭事务"); return 1; }); int x = proxyInstance.transfer(100); System.out.println(x); } }
没有目标对象,还是代理模式吗?又有什么意义?
Mybatis为Mapper接口创建代理对象使用的就是这种方式,不需要为Mapper接口创建实现类,我寻得一篇文章 → 一文搞懂Java动态代理:为什么Mybatis Mapper不需要实现类?(转发),那位博主讲述得狠详细,大家自行转站学习,我在此就不赘述了。
最后
本文中的例子是为了阐述静态代理和动态代理的实现思想、方便大家理解而简单举出的,不一定有实用性,仅是抛砖引玉。
本文完结。
· [翻译] 为什么 Tracebit 用 C# 开发
· 腾讯ima接入deepseek-r1,借用别人脑子用用成真了~
· Deepseek官网太卡,教你白嫖阿里云的Deepseek-R1满血版
· DeepSeek崛起:程序员“饭碗”被抢,还是职业进化新起点?
· RFID实践——.NET IoT程序读取高频RFID卡/标签