简介
代理模式:使用代理类来增强目标类的功能。在代码结构上,代理对象持有目标对象,通过代理对象访问目标对象,这样可以在不改变目标对象的前提下增加额外的功能,如权限校验、缓存等
代理模式内部的角色:
- 目标类:实现业务功能
- 代理类:用户通过代理类来访问目标类
- 增强方法:代理类中要调用的方法,就是通过它来为目标类添加功能
- 目标接口:代理类和目标类都需要实现目标接口,在有些情况下目标接口是不需要的
代理模式的分类:依据代理类的创建方式,代理模式分为静态代理和动态代理
spring、mybatis等常见框架中大量使用到了动态代理,例如spring的aop,通过动态代理来为目标类增加功能
代理模式的分类
静态代理
静态代理:用户手动编写代理类,在编译时,目标类、代理类已经全部确定
案例:目标类实现售卖功能,代理类中收取服务费
第一步:目标接口
public interface SellTickets {void sell();
}
第二步:目标类
public class TrainStation implements SellTickets {@Overridepublic void sell() {System.out.println("火车站卖票");}
}
第三步:代理类
public class ProxyPoint implements SellTickets {private final TrainStation trainStation; // 代理类持有目标类的实例public ProxyPoint() { }public ProxyPoint(TrainStation trainStation) {this.trainStation = trainStation;}@Overridepublic void sell() {System.out.println("代理点收取服务费");trainStation.sell();}
}
测试:
public class StaticProxyClient {public static void main(String[] args) {// 创建代理类时,传入目标类的实例,通过代理类来访问目标类ProxyPoint proxyPoint = new ProxyPoint(new TrainStation());proxyPoint.sell();}
}
动态代理
动态代理:在程序运行时,基于字节码技术,动态地创建代理类。和静态代理不同的地方在于,静态代理时代理类需要用户手动编写,是编译时生成,动态代理时代理类由程序来自动创建,是运行时生成,用户只需要指定代理类要执行的增强方法和目标类。
用户可以使用jdk原生的方式来创建动态代理类,也可以使用第三方库,例如cglib,提供的方式来创建代理类
基于jdk的动态代理技术
jdk原生的动态代理技术
案例:
第一步:目标接口、目标类,和之前一样
第二步:创建代理类的工厂。代理类是运行时创建的,这里指定代理类的创建方式,程序运行时,使用用户指定的方式,在内存中动态地创建一个类,就是动态代理
public class JdkProxyFactory {// 创建火车站的代理类public static Object getTrainStationProxyObj(TrainStation trainStation) {return dynamicProxy(trainStation,// 代理类中要执行的方法(InvocationHandler) (proxy, method, args) -> { // 这里代理类只拦截指定方法if (method.getName().equals("sell")) { System.out.println("收取代理费");}return method.invoke(trainStation, args); // 执行目标类中的方法});}// 传入目标对象和调用处理器private static <T> Object dynamicProxy(T target, InvocationHandler h) {return Proxy.newProxyInstance(target.getClass().getClassLoader() // 代理类的类加载器, target.getClass().getInterfaces() // 代理类要实现的接口, h);}
}
测试:
public class JdkProxyClient {public static void main(String[] args) {TrainStation targetObj = new TrainStation();SellTickets proxyObj = (SellTickets) JdkProxyFactory.getTrainStationProxyObj(targetObj);proxyObj.sell();}
}
基于cglib的动态代理技术
基于cglib的动态代理技术:由于jdk提供的动态代理技术要求目标类必须要实现目标接口,所以它的使用范围比较小,cglib提供了一种动态代理技术,它是基于继承关系,目标类不需要实现接口。
案例:
第一步:添加依赖
<dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>2.2.2</version>
</dependency>
第二步:目标类,可以没有目标接口,因为cglib提供的动态代理技术,是基于继承关系来进行代理
第三步:创建代理类的工厂
public class CgProxyFactory {public static TrainStation getProxyObj(TrainStation trainStation) {Enhancer enhancer = new Enhancer();// 代理类的父类enhancer.setSuperclass(TrainStation.class);// 代理类中要执行的方法enhancer.setCallback(new MethodInterceptor() {@Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy)throws Throwable {if (method.getName().equals("sell")) {System.out.println("收取代理费");}return method.invoke(trainStation, objects);}});return (TrainStation) enhancer.create();}
}
测试:
public class CgProxyClient {public static void main(String[] args) {TrainStation targetObj = new TrainStation();TrainStation proxyObj = CgProxyFactory.getProxyObj(targetObj);proxyObj.sell();}
}
原理解析
查看创建出的代理类
使用arthas,查看动态创建出的代理类。具体方法,在程序中打印出代理类的类名,然后程序休眠,使用arthas连接程序,在arthas中使用jad查看类的字节码
案例:
public class CgProxyClient {public static void main(String[] args) {TrainStation targetObj = new TrainStation();TrainStation proxyObj = CgProxyFactory.getProxyObj(targetObj);proxyObj.sell();// 代理类的类名// class org.wyj.proxy.cg_proxy.TrainStation$$EnhancerByCGLIB$$37abd3d3System.out.println("proxyObj.getClass() = " + proxyObj.getClass());// 程序休眠,方便arthus连接try {Thread.sleep(10 * 1000 * 1000);} catch (InterruptedException e) {e.printStackTrace();}}
}
使用arthas连接到当前程序,执行命令,jad ${类的全限定名}
,查看动态生成的代理类
jdk生成的代理类
案例:这里直接展示创建结果,下面就是之前案例中jdk在内存中动态创建的代理类的字节码,
package com.sun.proxy;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import org.wyj.proxy.jdk_proxy.SellTickets;public final class $Proxy0
extends Proxy
implements SellTickets {private static Method m1;private static Method m4;private static Method m2;private static Method m3;private static Method m0;// 构造方法,调用这个方法来创建代理类的实例public $Proxy0(InvocationHandler invocationHandler) {super(invocationHandler);}static {try {m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));m4 = Class.forName("org.wyj.proxy.jdk_proxy.SellTickets").getMethod("show", new Class[0]);m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);m3 = Class.forName("org.wyj.proxy.jdk_proxy.SellTickets").getMethod("sell", new Class[0]);m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);return;}catch (NoSuchMethodException noSuchMethodException) {throw new NoSuchMethodError(noSuchMethodException.getMessage());}catch (ClassNotFoundException classNotFoundException) {throw new NoClassDefFoundError(classNotFoundException.getMessage());}}public final boolean equals(Object object) {try {return (Boolean)this.h.invoke(this, m1, new Object[]{object});}catch (Error | RuntimeException throwable) {throw throwable;}catch (Throwable throwable) {throw new UndeclaredThrowableException(throwable);}}public final String toString() {try {return (String)this.h.invoke(this, m2, null);}catch (Error | RuntimeException throwable) {throw throwable;}catch (Throwable throwable) {throw new UndeclaredThrowableException(throwable);}}public final int hashCode() {try {return (Integer)this.h.invoke(this, m0, null);}catch (Error | RuntimeException throwable) {throw throwable;}catch (Throwable throwable) {throw new UndeclaredThrowableException(throwable);}}public final void sell() {try {this.h.invoke(this, m3, null);return;}catch (Error | RuntimeException throwable) {throw throwable;}catch (Throwable throwable) {throw new UndeclaredThrowableException(throwable);}}
}
总结:使用jdk创建出的代理类,默认是Proxy的子类,并且实现了用户指定的接口
cglib创建的代理类
案例:之前案例中创建出的代理类
package org.wyj.proxy.cg_proxy;import java.lang.reflect.Method;
import net.sf.cglib.core.ReflectUtils;
import net.sf.cglib.core.Signature;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.Factory;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.wyj.proxy.cg_proxy.TrainStation;public class TrainStation$$EnhancerByCGLIB$$c8c06447
extends TrainStation
implements Factory {private boolean CGLIB$BOUND;private static final ThreadLocal CGLIB$THREAD_CALLBACKS;private static final Callback[] CGLIB$STATIC_CALLBACKS;private MethodInterceptor CGLIB$CALLBACK_0;private static final Method CGLIB$sell$0$Method;private static final MethodProxy CGLIB$sell$0$Proxy;private static final Object[] CGLIB$emptyArgs;private static final Method CGLIB$finalize$1$Method;private static final MethodProxy CGLIB$finalize$1$Proxy;private static final Method CGLIB$equals$2$Method;private static final MethodProxy CGLIB$equals$2$Proxy;private static final Method CGLIB$toString$3$Method;private static final MethodProxy CGLIB$toString$3$Proxy;private static final Method CGLIB$hashCode$4$Method;private static final MethodProxy CGLIB$hashCode$4$Proxy;private static final Method CGLIB$clone$5$Method;private static final MethodProxy CGLIB$clone$5$Proxy;static void CGLIB$STATICHOOK1() {CGLIB$THREAD_CALLBACKS = new ThreadLocal();CGLIB$emptyArgs = new Object[0];Class<?> clazz = Class.forName("org.wyj.proxy.cg_proxy.TrainStation$$EnhancerByCGLIB$$c8c06447");Class<?> clazz2 = Class.forName("java.lang.Object");Method[] methodArray = ReflectUtils.findMethods(new String[]{"finalize", "()V", "equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, clazz2.getDeclaredMethods());CGLIB$finalize$1$Method = methodArray[0];CGLIB$finalize$1$Proxy = MethodProxy.create(clazz2, clazz, "()V", "finalize", "CGLIB$finalize$1");CGLIB$equals$2$Method = methodArray[1];CGLIB$equals$2$Proxy = MethodProxy.create(clazz2, clazz, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$2");CGLIB$toString$3$Method = methodArray[2];CGLIB$toString$3$Proxy = MethodProxy.create(clazz2, clazz, "()Ljava/lang/String;", "toString", "CGLIB$toString$3");CGLIB$hashCode$4$Method = methodArray[3];CGLIB$hashCode$4$Proxy = MethodProxy.create(clazz2, clazz, "()I", "hashCode", "CGLIB$hashCode$4");CGLIB$clone$5$Method = methodArray[4];CGLIB$clone$5$Proxy = MethodProxy.create(clazz2, clazz, "()Ljava/lang/Object;", "clone", "CGLIB$clone$5");clazz2 = Class.forName("org.wyj.proxy.cg_proxy.TrainStation");CGLIB$sell$0$Method = ReflectUtils.findMethods(new String[]{"sell", "()V"}, clazz2.getDeclaredMethods())[0];CGLIB$sell$0$Proxy = MethodProxy.create(clazz2, clazz, "()V", "sell", "CGLIB$sell$0");}final void CGLIB$sell$0() {super.sell();}// 重写目标类中的方法,在方法中调用用户设置的拦截器public final void sell() {MethodInterceptor methodInterceptor = this.CGLIB$CALLBACK_0;if (methodInterceptor == null) {TrainStation$$EnhancerByCGLIB$$c8c06447.CGLIB$BIND_CALLBACKS(this);methodInterceptor = this.CGLIB$CALLBACK_0;}if (methodInterceptor != null) {Object object = methodInterceptor.intercept(this, CGLIB$sell$0$Method, CGLIB$emptyArgs, CGLIB$sell$0$Proxy);return;}super.sell();}final void CGLIB$finalize$1() throws Throwable {super.finalize();}// 重写Object类中的方法protected final void finalize() throws Throwable {MethodInterceptor methodInterceptor = this.CGLIB$CALLBACK_0;if (methodInterceptor == null) {TrainStation$$EnhancerByCGLIB$$c8c06447.CGLIB$BIND_CALLBACKS(this);methodInterceptor = this.CGLIB$CALLBACK_0;}if (methodInterceptor != null) {Object object = methodInterceptor.intercept(this, CGLIB$finalize$1$Method, CGLIB$emptyArgs, CGLIB$finalize$1$Proxy);return;}super.finalize();}final boolean CGLIB$equals$2(Object object) {return super.equals(object);}public final boolean equals(Object object) {MethodInterceptor methodInterceptor = this.CGLIB$CALLBACK_0;if (methodInterceptor == null) {TrainStation$$EnhancerByCGLIB$$c8c06447.CGLIB$BIND_CALLBACKS(this);methodInterceptor = this.CGLIB$CALLBACK_0;}if (methodInterceptor != null) {Object object2 = methodInterceptor.intercept(this, CGLIB$equals$2$Method, new Object[]{object}, CGLIB$equals$2$Proxy);return object2 == null ? false : (Boolean)object2;}return super.equals(object);}final String CGLIB$toString$3() {return super.toString();}public final String toString() {MethodInterceptor methodInterceptor = this.CGLIB$CALLBACK_0;if (methodInterceptor == null) {TrainStation$$EnhancerByCGLIB$$c8c06447.CGLIB$BIND_CALLBACKS(this);methodInterceptor = this.CGLIB$CALLBACK_0;}if (methodInterceptor != null) {return (String)methodInterceptor.intercept(this, CGLIB$toString$3$Method, CGLIB$emptyArgs, CGLIB$toString$3$Proxy);}return super.toString();}final int CGLIB$hashCode$4() {return super.hashCode();}public final int hashCode() {MethodInterceptor methodInterceptor = this.CGLIB$CALLBACK_0;if (methodInterceptor == null) {TrainStation$$EnhancerByCGLIB$$c8c06447.CGLIB$BIND_CALLBACKS(this);methodInterceptor = this.CGLIB$CALLBACK_0;}if (methodInterceptor != null) {Object object = methodInterceptor.intercept(this, CGLIB$hashCode$4$Method, CGLIB$emptyArgs, CGLIB$hashCode$4$Proxy);return object == null ? 0 : ((Number)object).intValue();}return super.hashCode();}final Object CGLIB$clone$5() throws CloneNotSupportedException {return super.clone();}protected final Object clone() throws CloneNotSupportedException {MethodInterceptor methodInterceptor = this.CGLIB$CALLBACK_0;if (methodInterceptor == null) {TrainStation$$EnhancerByCGLIB$$c8c06447.CGLIB$BIND_CALLBACKS(this);methodInterceptor = this.CGLIB$CALLBACK_0;}if (methodInterceptor != null) {return methodInterceptor.intercept(this, CGLIB$clone$5$Method, CGLIB$emptyArgs, CGLIB$clone$5$Proxy);}return super.clone();}public static MethodProxy CGLIB$findMethodProxy(Signature signature) {String string = ((Object)signature).toString();switch (string.hashCode()) {case -1574182249: {if (!string.equals("finalize()V")) break;return CGLIB$finalize$1$Proxy;}case -508378822: {if (!string.equals("clone()Ljava/lang/Object;")) break;return CGLIB$clone$5$Proxy;}case 1826985398: {if (!string.equals("equals(Ljava/lang/Object;)Z")) break;return CGLIB$equals$2$Proxy;}case 1913648695: {if (!string.equals("toString()Ljava/lang/String;")) break;return CGLIB$toString$3$Proxy;}case 1978249955: {if (!string.equals("sell()V")) break;return CGLIB$sell$0$Proxy;}case 1984935277: {if (!string.equals("hashCode()I")) break;return CGLIB$hashCode$4$Proxy;}}return null;}public TrainStation$$EnhancerByCGLIB$$c8c06447() {TrainStation$$EnhancerByCGLIB$$c8c06447 trainStation$$EnhancerByCGLIB$$c8c06447 = this;TrainStation$$EnhancerByCGLIB$$c8c06447.CGLIB$BIND_CALLBACKS(trainStation$$EnhancerByCGLIB$$c8c06447);}public static void CGLIB$SET_THREAD_CALLBACKS(Callback[] callbackArray) {CGLIB$THREAD_CALLBACKS.set(callbackArray);}public static void CGLIB$SET_STATIC_CALLBACKS(Callback[] callbackArray) {CGLIB$STATIC_CALLBACKS = callbackArray;}private static final void CGLIB$BIND_CALLBACKS(Object object) {block2: {Object object2;block3: {TrainStation$$EnhancerByCGLIB$$c8c06447 trainStation$$EnhancerByCGLIB$$c8c06447 = (TrainStation$$EnhancerByCGLIB$$c8c06447)object;if (trainStation$$EnhancerByCGLIB$$c8c06447.CGLIB$BOUND) break block2;trainStation$$EnhancerByCGLIB$$c8c06447.CGLIB$BOUND = true;object2 = CGLIB$THREAD_CALLBACKS.get();if (object2 != null) break block3;object2 = CGLIB$STATIC_CALLBACKS;if (CGLIB$STATIC_CALLBACKS == null) break block2;}trainStation$$EnhancerByCGLIB$$c8c06447.CGLIB$CALLBACK_0 = (MethodInterceptor)((Callback[])object2)[0];}}public Object newInstance(Callback[] callbackArray) {TrainStation$$EnhancerByCGLIB$$c8c06447.CGLIB$SET_THREAD_CALLBACKS(callbackArray);TrainStation$$EnhancerByCGLIB$$c8c06447 trainStation$$EnhancerByCGLIB$$c8c06447 = new TrainStation$$EnhancerByCGLIB$$c8c06447();TrainStation$$EnhancerByCGLIB$$c8c06447.CGLIB$SET_THREAD_CALLBACKS(null);return trainStation$$EnhancerByCGLIB$$c8c06447;}public Object newInstance(Callback callback) {TrainStation$$EnhancerByCGLIB$$c8c06447.CGLIB$SET_THREAD_CALLBACKS(new Callback[]{callback});TrainStation$$EnhancerByCGLIB$$c8c06447 trainStation$$EnhancerByCGLIB$$c8c06447 = new TrainStation$$EnhancerByCGLIB$$c8c06447();TrainStation$$EnhancerByCGLIB$$c8c06447.CGLIB$SET_THREAD_CALLBACKS(null);return trainStation$$EnhancerByCGLIB$$c8c06447;}public Object newInstance(Class[] classArray, Object[] objectArray, Callback[] callbackArray) {TrainStation$$EnhancerByCGLIB$$c8c06447 trainStation$$EnhancerByCGLIB$$c8c06447;TrainStation$$EnhancerByCGLIB$$c8c06447.CGLIB$SET_THREAD_CALLBACKS(callbackArray);Class[] classArray2 = classArray;switch (classArray.length) {case 0: {trainStation$$EnhancerByCGLIB$$c8c06447 = new TrainStation$$EnhancerByCGLIB$$c8c06447();break;}default: {throw new IllegalArgumentException("Constructor not found");}}TrainStation$$EnhancerByCGLIB$$c8c06447.CGLIB$SET_THREAD_CALLBACKS(null);return trainStation$$EnhancerByCGLIB$$c8c06447;}public Callback getCallback(int n) {MethodInterceptor methodInterceptor;TrainStation$$EnhancerByCGLIB$$c8c06447.CGLIB$BIND_CALLBACKS(this);switch (n) {case 0: {methodInterceptor = this.CGLIB$CALLBACK_0;break;}default: {methodInterceptor = null;}}return methodInterceptor;}public void setCallback(int n, Callback callback) {switch (n) {case 0: {this.CGLIB$CALLBACK_0 = (MethodInterceptor)callback;break;}}}public Callback[] getCallbacks() {TrainStation$$EnhancerByCGLIB$$c8c06447.CGLIB$BIND_CALLBACKS(this);TrainStation$$EnhancerByCGLIB$$c8c06447 trainStation$$EnhancerByCGLIB$$c8c06447 = this;return new Callback[]{this.CGLIB$CALLBACK_0};}public void setCallbacks(Callback[] callbackArray) {Callback[] callbackArray2 = callbackArray;TrainStation$$EnhancerByCGLIB$$c8c06447 trainStation$$EnhancerByCGLIB$$c8c06447 = this;this.CGLIB$CALLBACK_0 = (MethodInterceptor)callbackArray[0];}static {TrainStation$$EnhancerByCGLIB$$c8c06447.CGLIB$STATICHOOK1();}
}
总结:代理类是目标类的子类,会重写它从父类中继承来的方法,包括Object类中的方法。在重写的方法中,调用拦截器来完成任务,拦截器是用户提供的,指定了增强方法和目标对象的执行方式。
代理类失效的情况
基于jdk创建的代理类,代理类只可以代理目标接口中的方法,因为代理类只会实现目标接口中的方法。
基于cglib创建的代理类,代理类只可以代理目标类中可以被继承的方法,因为代理类只会重写可以被继承的方法,如果方法无法被继承,在执行时,会直接调用目标类中的方法,从而导致代理类无法代理。
代理类的执行效率
测试方式:执行100万次,每一次都创建一个代理类,然后调用一次代理类中的方法,方法中什么都不做,这里只关心方法的调用速度
public class JdkProxyClient {public static void main(String[] args) {TrainStation targetObj = new TrainStation();long startTime = System.currentTimeMillis();for (int i = 0; i < 1000000; i++) {SellTickets proxyObj = (SellTickets) JdkProxyFactory.getTrainStationProxyObj(targetObj);proxyObj.sell();}long executeTime = System.currentTimeMillis() - startTime;System.out.println("执行时间:" + executeTime + "ms");}
}
得出的结论:
测试方式 | 基于jdk创建的代理类 | 基于cglib创建的代理类 |
---|---|---|
创建100万个代理类,每个代理类都执行一次 | 112ms | 1130ms |
创建1个代理类,每个代理类都执行100万次 | 29ms | 34ms |
可以看出,执行速度差不多,但是jdk创建代理类的速度更快,因为jdk生成的字节码更少
源码解析
jdk创建代理的方式
API总结:
Proxy:public class Proxy implements java.io.Serializable
:创建代理类的工具类,提供了静态方法,用于创建动态代理类和动态代理对象,它也是所有动态代理类的父类。
public class Proxy implements java.io.Serializable {private static final long serialVersionUID = -2222568056686623797L;// 构造器的参数,动态生成的代理类,会生成一个构造方法,这里就是构造方法的参数/** parameter types of a proxy class constructor */private static final Class<?>[] constructorParams ={ InvocationHandler.class };// 创建代理类并且获取代理对象的方法,用户需要提供三个参数,用于加载代理类的类加载器、// 代理类需要实现的接口、代理类中需要执行的增强方法。@CallerSensitivepublic static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)throws IllegalArgumentException{Objects.requireNonNull(h);final Class<?>[] intfs = interfaces.clone();final SecurityManager sm = System.getSecurityManager();if (sm != null) {checkProxyAccess(Reflection.getCallerClass(), loader, intfs);}/** Look up or generate the designated proxy class.*/// 在这一步会生成代理类Class<?> cl = getProxyClass0(loader, intfs);/** Invoke its constructor with the designated invocation handler.*/try {if (sm != null) {checkNewProxyPermission(Reflection.getCallerClass(), cl);}// 获取代理类中指定的构造方法final Constructor<?> cons = cl.getConstructor(constructorParams);final InvocationHandler ih = h;if (!Modifier.isPublic(cl.getModifiers())) {AccessController.doPrivileged(new PrivilegedAction<Void>() {public Void run() {cons.setAccessible(true);return null;}});}return cons.newInstance(new Object[]{h});} catch (IllegalAccessException|InstantiationException e) {throw new InternalError(e.toString(), e);} catch (InvocationTargetException e) {Throwable t = e.getCause();if (t instanceof RuntimeException) {throw (RuntimeException) t;} else {throw new InternalError(t.toString(), t);}} catch (NoSuchMethodException e) {throw new InternalError(e.toString(), e);}}
}
InvocationHandler:public interface InvocationHandler
:调用处理器,在调用处理器中调用增强方法和目标对象。每个代理实例有一个相关联的调用处理器,代理对象内部实际上是通过调用处理器来工作的
public interface InvocationHandler {// 参数proxy是代理对象// 参数method是目标类中的方法的Method对象,调用哪个方法,这儿就是哪个方法的实例// 参数args是method方法的参数public Object invoke(Object proxy, Method method, Object[] args)throws Throwable;
}
这里对源码的介绍很浅,重点是要理解创建代理类需要提供哪些参数、代理类的执行机制。