代理模式的详细介绍

article/2025/10/2 8:17:05

国庆期间闲来无事,写了一个简单的小程序,小程序名称叫做 IT藏经楼。目的是分享这些年自己积累的一些学习材料,方面大家查找使用,包括电子书、案例项目、学习视频、面试题和一些PPT模板。里面所有材料都免费分享。目前小程序中只发布了非常小的一部分,后续会陆续上传分享。当前版本的小程序页面也比较简单,还在逐渐的优化中。

代理模式的应用场景
在实际生活中,我们经常见到这样的场景,如:租房中介、售票黄牛、婚介、经纪人、快递、事务代理、非侵入式日只监听等,这些都是代理模式的实际体现。代理模式的定义也非常简单,是指为其它对象提供一种代理,以控制对这个对象的访问。
代理对象在客户端和目标对象之间起到中介作用,代理模式属于结构性设计模式。使用代理模式主要有两个目的:一是保护目标对象,二是增强目标对象,下面是代理模式的结构类图:
在这里插入图片描述
Subject是顶层接口,RealSubject是真实对象,Proxy是代理对象,代理对象持有被代理对象的引用,客户端调用代理对象的方法,同时也调用被代理对象的方法,但是在代理对象前后增加一些处理。在代码中,我们想到代理,就会理解为是代码的增强,其实就是在原本逻辑前后增加一些逻辑,而调用者无感知。代理模式属于结构性模式,有静态代理和动态代理。

静态代理
举个例子:人到了试婚年龄,父母总是迫不及待的开始到处为自己的子女相亲。这个相亲的过程,就是一种我们人人都有份的代理。来看代码实现:
顶层接口Person:

public interface Person {public void findLover();
}

儿子要找对象,实现Son 类:

public class Son implements Person {public void findLover() {System.out.println("儿子要求:xxxxxx");}
}

父亲要帮儿子相亲,实现Father 类:

public class Father {private Son son = null;public Father(Son son) {this.son = son;}public void findLover() {System.out.println("父母物色对象");this.son.findLover();System.out.println("双方同意");}
}

来看测试代码:

public class FatherTest {public static void main(String[] args) {Father father = new Father(new Son());father.findLover();}
}

运行结果:

父母物色对象
儿子要求:xxxxxx
双方同意

这里小伙伴们可能会觉得还是不知道如何讲代理模式应用到业务场景中,那么我们再来举例一个实际的业务场景。在分布式业务场景中,我们通常会对数据库进行分库分表,分库分表之后使用Java 操作时,就可能需要配置多个数据源,我们通过设置数据源路由来动态切换数据源。先创建Order 订单实体:

public class Order {private Object orderInfo;private Long createTime;private String id;public Object getOrderInfo() {return orderInfo;}public void setOrderInfo(Object orderInfo) {this.orderInfo = orderInfo;}public Long getCreateTime() {return createTime;}public void setCreateTime(Long createTime) {this.createTime = createTime;}public String getId() {return id;}public void setId(String id) {this.id = id;}
}

创建OrderDao 持久层操作类:

public class OrderDao {public int insert(Order order) {System.out.println("OrderDao创建Order成功");return 1;}
}

创建OrderService 接口:

public interface OrderService {int createOrder(Order order);
}

创建OrderService 实现类:

public class OrderServiceBean implements OrderService {private OrderDao orderDao;public OrderServiceBean() {// 如果使用Spring应该是自动注入// 我们为了测试方便,在构造方法中将orderDao直接初始化了this.orderDao = new OrderDao();}public int createOrder(Order order) {System.out.println("OrderService调用OrderDao创建Order");return orderDao.insert(order);}
}

接下来是用静态代理,主要完成的功能是根据订单创建时间自动按年份进行分开。根据开闭原则,原来写好的逻辑我们不去修改,通过代理对象来完成。先创建数据源路由对象,我们使用ThreadLocal的单例实现,DynamicDataSourceEntry类:

public class DynamicDataSourceEntry {private final static String DEFAULT = null;private final static ThreadLocal<String> local = new ThreadLocal<String>();private DynamicDataSourceEntry() {}// 清空数据源public static void clear() {local.remove();}// 获取当前正在使用的数据源的名字public static String get() {return local.get();}// 还原当前切面的数据源public static void reset() {local.set(DEFAULT);}// 设置数据源的名字public static void set(String source) {local.set(source);}// 根据年份动态设置数据源public static void set(int year) {local.set("DB_" + year);}
}

创建切换数据源的静态代理OrderServiceStaticProxy:

public class OrderServiceStaticProxy implements OrderService {private OrderService orderService;public OrderServiceStaticProxy(OrderService orderService) {this.orderService = orderService;}public int createOrder(Order order) {before();Long createTime = order.getCreateTime();SimpleDateFormat sdf = new SimpleDateFormat("yyyy");Integer routeNumber = Integer.valueOf(sdf.format(new Date(createTime)));System.out.println("静态代理自动分配到 DB_" + routeNumber + " 数据源处理数据");DynamicDataSourceEntry.set(routeNumber);this.orderService.createOrder(order);after();return 0;}private void after() {System.out.println("Proxy after method");}private void before() {System.out.println("Proxy before method");}
}

测试代码:

public class OrderServiceStaticProxyTest {public static void main(String[] args) throws Exception {Order order = new Order();SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");Date date = sdf.parse("2017/02/02");order.setCreateTime(date.getTime());OrderService orderService = new OrderServiceStaticProxy(new OrderServiceBean());orderService.createOrder(order);}
}

运行结果:

Proxy before method
静态代理自动分配到 DB_2017 数据源处理数据
OrderService调用OrderDao创建Order
OrderDao创建Order成功
Proxy after method

符合我们预期的效果,我们再来回归一下类图,看看是不是和我们最开始画的类图一致:
在这里插入图片描述

动态代理
动态代理和静态代理基本思路是一致的,只不过动态代理功能更强大,随着业务的扩展适应性更强。如果还以相亲为例,使用动态代理相当于是能够适应复杂的业务场景,不仅仅是父亲给儿子相亲,如果相亲这一业务发展成了一个产业,进而出现了媒婆、婚介所等这样的形式。那么,此时用的静态代理成本就更大了,需要一个更加通用的解决方案,要满足任何单身人士相亲的需求。我们现在升级一下之前的代码,先看用JDK方式实现:
JDK方式
创建媒婆JDKMeipo类:

public class JDKMeiPo implements InvocationHandler {private Object target;public Object getInstance(Object obj) {this.target = obj;Class<?> clazz = this.target.getClass();return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);}public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {before();Object result = method.invoke(this.target, args);after();return result;}private void after() {System.out.println("物色成功");}private void before() {System.out.println("我是媒婆,以拿到需求");System.out.println("开始物色人选");}
}

创建单身客户Customer类:

public class Customer implements Person {public void findLover() {System.out.println("Customer 相亲对象要求:XXXXXXX");}
}

测试代码:

public class JDKMeiPoTest {public static void main(String[] args) throws Exception {Person person = (Person) new JDKMeiPo().getInstance(new Customer());person.findLover();}
}

运行结果:

我是媒婆,以拿到需求
开始物色人选
Customer 相亲对象要求:XXXXXXX
物色成功

上面这个例子理解了的话,我们再看看数据源动态路由的业务,创建动态代理的类:

public class OrderServiceDynamicProxy implements InvocationHandler {private Object target;public Object getInstance(Object obj) {this.target = obj;Class<?> clazz = this.target.getClass();return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);}public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {before(args[0]);Object obj = method.invoke(this.target, args);after();return obj;}private void before(Object object) {try {System.out.println("proxy before method");Long createTime = (Long) object.getClass().getMethod("getCreateTime").invoke(object, new Object[] {});SimpleDateFormat sdf = new SimpleDateFormat("yyyy");Integer routeNumber = Integer.valueOf(sdf.format(new Date(createTime)));System.out.println("动态代理类自动分配到 DB_" + routeNumber);DynamicDataSourceEntry.set(routeNumber);} catch (Exception e) {e.printStackTrace();}}private void after() {System.out.println("proxy after method");DynamicDataSourceEntry.reset();}
}

测试类代码:

public class OrderServiceDynamicProxyTest {public static void main(String[] args) throws ParseException {Order order = new Order();SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");Date date = sdf.parse("2017/02/02");order.setCreateTime(date.getTime());OrderService orderService = (OrderService) new OrderServiceDynamicProxy().getInstance(new OrderServiceBean());orderService.createOrder(order);}
}

运行结果:

proxy before method
动态代理类自动分配到 DB_2017
OrderService调用OrderDao创建Order
OrderDao创建Order成功
proxy after method

依然能够达到相同的效果,但是使用动态代理后,我们不仅能够实现Order类的动态路由,还可以实现其它任何类的数据源动态路由。当然,有比较重要的约定,必须要求实现getCreateTime()方法,因为路由规则是根据时间来运算的。
JDK Proxy功能这么强大,它是如何实现的呢?我们知道JDK Proxy采用字节码重组,重新生成新的对象来替代原始的对象以达到动态代理的目的,JDK Proxy生成对象的步骤如下:

  1. 拿到被代理对象的引用,通过反射获取到它的所有的接口
  2. JDK Proxy重新生成一个新的类,同时新的类要实现被代理类的所有接口方法
  3. 动态生成Java字节码,把新加的业务逻辑方法由一定的逻辑代码去调用
  4. 编译新生成的Java代码.class
  5. 重新加载到JVM中运行
    以上这个过程就叫做字节码重组,JDK中有一个规范,在classpath下只要是$开头的class文件一般都是自动生成的。那么我们有没有办法看到替代后的对象的真实内容呢?做这样一个测试,我们把内存中的对象字节码通过文件流输出到一个新的class文件,然后利用反编译工具查看class的源代码:
public class JDKMeiPoTest {public static void main(String[] args) throws Exception {Person person = (Person) new JDKMeiPo().getInstance(new Customer());person.findLover();byte [] bytes = ProxyGenerator.generateProxyClass("$Proxy0",new Class[]{Person.class});FileOutputStream os = new FileOutputStream("E://sourceCode//$Proxy0.class");os.write(bytes);os.close();}
}

运行之后我们能在E盘找到一个 P r o x y 0. c l a s s 文件,使用 J a d 反编译得到 Proxy0.class文件,使用Jad反编译得到 Proxy0.class文件,使用Jad反编译得到Proxy0.jad文件,打开可以看到下面内容:

// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.kpdus.com/jad.html
// Decompiler options: packimports(3) import com.yrk.designpattern.proxypattern.common.Person;
import java.lang.reflect.*;public final class $Proxy0 extends Proxyimplements Person
{public $Proxy0(InvocationHandler invocationhandler){super(invocationhandler);}public final boolean equals(Object obj){try{return ((Boolean)super.h.invoke(this, m1, new Object[] {obj})).booleanValue();}catch(Error _ex) { }catch(Throwable throwable){throw new UndeclaredThrowableException(throwable);}}public final void findLover(){try{super.h.invoke(this, m3, null);return;}catch(Error _ex) { }catch(Throwable throwable){throw new UndeclaredThrowableException(throwable);}}public final String toString(){try{return (String)super.h.invoke(this, m2, null);}catch(Error _ex) { }catch(Throwable throwable){throw new UndeclaredThrowableException(throwable);}}public final int hashCode(){try{return ((Integer)super.h.invoke(this, m0, null)).intValue();}catch(Error _ex) { }catch(Throwable throwable){throw new UndeclaredThrowableException(throwable);}}private static Method m1;private static Method m3;private static Method m2;private static Method m0;static {try{m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] {Class.forName("java.lang.Object")});m3 = Class.forName("com.yrk.designpattern.proxypattern.common.Person").getMethod("findLover", new Class[0]);m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);}catch(NoSuchMethodException nosuchmethodexception){throw new NoSuchMethodError(nosuchmethodexception.getMessage());}catch(ClassNotFoundException classnotfoundexception){throw new NoClassDefFoundError(classnotfoundexception.getMessage());}}
}

我们发现$Proxy0 继承了Proxy 类,同时还实现了我们的Person 接口,而且重写了findLove()等方法。而且在静态块中用反射查找到了目标对象的所有方法,而且保存了所有方法的引用,在重写的方法用反射调用目标对象的方法。小伙伴们此时一定在好奇,这些代码是哪里来的呢?其实是JDK 帮我们自动生成的。

CGLib动态代理
简单看一下CGLib代理类的使用,还是以媒婆为例,创建CGLibMeipo:

public class CglibMeiPoProxy implements MethodInterceptor{public Object getInstance(Class<?> clazz) {Enhancer enhancer = new Enhancer();enhancer.setSuperclass(clazz);enhancer.setCallback(this);return enhancer.create();}private void before() {System.out.println("我是媒婆,以拿到需求");System.out.println("开始物色人选");}private void after() {System.out.println("物色成功");}@Overridepublic Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable {before();Object o = arg3.invokeSuper(arg0, arg2);after();return o;}
}

有个小细节,CGLib 代理的目标对象不需要实现任何接口,它是通过动态继承目标对象实现的动态代理。
测试类:

public class CglibMeiPoProxyTest {public static void main(String[] args) {System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,"E://sourceCode//cglib_proxy_class/");Customer customer = (Customer) new CglibMeiPoProxy().getInstance(Customer.class);customer.findLover();}
}

CGLib 的实现原理又是怎样的呢?我们可以在测试代码中加上一句代码,将CGLib 代理后的class 写入到磁盘,然后,我们再反编译一探究竟:
运行结果:

CGLIB debugging enabled, writing to 'E://sourceCode//cglib_proxy_class/'
我是媒婆,以拿到需求
开始物色人选
Customer 相亲对象要求:XXXXXXX
物色成功

重新执行代码,我们会发现在E://sourceCode//cglib_proxy_class 目录下多了三个class 文件,如图:
在这里插入图片描述
通过调试跟踪,我们发现Customer E n h a n c e r B y C G L I B EnhancerByCGLIB EnhancerByCGLIB85c3b6e2.class 就是CGLib生成的代理类,继承了Customer 类。反编译后代码是这样的:

// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.kpdus.com/jad.html
// Decompiler options: packimports(3) 
// Source File Name:   <generated>package com.yrk.designpattern.proxypattern.common;import java.lang.reflect.Method;
import net.sf.cglib.core.ReflectUtils;
import net.sf.cglib.core.Signature;
import net.sf.cglib.proxy.*;// Referenced classes of package com.yrk.designpattern.proxypattern.common:
//            Customerpublic class Customer$$EnhancerByCGLIB$$85c3b6e2 extends Customerimplements Factory
{static void CGLIB$STATICHOOK1(){Method amethod[];Method amethod1[];CGLIB$THREAD_CALLBACKS = new ThreadLocal();CGLIB$emptyArgs = new Object[0];Class class1 = Class.forName("com.yrk.designpattern.proxypattern.common.Customer$$EnhancerByCGLIB$$85c3b6e2");Class class2;amethod = ReflectUtils.findMethods(new String[] {"findLover", "()V"}, (class2 = Class.forName("com.yrk.designpattern.proxypattern.common.Customer")).getDeclaredMethods());Method[] _tmp = amethod;CGLIB$findLover$0$Method = amethod[0];CGLIB$findLover$0$Proxy = MethodProxy.create(class2, class1, "()V", "findLover", "CGLIB$findLover$0");amethod1 = ReflectUtils.findMethods(new String[] {"equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (class2 = Class.forName("java.lang.Object")).getDeclaredMethods());Method[] _tmp1 = amethod1;CGLIB$equals$1$Method = amethod1[0];CGLIB$equals$1$Proxy = MethodProxy.create(class2, class1, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$1");CGLIB$toString$2$Method = amethod1[1];CGLIB$toString$2$Proxy = MethodProxy.create(class2, class1, "()Ljava/lang/String;", "toString", "CGLIB$toString$2");CGLIB$hashCode$3$Method = amethod1[2];CGLIB$hashCode$3$Proxy = MethodProxy.create(class2, class1, "()I", "hashCode", "CGLIB$hashCode$3");CGLIB$clone$4$Method = amethod1[3];CGLIB$clone$4$Proxy = MethodProxy.create(class2, class1, "()Ljava/lang/Object;", "clone", "CGLIB$clone$4");}final void CGLIB$findLover$0(){super.findLover();}public final void findLover(){CGLIB$CALLBACK_0;if(CGLIB$CALLBACK_0 != null) goto _L2; else goto _L1
_L1:JVM INSTR pop ;CGLIB$BIND_CALLBACKS(this);CGLIB$CALLBACK_0;
_L2:JVM INSTR dup ;JVM INSTR ifnull 37;goto _L3 _L4
_L3:break MISSING_BLOCK_LABEL_21;
_L4:break MISSING_BLOCK_LABEL_37;this;CGLIB$findLover$0$Method;CGLIB$emptyArgs;CGLIB$findLover$0$Proxy;intercept();return;super.findLover();return;}final boolean CGLIB$equals$1(Object obj){return super.equals(obj);}public final boolean equals(Object obj){CGLIB$CALLBACK_0;if(CGLIB$CALLBACK_0 != null) goto _L2; else goto _L1
_L1:JVM INSTR pop ;CGLIB$BIND_CALLBACKS(this);CGLIB$CALLBACK_0;
_L2:JVM INSTR dup ;JVM INSTR ifnull 57;goto _L3 _L4
_L3:this;CGLIB$equals$1$Method;new Object[] {obj};CGLIB$equals$1$Proxy;intercept();JVM INSTR dup ;JVM INSTR ifnonnull 50;goto _L5 _L6
_L5:JVM INSTR pop ;false;goto _L7
_L6:(Boolean);booleanValue();
_L7:return;
_L4:return super.equals(obj);}final String CGLIB$toString$2(){return super.toString();}public final String toString(){CGLIB$CALLBACK_0;if(CGLIB$CALLBACK_0 != null) goto _L2; else goto _L1
_L1:JVM INSTR pop ;CGLIB$BIND_CALLBACKS(this);CGLIB$CALLBACK_0;
_L2:JVM INSTR dup ;JVM INSTR ifnull 40;goto _L3 _L4
_L3:this;CGLIB$toString$2$Method;CGLIB$emptyArgs;CGLIB$toString$2$Proxy;intercept();(String);return;
_L4:return super.toString();}final int CGLIB$hashCode$3(){return super.hashCode();}public final int hashCode(){CGLIB$CALLBACK_0;if(CGLIB$CALLBACK_0 != null) goto _L2; else goto _L1
_L1:JVM INSTR pop ;CGLIB$BIND_CALLBACKS(this);CGLIB$CALLBACK_0;
_L2:JVM INSTR dup ;JVM INSTR ifnull 52;goto _L3 _L4
_L3:this;CGLIB$hashCode$3$Method;CGLIB$emptyArgs;CGLIB$hashCode$3$Proxy;intercept();JVM INSTR dup ;JVM INSTR ifnonnull 45;goto _L5 _L6
_L5:JVM INSTR pop ;0;goto _L7
_L6:(Number);intValue();
_L7:return;
_L4:return super.hashCode();}final Object CGLIB$clone$4()throws CloneNotSupportedException{return super.clone();}protected final Object clone()throws CloneNotSupportedException{CGLIB$CALLBACK_0;if(CGLIB$CALLBACK_0 != null) goto _L2; else goto _L1
_L1:JVM INSTR pop ;CGLIB$BIND_CALLBACKS(this);CGLIB$CALLBACK_0;
_L2:JVM INSTR dup ;JVM INSTR ifnull 37;goto _L3 _L4
_L3:this;CGLIB$clone$4$Method;CGLIB$emptyArgs;CGLIB$clone$4$Proxy;intercept();return;
_L4:return super.clone();}public static MethodProxy CGLIB$findMethodProxy(Signature signature){String s = signature.toString();s;s.hashCode();JVM INSTR lookupswitch 5: default 120//                   -1700020978: 60//                   -508378822: 72//                   1826985398: 84//                   1913648695: 96//                   1984935277: 108;goto _L1 _L2 _L3 _L4 _L5 _L6
_L2:"findLover()V";equals();JVM INSTR ifeq 121;goto _L7 _L8
_L8:break MISSING_BLOCK_LABEL_121;
_L7:return CGLIB$findLover$0$Proxy;
_L3:"clone()Ljava/lang/Object;";equals();JVM INSTR ifeq 121;goto _L9 _L10
_L10:break MISSING_BLOCK_LABEL_121;
_L9:return CGLIB$clone$4$Proxy;
_L4:"equals(Ljava/lang/Object;)Z";equals();JVM INSTR ifeq 121;goto _L11 _L12
_L12:break MISSING_BLOCK_LABEL_121;
_L11:return CGLIB$equals$1$Proxy;
_L5:"toString()Ljava/lang/String;";equals();JVM INSTR ifeq 121;goto _L13 _L14
_L14:break MISSING_BLOCK_LABEL_121;
_L13:return CGLIB$toString$2$Proxy;
_L6:"hashCode()I";equals();JVM INSTR ifeq 121;goto _L15 _L16
_L16:break MISSING_BLOCK_LABEL_121;
_L15:return CGLIB$hashCode$3$Proxy;
_L1:JVM INSTR pop ;return null;}public static void CGLIB$SET_THREAD_CALLBACKS(Callback acallback[]){CGLIB$THREAD_CALLBACKS.set(acallback);}public static void CGLIB$SET_STATIC_CALLBACKS(Callback acallback[]){CGLIB$STATIC_CALLBACKS = acallback;}private static final void CGLIB$BIND_CALLBACKS(Object obj){Customer$$EnhancerByCGLIB$$85c3b6e2 customer$$enhancerbycglib$$85c3b6e2 = (Customer$$EnhancerByCGLIB$$85c3b6e2)obj;if(customer$$enhancerbycglib$$85c3b6e2.CGLIB$BOUND) goto _L2; else goto _L1
_L1:Object obj1;customer$$enhancerbycglib$$85c3b6e2.CGLIB$BOUND = true;obj1 = CGLIB$THREAD_CALLBACKS.get();obj1;if(obj1 != null) goto _L4; else goto _L3
_L3:JVM INSTR pop ;CGLIB$STATIC_CALLBACKS;if(CGLIB$STATIC_CALLBACKS != null) goto _L4; else goto _L5
_L5:JVM INSTR pop ;goto _L2
_L4:(Callback[]);customer$$enhancerbycglib$$85c3b6e2;JVM INSTR swap ;0;JVM INSTR aaload ;(MethodInterceptor);CGLIB$CALLBACK_0;
_L2:}public Object newInstance(Callback acallback[]){CGLIB$SET_THREAD_CALLBACKS(acallback);CGLIB$SET_THREAD_CALLBACKS(null);return new Customer$$EnhancerByCGLIB$$85c3b6e2();}public Object newInstance(Callback callback){CGLIB$SET_THREAD_CALLBACKS(new Callback[] {callback});CGLIB$SET_THREAD_CALLBACKS(null);return new Customer$$EnhancerByCGLIB$$85c3b6e2();}public Object newInstance(Class aclass[], Object aobj[], Callback acallback[]){CGLIB$SET_THREAD_CALLBACKS(acallback);JVM INSTR new #2   <Class Customer$$EnhancerByCGLIB$$85c3b6e2>;JVM INSTR dup ;aclass;aclass.length;JVM INSTR tableswitch 0 0: default 35//                   0 28;goto _L1 _L2
_L2:JVM INSTR pop ;Customer$$EnhancerByCGLIB$$85c3b6e2();goto _L3
_L1:JVM INSTR pop ;throw new IllegalArgumentException("Constructor not found");
_L3:CGLIB$SET_THREAD_CALLBACKS(null);return;}public Callback getCallback(int i){CGLIB$BIND_CALLBACKS(this);this;i;JVM INSTR tableswitch 0 0: default 30//                   0 24;goto _L1 _L2
_L2:CGLIB$CALLBACK_0;goto _L3
_L1:JVM INSTR pop ;null;
_L3:return;}public void setCallback(int i, Callback callback){switch(i){case 0: // '\0'CGLIB$CALLBACK_0 = (MethodInterceptor)callback;break;}}public Callback[] getCallbacks(){CGLIB$BIND_CALLBACKS(this);this;return (new Callback[] {CGLIB$CALLBACK_0});}public void setCallbacks(Callback acallback[]){this;acallback;JVM INSTR dup2 ;0;JVM INSTR aaload ;(MethodInterceptor);CGLIB$CALLBACK_0;}private boolean CGLIB$BOUND;public static Object CGLIB$FACTORY_DATA;private static final ThreadLocal CGLIB$THREAD_CALLBACKS;private static final Callback CGLIB$STATIC_CALLBACKS[];private MethodInterceptor CGLIB$CALLBACK_0;private static Object CGLIB$CALLBACK_FILTER;private static final Method CGLIB$findLover$0$Method;private static final MethodProxy CGLIB$findLover$0$Proxy;private static final Object CGLIB$emptyArgs[];private static final Method CGLIB$equals$1$Method;private static final MethodProxy CGLIB$equals$1$Proxy;private static final Method CGLIB$toString$2$Method;private static final MethodProxy CGLIB$toString$2$Proxy;private static final Method CGLIB$hashCode$3$Method;private static final MethodProxy CGLIB$hashCode$3$Proxy;private static final Method CGLIB$clone$4$Method;private static final MethodProxy CGLIB$clone$4$Proxy;static {CGLIB$STATICHOOK1();}public Customer$$EnhancerByCGLIB$$85c3b6e2(){CGLIB$BIND_CALLBACKS(this);}
}

重写了Customer 类的所有方法。我们通过代理类的源码可以看到,代理类会获得所有在父类继承来的方法, 并且会有MethodProxy 与之对应, 比如MethodCGLIB$findLove 0 0 0Method、MethodProxy CGLIB$findLove 0 0 0Proxy;这些方法在代
理类的findLove()中都有调用。

final void CGLIB$findLover$0(){super.findLover();}public final void findLover(){CGLIB$CALLBACK_0;if(CGLIB$CALLBACK_0 != null) goto _L2; else goto _L1
_L1:JVM INSTR pop ;CGLIB$BIND_CALLBACKS(this);CGLIB$CALLBACK_0;
_L2:JVM INSTR dup ;JVM INSTR ifnull 37;goto _L3 _L4
_L3:break MISSING_BLOCK_LABEL_21;
_L4:break MISSING_BLOCK_LABEL_37;this;CGLIB$findLover$0$Method;CGLIB$emptyArgs;CGLIB$findLover$0$Proxy;intercept();return;super.findLover();return;}

调用过程: 代理对象调用this.findLove() 方法-> 调用拦截器->methodProxy.invokeSuper->CGLIB$findLove$0->被代理对象findLove()方法。此时,我们发现拦截器MethodInterceptor 中就是由MethodProxy 的invokeSuper方法调用代理方法的,MethodProxy 非常关键,我们分析一下它具体做了什么。

public class MethodProxy {private Signature sig1;private Signature sig2;private CreateInfo createInfo;private final Object initLock = new Object();private volatile FastClassInfo fastClassInfo;/*** For internal use by {@link Enhancer} only; see the {@link net.sf.cglib.reflect.FastMethod} class* for similar functionality.*/public static MethodProxy create(Class c1, Class c2, String desc, String name1, String name2) {MethodProxy proxy = new MethodProxy();proxy.sig1 = new Signature(name1, desc);proxy.sig2 = new Signature(name2, desc);proxy.createInfo = new CreateInfo(c1, c2);return proxy;}private void init(){/* * Using a volatile invariant allows us to initialize the FastClass and* method index pairs atomically.* * Double-checked locking is safe with volatile in Java 5.  Before 1.5 this * code could allow fastClassInfo to be instantiated more than once, which* appears to be benign.*/if (fastClassInfo == null){synchronized (initLock){if (fastClassInfo == null){CreateInfo ci = createInfo;FastClassInfo fci = new FastClassInfo();fci.f1 = helper(ci, ci.c1);fci.f2 = helper(ci, ci.c2);fci.i1 = fci.f1.getIndex(sig1);fci.i2 = fci.f2.getIndex(sig2);fastClassInfo = fci;createInfo = null;}}}}private static class FastClassInfo{FastClass f1;FastClass f2;int i1;int i2;}private static class CreateInfo{Class c1;Class c2;NamingPolicy namingPolicy;GeneratorStrategy strategy;boolean attemptLoad;public CreateInfo(Class c1, Class c2){this.c1 = c1;this.c2 = c2;AbstractClassGenerator fromEnhancer = AbstractClassGenerator.getCurrent();if (fromEnhancer != null) {namingPolicy = fromEnhancer.getNamingPolicy();strategy = fromEnhancer.getStrategy();attemptLoad = fromEnhancer.getAttemptLoad();}}}private static FastClass helper(CreateInfo ci, Class type) {FastClass.Generator g = new FastClass.Generator();g.setType(type);g.setClassLoader(ci.c2.getClassLoader());g.setNamingPolicy(ci.namingPolicy);g.setStrategy(ci.strategy);g.setAttemptLoad(ci.attemptLoad);return g.create();}private MethodProxy() {}/*** Return the signature of the proxied method.*/public Signature getSignature() {return sig1;}/*** Return the name of the synthetic method created by CGLIB which is* used by {@link #invokeSuper} to invoke the superclass* (non-intercepted) method implementation. The parameter types are* the same as the proxied method.*/public String getSuperName() {return sig2.getName();}/*** Return the {@link net.sf.cglib.reflect.FastClass} method index* for the method used by {@link #invokeSuper}. This index uniquely* identifies the method within the generated proxy, and therefore* can be useful to reference external metadata.* @see #getSuperName*/public int getSuperIndex() {init();return fastClassInfo.i2;}// For testingFastClass getFastClass() {init();return fastClassInfo.f1;}// For testingFastClass getSuperFastClass() {init();return fastClassInfo.f2;}/*** Return the <code>MethodProxy</code> used when intercepting the method* matching the given signature.* @param type the class generated by Enhancer* @param sig the signature to match* @return the MethodProxy instance, or null if no applicable matching method is found* @throws IllegalArgumentException if the Class was not created by Enhancer or does not use a MethodInterceptor*/public static MethodProxy find(Class type, Signature sig) {try {Method m = type.getDeclaredMethod(MethodInterceptorGenerator.FIND_PROXY_NAME,MethodInterceptorGenerator.FIND_PROXY_TYPES);return (MethodProxy)m.invoke(null, new Object[]{ sig });} catch (NoSuchMethodException e) {throw new IllegalArgumentException("Class " + type + " does not use a MethodInterceptor");} catch (IllegalAccessException e) {throw new CodeGenerationException(e);} catch (InvocationTargetException e) {throw new CodeGenerationException(e);}}/*** Invoke the original method, on a different object of the same type.* @param obj the compatible object; recursion will result if you use the object passed as the first* argument to the MethodInterceptor (usually not what you want)* @param args the arguments passed to the intercepted method; you may substitute a different* argument array as long as the types are compatible* @see MethodInterceptor#intercept* @throws Throwable the bare exceptions thrown by the called method are passed through* without wrapping in an <code>InvocationTargetException</code>*/public Object invoke(Object obj, Object[] args) throws Throwable {try {init();FastClassInfo fci = fastClassInfo;return fci.f1.invoke(fci.i1, obj, args);} catch (InvocationTargetException e) {throw e.getTargetException();} catch (IllegalArgumentException e) {if (fastClassInfo.i1 < 0)throw new IllegalArgumentException("Protected method: " + sig1);throw e;}}/*** Invoke the original (super) method on the specified object.* @param obj the enhanced object, must be the object passed as the first* argument to the MethodInterceptor* @param args the arguments passed to the intercepted method; you may substitute a different* argument array as long as the types are compatible* @see MethodInterceptor#intercept* @throws Throwable the bare exceptions thrown by the called method are passed through* without wrapping in an <code>InvocationTargetException</code>*/public Object invokeSuper(Object obj, Object[] args) throws Throwable {try {init();FastClassInfo fci = fastClassInfo;return fci.f2.invoke(fci.i2, obj, args);} catch (InvocationTargetException e) {throw e.getTargetException();}}
}

上面代码调用过程就是获取到代理类对应的FastClass,并执行了代理方法。还记得之前生成三个class 文件吗?
Customer$$EnhancerByCGLIB$$85c3b6e2$$FastClassByCGLIB$$59eda6fa.class 就是代理类的FastClass,Customer$$FastClassByCGLIB$$9243bf7a.class 就是被代理类的FastClass。CGLib 动态代理执行代理方法效率之所以比JDK 的高是因为Cglib 采用了FastClass 机制,它的原理简单来说就是:为代理类和被代理类各生成一个Class,这个Class 会为代理类或被代理类的方法分配一个index(int 类型)。这个index 当做一个入参,FastClass就可以直接定位要调用的方法直接进行调用,这样省去了反射调用,所以调用效率比JDK动态代理通过反射调用高。

CGLib 和JDK 动态代理对比

  1. JDK 动态代理是实现了被代理对象的接口,CGLib是集成了被代理对象
  2. JDK和CGLib都是在运行期间生成字节码,JDK是直接写Class字节码,CGLib使用ASM框架写Class字节码,CGLib实现更复杂,生成代理类比JDK效率低
  3. JDK调用代理方法是通过反射调用,CGLib是通过FastCalss机制直接调用,CGLib执行效率更高

代理模式在Spring中的应用
Spring利用动态代理实现AOP有两个非常重要的类,一个是JdkDynamicAopProxy,一个是CglibAopProxy。来看一下类图:
在这里插入图片描述
Spring中代理选择的原则:

  1. 当Bean有实现接口时,Spring就会用JDK动态代理
  2. 当Bean没有实现接口,Spring选择CGLib
  3. Spring可以通过配置强制使用CGLib,只需要在Spring中加入下面代码:
<aop:aspectj-autoproxy proxy-target-class="true"/>

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

  1. 静态代理只能通过手动完成代理操作,如果被代理类增加新方法,代理类需要同步新增方法,违背开闭原则
  2. 动态代理采用运行时动态生成代码的方式,取消了对被代理类的扩展限制,遵循开闭原则
  3. 若动态代理需要对目标类的增强逻辑扩展,结合策略模式,只需新增策略类便可完成,无需修改代理类代码

代理模式优缺点
使用代理模式有以下优点:

  1. 代理模式能将代理对象与真实被调用的目标对象隔离
  2. 一定程度上降低了系统的耦合度,扩展性好
  3. 可以起到保护目标对象的作用
  4. 可以对目标对象的功能增强

当然,代理模式也有缺点:

  1. 代理模式会造成系统设计中类的数量增加
  2. 在客户端与目标对象之间增加一个代理对象,会造成请求处理速度变慢
  3. 增加了系统的复杂度

http://chatgpt.dhexx.cn/article/KdvjXmCd.shtml

相关文章

代理模式

代理模式 代理模式应用场景模式结构实现方式 代理模式 控制着对于原对象的访问&#xff0c; 并允许在将请求提交给对象前后进行一些处理。 优点&#xff1a; 在客户端与目标对象之间起到一个中介作用和保护目标对象的作用&#xff1b;代理对象可以扩展目标对象的功能&#x…

代理模式详解

1. 代理模式 代理模式是一种比较好理解的设计模式。简单来说就是 我们使用代理对象来代替对真实对象(real object)的访问&#xff0c;这样就可以在不修改原目标对象的前提下&#xff0c;提供额外的功能操作&#xff0c;扩展目标对象的功能。 代理模式一半包含三种角色&#x…

代理模式详细讲解

文章目录 一、什么是代理模式&#xff1f;二、 为什么使用代理模式&#xff1f;三、 代理模式的实现1、静态代理1.1、 创建公共的接口1.2、 创建被代理角色1.3、 创建代理角色1.4、 创建测试类 2、动态代理的实现2.1、 使用 JDK 的 Proxy 类实现动态代理1、 创建业务接口2、 创…

一文搞懂代理模式

代理模式 前言一、代理模式是什么&#xff1f;二、静态代理三、动态代理1.jdk动态代理&#xff08;接口代理&#xff09;2.cglib动态代理 前言 一、代理模式是什么&#xff1f; 代理模式是常见的设计模式之一&#xff0c;顾名思义&#xff0c;代理模式就是代理对象具备真实对象…

手机测试耳机音质的软件,耳机音质测试软件有哪些

耳机音质测试软件并不存在&#xff0c;因为耳机的音质对于人来说是主观的&#xff0c;你认为音质好的耳机&#xff0c;有的人却并不认为也很正常&#xff0c;国内外都没有恒定的耳机音质评判标准&#xff0c;大多数人认为音质好的耳机&#xff0c;那么它十有八九实际就是好的。…

软件测试的基本理论与方法

文章目录 前言软件测试基础1. 软件测试的概念2. 软件测试的目的 测试用例1. 测试用例的概念2. 测试用例的分类3. 测试用例的治理4. 测试用例的编制及使用5. 测试需求 软件生命周期1. 问题的定义及规划2. 需求分析3. 软件设计4. 程序编码5. 软件测试6. 运行维护7. 生命周期模型 …

固态硬盘测试软件有哪些,SSD测试软件有哪些?SSD测试软件盘点

在购买了固态硬盘(SSD)后&#xff0c;如果对商家或官方给出的诸如读写速度等关键性数据存在质疑的话&#xff0c;最好的解决方法莫过于通过SSD测试软件来找寻答案。那么&#xff0c;SSD测试软件有哪些&#xff1f;SSD测试软件哪个比较好用呢&#xff1f;针对上述问题&#xff0…

软件工程-软件测试-测试方法

软件工程-测试方法-白盒和黑盒测试 软件工程-软件测试 软件测试方法可以分为静态测试和动态测试。 静态测试 静态测试是指被测试程序不在程序上运行&#xff0c;而是采用人工检测和计算机辅助金泰分析的手段对程序进行检测。 静态测试方法 &#xff08;1&#xff09;人工检…

软件测试方法和技术有哪些?

软件测试方法和技术有哪些&#xff1f;包括软件测试基础、软件测试方法、软件测试流程和规范、单元测试与集成测试、系统测试、验收测试、软件本地化测试、测试自动化及其框架、白盒测试和黑盒测试等。 软件测试方法技术   软件测试方法&#xff1a; 一、等价类测试方法的…

软件测试方法汇总 - 从不同角度分析软件测试方法有哪些

1、从是否关心内部结构来看 &#xff08;1&#xff09;白盒测试&#xff1a;又称为结构测试或逻辑驱动测试&#xff0c;是一种按照程序内部逻辑结构和编码结构&#xff0c;设计测试数据并完成测试的一种测试方法。 &#xff08;2&#xff09;黑盒测试&#xff1a;又称为数据驱…

常见的软件测试方法有,常见的几种软件测试方法都有哪些

随着互联网的不断发展&#xff0c;越来越多的人也都开始学习软件测试的相关技术&#xff0c;而今天我们就一起来了解一下&#xff0c;常见的几种软件测试方法都有哪些。 1、单元测试 单元测试测试的是代码库的单元。 它们直接调用函数或单元&#xff0c;并确保返回正确的结果。…

Dynatrace系列之- 标记常见问题

Dynatrace系列之- 标记常见问题 在大型环境中&#xff0c;系统的某些方面可能会持续触发不必要的警报。这些告警可能来自非关键组件或者是非关键情况下的资源不足的问题。通常这些问题不需要人员响应。 为了减少此类警报并避免发出垃圾告警邮件&#xff0c;Dynatrace AI根因分…

什么是 Dynatrace 里的 Visually Complete 度量标准

Dynatrace 中的 Visually Complete 是一个度量标准&#xff0c;用于测量在加载过程中用户在页面上看到的内容。Visually Complete 指标是当一个用户看到页面上的所有重要元素并且它们正确地渲染时所测量的时间点。这意味着所有可见的文本、图像和视频都已加载并正确显示。该指标…

DevSecOps 团队请避免陷入这些可观测性陷阱

如果您发现难以管理跨越多个云运行不断变化的容器化工作负载的庞大基础架构&#xff0c;那么您并不孤单。 根据 Dynatrace 近期发布的一项研究&#xff0c;超过一半的组织的 DevSecOps 团队在可观测性数据方面感到痛苦。 多云和混合计算设置的兴起&#xff0c;使大部分组织能够…

前端性能分析工具Dyna Trace使用心得(转)

什么是dynatrace ajax “dynatrace ajax 是一个详细的底层追踪工具&#xff0c;它不仅可以显示所有请求和文件在网络中传输的时间&#xff0c;还会记录浏览器render&#xff0c;CPU消耗、JS解析和运行情况等详细的信息&#xff0c;而这些也只是dynatrace ajax的冰山一角。” 为…

使用dynatrace+showslow进行前端性能测试

原文&#xff1a;http://blog.csdn.net/zhangren07/article/details/6883617 1.背景 应用的性能测试与优化目前主要停留在服务器端的反馈&#xff0c;而对于前端性能标准的研究与测试相对比较空白&#xff0c;缺乏统一的标准与工具。众所周知&#xff0c;浏览器html组件的下载…

什么是 Dynatrace 里的 User Action

用户操作是与最终用户界面的交互&#xff0c;涉及对 Web 服务器的调用&#xff0c;这可能有多个嵌套调用。 它是由用户输入&#xff08;例如页面加载、单击或触摸&#xff09;触发的从一个视图到另一个视图的转换。 Web 应用的 User Action 类型&#xff1a; Load actionsXHR …

Ultra Fast Deep Lane Detection with HybridAnchor Driven Ordinal Classification

Abstract 我们将车道检测过程视为一个使用全局特征的锚定驱动的有序分类问题。 首先&#xff0c;我们在一系列混合&#xff08;行和列&#xff09;锚点上用稀疏坐标表示车道。在锚驱动表示的帮助下&#xff0c;我们将车道检测任务重新表述为一个有序分类问题&#xff0c;以得到…

Dynatrace系列之-排除干扰请求

排除干扰请求 Dyatrace监控了所有服务端的请求。当特定请求的性能或者失败率高的时候&#xff0c;Dynatrace将触发告警。然尔不是所有的高并发的请求都是重要的请求&#xff0c;有些慢请求也不需要告警。比如心跳请求。这些不重要的请求可能会干扰整个服务(service)的响应时间…

Web Performance工具 – Dynatrace AJAX Edition

Dynatrace AJAX Edition是我认为最为强大的Web Performance Profile工具。废话不说了,直接上图介绍其主要功能。 先用IE访问你需要profile的网站,例如google,可以点击dynatrace工具栏来启动。这时候dynatrace就开始记录这个网站触发的一切事件。 我简单测试一下,点击googl…