代理模式(Proxy Pattern):为其余对象提供一种代理以控制
对这个对象的访问。简而言之,既能使被代理对象无入侵
,又能附加代理本身的操做,使方法加强功能
。java
水果店代理销售海南芝麻蕉,此外还销售苹果、橘子等其余水果。
git
代理的主要实现技术与方法以下图所示,本篇主要讲静态代理与动态代理的主要实现方式,原理部分的深刻,以及ASM字节码技术,将放到后续篇幅讲解。
程序员
下面分多个版本,经过逐步演进的方式,讲解代理模式,其中版本1到版本6为静态代理的逐步演进过程,版本7-9为JDK动态代理内容,AspjectJ静态代理与Cglib动态代理单独演示。github
假设水果店有待销苹果,并参与秒杀活动,定义Apple
类,待销水果接口Sellable
,接口秒杀方法secKill()
,代码以下:spring
package com.wzj.proxy.v1; /** * @Author: wzj * @Date: 2020/8/3 10:29 * @Desc: 苹果 */ public class Apple implements Sellalbe { @Override public void secKill() { System.out.println("苹果正在秒杀中..."); try { Thread.sleep(new Random().nextInt(3000)); } catch (InterruptedException e) { e.printStackTrace(); } } }
package com.wzj.proxy.v1; /** * @Author: wzj * @Date: 2020/8/3 10:56 * @Desc: 待销水果 */ public interface Sellalbe { /** * 秒杀 */ public void secKill(); }
现有个问题1,如需记录秒杀时间,该如何修改代码呢?express
针对问题1,直接修改秒杀方法app
package com.wzj.proxy.v2; import java.util.Random; /** * @Author: wzj * @Date: 2020/8/3 10:29 * @Desc: 待销苹果 * 问题1:记录苹果秒杀的具体时间 * 最简单的作法就是修改代码 */ public class Apple implements Sellalbe { @Override public void secKill() { Long start = System.currentTimeMillis(); System.out.println("苹果正在秒杀中..."); try { Thread.sleep(new Random().nextInt(3000)); } catch (InterruptedException e) { e.printStackTrace(); } Long end = System.currentTimeMillis(); System.out.println("记录秒杀时间为:" + (end - start)); } }
测试代码以下框架
package com.wzj.proxy.v2; /** * @Author: wzj * @Date: 2020/8/3 15:13 * @Desc: */ public class Test2 { public static void main(String[] args) { new Apple().secKill(); } }
结果:dom
苹果正在秒杀中... 记录秒杀时间为:2944
如今赶上问题2,若是没法改变方法源码呢,对源代码无入侵,该如何作呢?maven
针对问题2,很容易想到,用继承
解决。新增一个类Apple2
,从Apple
继承。
package com.wzj.proxy.v3; /** * @Author: wzj * @Date: 2020/8/3 15:25 * @Desc: 继承实现 */ public class Apple2 extends Apple { @Override public void secKill() { long start = System.currentTimeMillis(); super.secKill(); long end = System.currentTimeMillis(); System.out.println("记录秒杀时间为:" + (end - start)); } }
测试类:
package com.wzj.proxy.v3; /** * @Author: wzj * @Date: 2020/8/3 15:13 * @Desc: */ public class Test3 { public static void main(String[] args) { new Apple2().secKill(); } }
结果:
苹果正在秒杀中... 记录秒杀时间为:1660
需求老是变化不断,问题3又来了,若是须要记录秒杀开始前与开始后的日志,该如何修改呢?可能想到设计一个日志类Apple3
,从Apple
类继承;
若是需求变化了,先要记录日志,再记录秒杀时间,是否是要继续设计一个先日志后时间的类Apple4
,从Apple3
继承?
若是需求又变了,先记录秒杀时间,再记录秒杀先后日志,是否是又得从新设计Apple5
,从Apple4
继承?
若是需求再次变化,须要先记录权限,而后记录秒杀时间,最后秒杀日志,发现须要一直不停的设计新的类,并继承于各类派生出来的类,容易产生类爆炸
,且难以复用,不灵活,该如何解决呢?
出现问题3的根源是,继承破坏封装
,集合框架的创始人Joshua Bloch,在其著做《effective java》一书时,指出复合优于继承
这一原则,很好的解决了继承带来的脆弱性。针对问题3,设计一个类记录秒杀时间的代理类AppleTimeProxy
:
package com.wzj.proxy.v4; /** * @Author: wzj * @Date: 2020/8/3 15:46 * @Desc: 记录时间的代理 * 组合优于继承 */ public class AppleTimeProxy implements Sellalbe{ Apple apple; public AppleTimeProxy(Apple apple) { this.apple = apple; } @Override public void secKill() { long start = System.currentTimeMillis(); apple.secKill(); long end = System.currentTimeMillis(); System.out.println("记录秒杀时间为:" + (end - start)); } }
针对问题3,若是要代理各类类型,好比代理记录秒杀时间
,代理记录日志
,每一个类只须要单一代理各自的功能,只须要增长代码以下:
package com.wzj.proxy.v5; /** * @Author: wzj * @Date: 2020/8/3 21:39 * @Desc: 记录日志的代理类 */ public class AppleLogProxy implements Sellalbe { Apple apple; public AppleLogProxy(Apple apple) { this.apple = apple; } @Override public void secKill() { System.out.println("秒杀开始..."); apple.secKill(); System.out.println("秒杀结束..."); } }
package com.wzj.proxy.v5; /** * @Author: wzj * @Date: 2020/8/3 15:46 * @Desc: 记录时间的代理 * 组合优于继承 */ public class AppleTimeProxy implements Sellalbe { Apple apple; public AppleTimeProxy(Apple apple) { this.apple = apple; } @Override public void secKill() { long start = System.currentTimeMillis(); apple.secKill(); long end = System.currentTimeMillis(); System.out.println("记录秒杀时间为:" + (end - start)); } }
进一步优化上述代码,将组合进来的类型Apple
,改形成接口sellable
。
package com.wzj.proxy.v6; /** * @Author: wzj * @Date: 2020/8/3 21:39 * @Desc: 记录日志的代理类 */ public class AppleLogProxy implements Sellalbe { Sellalbe sellalbe; public AppleLogProxy(Sellalbe sellalbe) { this.sellalbe = sellalbe; } @Override public void secKill() { System.out.println("秒杀开始..."); sellalbe.secKill(); System.out.println("秒杀结束..."); } }
package com.wzj.proxy.v6; /** * @Author: wzj * @Date: 2020/8/3 15:46 * @Desc: 记录时间的代理 * 组合优于继承 */ public class AppleTimeProxy implements Sellalbe { Sellalbe sellalbe; public AppleTimeProxy(Sellalbe sellalbe) { this.sellalbe = sellalbe; } @Override public void secKill() { long start = System.currentTimeMillis(); sellalbe.secKill(); long end = System.currentTimeMillis(); System.out.println("记录秒杀时间为:" + (end - start)); } }
这样作能够达到,在不增长类的状况下,能够实现自由嵌套
,先记录时间,后打印日志
,或者先打印日志,后记录时间
,能够在客户端发起,不用修改源代码。测试代码以下:
package com.wzj.proxy.v6; /** * @Author: wzj * @Date: 2020/8/3 21:44 * @Desc: 如何实现代理的各类组合?继承?Decorator? * 代理的对象改为Sellable类型-愈来愈像decorator了,为了实现嵌套 */ public class Test6 { public static void main(String[] args) { //先记录时间,后打印日志 new AppleLogProxy(new AppleTimeProxy(new Apple())).secKill(); System.out.println("==========================="); //先打印日志,后记录时间 new AppleTimeProxy(new AppleLogProxy(new Apple())).secKill(); } }
结果以下:
秒杀开始... 苹果正在秒杀中... 记录秒杀时间为:2047 秒杀结束... =========================== 秒杀开始... 苹果正在秒杀中... 秒杀结束... 记录秒杀时间为:2099
针对问题3的需求,若是要增长记录权限的功能,或者是权限与记录时间的功能组合,或者权限与记录日志的功能组合,或者权限、时间、日志三个功能按照任意顺序的组合,都不须要像v3版本那样,每一个需求的变动,都须要设计新的类,各种之间冗余度高,且臃肿。直接设计一个单独权限的代理类,在客户端作各类嵌套调用
便可。
第一种方式为配置实现,在maven中加入Aspject相关依赖
<dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.4</version> </dependency>
目标对象
package com.wzj.spring.v1; import com.wzj.proxy.v9.Sellalbe; import java.util.Random; /** * @Author: wzj * @Date: 2020/8/3 10:29 * @Desc: 待销苹果 */ public class Apple { public void secKill() { System.out.println("苹果正在秒杀中..."); try { Thread.sleep(new Random().nextInt(3000)); } catch (InterruptedException e) { e.printStackTrace(); } } }
切面类
package com.wzj.spring.v1; import com.wzj.proxy.v6.Sellalbe; import java.lang.reflect.Method; /** * @Author: wzj * @Date: 2020/8/3 15:46 * @Desc: 记录时间的代理 */ public class AppleTimeProxy { private void after() { System.out.println("方法执行结束时间:" + System.currentTimeMillis()); } private void before() { System.out.println("方法执行开始时间:" + System.currentTimeMillis()); } }
app.xml
配置文件
<bean id="tank" class="com.mashibing.dp.spring.v1.Tank"/> <bean id="timeProxy" class="com.mashibing.dp.spring.v1.TimeProxy"/> <aop:config> <aop:aspect id="time" ref="timeProxy"> <aop:pointcut id="onmove" expression="execution(void com.mashibing.dp.spring.v1.Tank.move())"/> <aop:before method="before" pointcut-ref="onmove"/> <aop:after method="after" pointcut-ref="onmove"/> </aop:aspect> </aop:config>
测试类
package com.wzj.spring.v1; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * @Author: wzj * @Date: 2020/8/5 15:09 * @Desc: */ public class TestAOP { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("app.xml"); Apple apple = (Apple)context.getBean("apple"); apple.secKill(); } }
第二种方式为注解实现,切面类以下
package com.wzj.spring.v2; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; /** * @Author: wzj * @Date: 2020/8/3 15:46 * @Desc: 记录时间的代理 */ @Aspect public class AppleTimeProxy { @Before("execution (void com.wzj.spring.v2.Apple.secKill())") private void after() { System.out.println("方法执行结束时间:" + System.currentTimeMillis()); } @After("execution (void com.wzj.spring.v2.Apple.secKill())") private void before() { System.out.println("方法执行开始时间:" + System.currentTimeMillis()); } }
配置类app-auto.xml
<aop:aspectj-autoproxy/> <bean id="apple" class="com.wzj.spring.v2.Apple"/> <bean id="timeProxy" class="com.wzj.spring.v2.AppleTimeProxy"/>
测试类
package com.wzj.spring.v2; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * @Author: wzj * @Date: 2020/8/5 15:09 * @Desc: */ public class TestAOP { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("app-auto.xml"); Apple apple = (Apple)context.getBean("apple"); apple.secKill(); } }
结果
方法执行开始时间:1596979946478 苹果正在秒杀中... 方法执行结束时间:1596979948708
以上版本都是基于静态代理
的实现,代理类由程序员编写源码,再编译成字节码文件,在编译期间
生成,静态代理类图
缺点
一种类型
的对象,好比上面的水果店例子,只能代理Sellable
类型的对象,若是该水果店除了卖水果,门口还有小孩玩的投币玩具车,新增一个Player
接口,那么须要新增代理类;若是一个系统有100多个类须要经过代理来加强功能,程序规模庞大时没法胜任;代理对象在运行期间
动态生成,能够代理任何对象
的任何方法
。
优势
JDK
动态代理的InvocationHandler
的invoke()
方法)。实现步骤以下:
InvocationHandlet
接口,并实现invoke()
方法建立本身的调用处理器;Proxy.newProxyInstance
方法建立代理对象,该方法有三个参数,分别为被代理类的类加载器、被代理类接口、以及InvocationHandlet
的实现类;package com.wzj.proxy.v7; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; /** * @Author: wzj * @Date: 2020/8/4 8:45 * @Desc: 经过实现InvocationHandlet接口建立本身的调用处理器 */ public class LogHandler implements InvocationHandler { Apple apple; public LogHandler(Apple apple) { this.apple = apple; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("方法执行日志开始,方法名为:" + method.getName()); Object o = method.invoke(apple, args); System.out.println("方法执行日志结束,方法名为:" + method.getName()); return o; } }
package com.wzj.proxy.v7; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; /** * @Author: wzj * @Date: 2020/8/4 8:55 * @Desc: JDK实现动态代理 */ public class ProxyGenerator { public Object createProxy(Apple apple, InvocationHandler handler) { Object o = Proxy.newProxyInstance(apple.getClass().getClassLoader(), apple.getClass().getInterfaces(), handler); return o; } }
测试类
package com.wzj.proxy.v7; import com.wzj.proxy.v6.AppleLogProxy; import com.wzj.proxy.v6.AppleTimeProxy; import java.lang.reflect.InvocationHandler; /** * @Author: wzj * @Date: 2020/8/3 21:44 * @Desc: 若是想让LogProxy能够重用,不只能够代理Apple,还能够代理任何其余能够代理的类型 Object * (毕竟日志记录,时间计算是不少方法都须要的东西),这时该怎么作呢? * 分离代理行为与被代理对象 * 使用jdk的动态代理 */ public class Test7 { public static void main(String[] args) { Apple apple = new Apple(); InvocationHandler handler = new LogHandler(apple); Sellalbe sellalbe = (Sellalbe) new ProxyGenerator().createProxy(apple, handler); sellalbe.secKill(); } }
结果:
方法执行日志开始,方法名为:secKill 苹果正在秒杀中... 方法执行日志结束,方法名为:secKill
针对版本7,若是如今有个橘子类Orange
,实现了打折接口Discount
,也须要在打折接口先后打印日志,如何使用动态代理呢,毕竟打印日志是通用功能,
该如何修改代码呢?
版本8,经过泛型实现,代码以下
package com.wzj.proxy.v8; /** * @Author: wzj * @Date: 2020/8/4 17:45 * @Desc: 待销橘子 */ public class Orange implements Discount { /** * 打折优惠 */ @Override public int calculateBySourcePrice(int price) { int i= 9; System.out.println("橘子打折优惠, 一概9元"); return i; } }
package com.wzj.proxy.v8; /** * @Author: wzj * @Date: 2020/8/5 20:56 * @Desc: 折扣优惠接口 */ public interface Discount { public int calculateBySourcePrice(int price); }
package com.wzj.proxy.v8; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; /** * @Author: wzj * @Date: 2020/8/4 8:45 * @Desc: 使用泛型,代理任何对象的任何行为 */ public class LogHandler<T> implements InvocationHandler { T t; public LogHandler(T t) { this.t = t; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { before(method); Object o = method.invoke(t, args); after(method); return o; } private void after(Method method) { System.out.println("方法执行日志结束,方法名为:" + method.getName()); } private void before(Method method) { System.out.println("方法执行日志开始,方法名为:" + method.getName()); } }
package com.wzj.proxy.v8; import com.wzj.proxy.v8.LogHandler; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; /** * @Author: wzj * @Date: 2020/8/4 8:55 * @Desc: 使用泛型,代理任何类的任何接口 */ public class ProxyGenerator<T> { public Object createProxy(T t, InvocationHandler handler) { Object o = Proxy.newProxyInstance(t.getClass().getClassLoader(), t.getClass().getInterfaces(), handler); return o; } }
测试类:
package com.wzj.proxy.v8; import java.lang.reflect.InvocationHandler; /** * @Author: wzj * @Date: 2020/8/3 21:44 * @Desc: 若是想让LogProxy能够重用,不只能够代理Tank,还能够代理任何其余能够代理的类型 Object * (毕竟日志记录,时间计算是不少方法都须要的东西),这时该怎么作呢? * 分离代理行为与被代理对象 * 使用jdk的动态代理 * * 增长泛型,既能够代理苹果秒杀的接口,也能够代理橘子打折接口,实现任何对象任何接口的代理 */ public class Test8 { public static void main(String[] args) { Apple apple = new Apple(); InvocationHandler appHandler = new LogHandler(apple); Sellalbe sellalbe = (Sellalbe) new ProxyGenerator().createProxy(apple, appHandler); sellalbe.secKill(); System.out.println("================================="); Orange orange = new Orange(); InvocationHandler orgHandler = new LogHandler(orange); Discount discount = (Discount) new ProxyGenerator().createProxy(orange, orgHandler); discount.calculateBySourcePrice(10); } }
结果:
方法执行日志开始,方法名为:secKill 苹果正在秒杀中... 方法执行日志结束,方法名为:secKill ================================= 方法执行日志开始,方法名为:calculateBySourcePrice 橘子打折优惠, 一概9元 方法执行日志结束,方法名为:calculateBySourcePrice
有人会有疑问,invoke()
方法并无显示调用,为什么会执行呢?下面的版本9将代理类生成出来就会揭晓答案。
版本9
package com.wzj.proxy.v9; /** * @Author: wzj * @Date: 2020/8/4 21:23 * @Desc: 打印生成的代理 */ public class Test9 { public static void main(String[] args) { Apple apple = new Apple(); //在项目目录下生成代理类 System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true"); LogHandler handler = new LogHandler(apple); Sellalbe sellalbe = (Sellalbe) new ProxyGenerator().createProxy(apple, handler); sellalbe.secKill(); } }
代理类如图:
发现生成了一个$Proxy0
的类,
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) // package com.sun.proxy; import com.wzj.proxy.v9.Sellalbe; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.lang.reflect.UndeclaredThrowableException; public final class $Proxy0 extends Proxy implements Sellalbe { private static Method m1; private static Method m3; private static Method m2; private static Method m0; public $Proxy0(InvocationHandler var1) throws { super(var1); } public final boolean equals(Object var1) throws { try { return (Boolean)super.h.invoke(this, m1, new Object[]{var1}); } catch (RuntimeException | Error var3) { throw var3; } catch (Throwable var4) { throw new UndeclaredThrowableException(var4); } } public final void secKill() throws { try { super.h.invoke(this, m3, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final String toString() throws { try { return (String)super.h.invoke(this, m2, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final int hashCode() throws { try { return (Integer)super.h.invoke(this, m0, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object")); m3 = Class.forName("com.wzj.proxy.v9.Sellalbe").getMethod("secKill"); m2 = Class.forName("java.lang.Object").getMethod("toString"); m0 = Class.forName("java.lang.Object").getMethod("hashCode"); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); } } }
进入该类,在调用secKill()
方法里面有这么一段代码super.h.invoke(this, m3, (Object[])null)
,显然是在代理类调用secKill()
方法时,里面调用了自定义的handler
的invoke()
方法。下图来经过调试验证一下
显然h
是自定义的LogHandler
。
图解以下:
代码实现:
package com.wzj.cglib; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; /** * @Author: wzj * @Date: 2020/8/7 11:09 * @Desc: */ public class LogMethodInterceptor implements MethodInterceptor { @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { //代理类的父类信息 System.out.println("代理类的父类信息:" + o.getClass().getSuperclass().getName()); //前置加强 before(method); Object result = null; //调用被代理对象的方法 result = methodProxy.invokeSuper(o, objects); //后置加强 after(method); return result; } private void after(Method method) { System.out.println("方法执行日志结束,方法名为:" + method.getName()); } private void before(Method method) { System.out.println("方法执行日志开始,方法名为:" + method.getName()); } }
package com.wzj.cglib; import net.sf.cglib.proxy.Enhancer; /** * @Author: wzj * @Date: 2020/8/7 15:13 * @Desc: */ public class CglibProxy<T> { public T createProxy(T t) { //加强器 Enhancer enhancer = new Enhancer(); //设置父类 enhancer.setSuperclass(t.getClass()); //设置回调的拦截器 enhancer.setCallback(new LogMethodInterceptor()); T proxy = (T) enhancer.create(); return proxy; } }
package com.wzj.cglib; import java.util.Random; /** * @Author: wzj * @Date: 2020/8/3 10:29 * @Desc: 待销苹果 */ public class Apple { public void secKill() { System.out.println("苹果正在秒杀中..."); try { Thread.sleep(new Random().nextInt(3000)); } catch (InterruptedException e) { e.printStackTrace(); } } }
测试代码:
package com.wzj.cglib; import net.sf.cglib.core.DebuggingClassWriter; import net.sf.cglib.proxy.Enhancer; /** * @Author: wzj * @Date: 2020/8/7 14:16 * @Desc: */ public class CglibTest { public static void main(String[] args) { //打印代理类 System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, (String)System.getProperties().get("user.dir")); Apple apple = new Apple(); Apple proxy = (Apple) new CglibProxy().createProxy(apple); proxy.secKill(); } }
结果:
代理类的父类信息:com.wzj.cglib.Apple 方法执行日志开始,方法名为:secKill 苹果正在秒杀中... 方法执行日志结束,方法名为:secKill
一样,代理类的信息会打印在工程所在目录下,如图:
这里会有三个类的生成,只有一个是代理类,继承Apple
,其余两个类继承FastClass
,其中一个class为生成的代理类中的每一个方法创建了索引,另一个则为咱们被代理类的全部方法包含其父类的方法创建了索引。
public class Apple$$EnhancerByCGLIB$$3a0529f7 extends Apple implements Factory
public class Apple$$EnhancerByCGLIB$$3a0529f7$$FastClassByCGLIB$$17333818 extends FastClass
public class Apple$$FastClassByCGLIB$$ca2d2eb9 extends FastClass
JDK动态代理的被代理类必需要实现接口。
cglib动态代理是经过继承被代理类来实现,若是被代理类为final
字所修饰的非protect与public类
,则无法代理。
性能测试设计了以下5个类JdkDynamicProxyTest
,CglibProxyTest
,Target
,TargetImpl
,ProxyPerformanceTest
package com.wzj.proxy.v10; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * @Author: wzj * @Date: 2020/8/7 21:58 * @Desc: JDK动态代理 */ public class JdkDynamicProxyTest implements InvocationHandler { private Target target; private JdkDynamicProxyTest(Target target) { this.target = target; } public static Target newProxyInstance(Target target) { return (Target) Proxy.newProxyInstance(JdkDynamicProxyTest.class.getClassLoader(), new Class<?>[]{Target.class}, new JdkDynamicProxyTest(target)); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return method.invoke(target, args); } }
package com.wzj.proxy.v10; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; /** * @Author: wzj * @Date: 2020/8/7 22:02 * @Desc: Cglib代理测试 */ public class CglibProxyTest implements MethodInterceptor { private CglibProxyTest() { } public static <T extends Target> Target newProxyInstance(Class<T> targetInstanceClazz) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(targetInstanceClazz); enhancer.setCallback(new CglibProxyTest()); return (Target) enhancer.create(); } @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { return methodProxy.invokeSuper(o, objects); } }
package com.wzj.proxy.v10; /** * @Author: wzj * @Date: 2020/8/7 21:54 * @Desc: 被代理类 */ public interface Target { int test(int i); }
package com.wzj.proxy.v10; /** * @Author: wzj * @Date: 2020/8/7 21:57 * @Desc: */ public class TargetImpl implements Target { @Override public int test(int i) { return i + 1; } }
package com.wzj.proxy.v10; import java.util.LinkedHashMap; import java.util.Map; /** * @Author: wzj * @Date: 2020/8/7 21:53 * @Desc: 代理性能测试 */ public class ProxyPerformanceTest { public static void main(String[] args) { //建立测试对象 Target nativeTest = new TargetImpl(); Target dynamicProxy = JdkDynamicProxyTest.newProxyInstance(nativeTest); Target cglibProxy = CglibProxyTest.newProxyInstance(TargetImpl.class); //预热一下 int preRunCount = 10000; runWithoutMonitor(nativeTest, preRunCount); runWithoutMonitor(cglibProxy, preRunCount); runWithoutMonitor(dynamicProxy, preRunCount); //执行测试 Map<String, Target> tests = new LinkedHashMap<String, Target>(); tests.put("Native ", nativeTest); tests.put("Dynamic ", dynamicProxy); tests.put("Cglib ", cglibProxy); int repeatCount = 3; int runCount = 1000000; runTest(repeatCount, runCount, tests); runCount = 50000000; runTest(repeatCount, runCount, tests); } private static void runTest(int repeatCount, int runCount, Map<String, Target> tests) { System.out.println( String.format("\n===== run test : [repeatCount=%s] [runCount=%s] [java.version=%s] =====", repeatCount, runCount, System.getProperty("java.version"))); for (int i = 0; i < repeatCount; i++) { System.out.println(String.format("\n--------- test : [%s] ---------", (i + 1))); for (String key : tests.keySet()) { runWithMonitor(tests.get(key), runCount, key); } } } private static void runWithoutMonitor(Target target, int runCount) { for (int i = 0; i < runCount; i++) { target.test(i); } } private static void runWithMonitor(Target target, int runCount, String tag) { long start = System.currentTimeMillis(); for (int i = 0; i < runCount; i++) { target.test(i); } long end = System.currentTimeMillis(); System.out.println("[" + tag + "] Total Time:" + (end - start) + "ms"); } }
测试结果:
JDK1.6.0_43
===== run test : [repeatCount=3] [runCount=1000000] [java.version=1.6.0_43] ===== --------- test : [1] --------- [Native ] Total Time:4ms [Dynamic ] Total Time:57ms [Cglib ] Total Time:59ms --------- test : [2] --------- [Native ] Total Time:7ms [Dynamic ] Total Time:34ms [Cglib ] Total Time:40ms --------- test : [3] --------- [Native ] Total Time:6ms [Dynamic ] Total Time:27ms [Cglib ] Total Time:42ms ===== run test : [repeatCount=3] [runCount=50000000] [java.version=1.6.0_43] ===== --------- test : [1] --------- [Native ] Total Time:358ms [Dynamic ] Total Time:985ms [Cglib ] Total Time:1340ms --------- test : [2] --------- [Native ] Total Time:230ms [Dynamic ] Total Time:564ms [Cglib ] Total Time:817ms --------- test : [3] --------- [Native ] Total Time:177ms [Dynamic ] Total Time:404ms [Cglib ] Total Time:726ms
JDK1.7.0_79
===== run test : [repeatCount=3] [runCount=1000000] [java.version=1.7.0_79] ===== --------- test : [1] --------- [Native ] Total Time:0ms [Dynamic ] Total Time:40ms [Cglib ] Total Time:59ms --------- test : [2] --------- [Native ] Total Time:10ms [Dynamic ] Total Time:10ms [Cglib ] Total Time:70ms --------- test : [3] --------- [Native ] Total Time:0ms [Dynamic ] Total Time:30ms [Cglib ] Total Time:50ms ===== run test : [repeatCount=3] [runCount=50000000] [java.version=1.7.0_79] ===== --------- test : [1] --------- [Native ] Total Time:500ms [Dynamic ] Total Time:933ms [Cglib ] Total Time:1490ms --------- test : [2] --------- [Native ] Total Time:262ms [Dynamic ] Total Time:595ms [Cglib ] Total Time:781ms --------- test : [3] --------- [Native ] Total Time:172ms [Dynamic ] Total Time:406ms [Cglib ] Total Time:693ms
JDK1.8.0_161
===== run test : [repeatCount=3] [runCount=1000000] [java.version=1.8.0_161] ===== --------- test : [1] --------- [Native ] Total Time:6ms [Dynamic ] Total Time:40ms [Cglib ] Total Time:94ms --------- test : [2] --------- [Native ] Total Time:5ms [Dynamic ] Total Time:23ms [Cglib ] Total Time:30ms --------- test : [3] --------- [Native ] Total Time:11ms [Dynamic ] Total Time:20ms [Cglib ] Total Time:28ms ===== run test : [repeatCount=3] [runCount=50000000] [java.version=1.8.0_161] ===== --------- test : [1] --------- [Native ] Total Time:273ms [Dynamic ] Total Time:990ms [Cglib ] Total Time:1419ms --------- test : [2] --------- [Native ] Total Time:241ms [Dynamic ] Total Time:562ms [Cglib ] Total Time:851ms --------- test : [3] --------- [Native ] Total Time:210ms [Dynamic ] Total Time:551ms [Cglib ] Total Time:855ms
笔者机器cpu是英特尔酷睿I5,4核,从测试结果看出,JDK动态代理的速度已经比CGLib动态代理的速度要稍微快一点,并不像一些资料或博客所说的那样,cglib比jdk快几倍。
笔者用的Spring版本是5.1.6,其中有个建立代理的类为DefaultAopProxyFactory
,部分源码以下
public class DefaultAopProxyFactory implements AopProxyFactory, Serializable { @Override public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException { //config.isOptimize() 是否对代理类的生成使用策略优化 其做用是和isProxyTargetClass是同样的 默认为false //config.isProxyTargetClass() 是否使用Cglib的方式建立代理对象 默认为false //hasNoUserSuppliedProxyInterfaces目标类是否有接口存在 且只有一个接口的时候接口类型不是 //SpringProxy类型 if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) { //上面的三个方法有一个为true的话,则进入到这里 Class<?> targetClass = config.getTargetClass(); if (targetClass == null) { throw new AopConfigException("TargetSource cannot determine target class: " + "Either an interface or a target is required for proxy creation."); } //判断目标类是不是接口,若是目标类是接口的话,则使用JDK的方式生成代理对象 //若是目标类是Proxy类型,则仍是使用JDK的方式生成代理对象 if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) { return new JdkDynamicAopProxy(config); } //配置了使用Cglib进行动态代理 或者目标类没有接口 那么使用Cglib的方式建立代理对象 return new ObjenesisCglibAopProxy(config); } else { //上面的三个方法没有一个为true 那使用JDK的提供的代理方式生成代理对象 return new JdkDynamicAopProxy(config); } }
可见SpringAOP底层源码在实现时采用了JDK与Cglib两种动态代理方式
本篇主要讲了代理的好处,即对被代理类无入侵,同时又可使原目标类功能加强;接着讲述了静态代理的迭代演进过程,以及静态代理与动态代理的主要技术实现、与区别,最后经过Spring源码分析了底层所用的代理技术,后续将深刻源码分析动态代理是如何一步一步生成代理类的整个过程,敬请期待。
附:githup源码下载地址:https://github.com/wuzhujun2006/design-patterns