我们在面试的时候经常被问到Spring AOP的原理,大部分背一背面试题就顺口说出基于JDK或者cglib来实现的。但是问到动态代理的底层是如何实现的就一脸懵逼了,也不知道如何在实际业务中使用动态代理来处理业务。本节我们一起来探讨一下动态代理原理以及其使用场景。
代理模式
这里可以举一个小场景来说明代理模式:小明由于喜欢小红,但是又胆小内向,不敢向小红表白。小明买好了鲜花并写好情书让他的好朋友小王去帮他去追小红。这里就可以理解为小王代理了小明,实际追求者还是小明。当然了代理对象不能瞎来,自己把情书一扔,拿着鲜花把小红给追了(被截胡了)。
接口类:
public interface Propose {
void say();
}
被代理类:
public class Xiaoming implements Propose {
@Override
public void say() {
System.out.println("小明深情的向小红说:我要向你求婚。");
}
}
代理类:
public class Xiaowang implements Propose {
private Xiaoming xiaoming;
public Xiaowang(Xiaoming xiaoming) {
this.xiaoming = xiaoming;
}
@Override
public void say() {
System.out.print("小王代");
this.xiaoming.say();
}
public static void main(String[] args) {
Xiaoming xm = new Xiaoming();
xm.say();
Xiaowang xw = new Xiaowang(xm);
xw.say();
}
}
执行后的效果如下:
小明深情的向小红说:我要向你求婚。
小王代小明深情的向小红说:我要向你求婚。
代理模式是为其它对象提供一种代理以控制对这个对象的访问。一般有三个角色,代理者、被代理者、共同实现的接口。
什么是动态代理
在理解动态代理之前,我们需要先了解静态代理。那什么是静态代理?静态代理就是与实际的代理对象共同实现一个接口,比如小明追小红,陪逛街等行为,都需要小王来实现(总感觉有点不对劲)。优点是小王帮小明来执行这些行为,小明给出脚本即可,小王不需要知道怎么去想如何追妹纸。缺点就是妹纸突然说我要买个房子,小王得去找小明商量(扩展困难,需要同时实现相同的接口方法),让小明出钱,不然妹纸的需求就不能满足了。这个就涉及到我们本节要说的动态代理了,动态代理就好比妹纸要买房子,实际小王只需要告诉去买房子就可以了。不管妹纸有什么新花样,一律找小明即可。
静态代理优点:
- 外部类不需要知道具体的实现类业务细节,只需知道代理对象即可(实现了解耦)。
静态代理缺点:
- 如果有大量的需要被代理的对象,则需要业务硬编码大量的代理对象,出现大量的重复代码,也不利于业务扩展。
- 一般代理对象只能服务一种类型,如果要作用其他类型,则还需要定义额外的类型,一般静态代理则无法胜任了。
JDK动态代理
接口类:
public interface Propose {
void say();
}
InvocationHandler:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class Test implements Propose, InvocationHandler {
private String animal;
public Test(String animal) {
this.animal = animal;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
this.animal = "小王代" + this.animal;
return method.invoke(this, args);
}
@Override
public void say() {
System.out.println(this.animal + "深情的向小红说:我要向你求婚。");
}
public static void main(String[] args) {
Test test = new Test("小明");
test.say();
Propose you = (Propose)Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class>[]{Propose.class}, test);
//打印代理者的类全路径
System.out.println(you.getClass().getName());
you.say();
}
}
执行后的效果:
[root@vis ~]# java Test
小明深情的向小红说:我要向你求婚。
com.sun.proxy.$Proxy0
小王代小明深情的向小红说:我要向你求婚。
那JDK动态代理他如何对被代理对象进行代理的呢?我们可以通过arthas
来反编译出JDK动态代理生成出来的类。
[arthas@24303]$ sc com.sun.proxy.$Proxy0
com.sun.proxy.$Proxy0
Affect(row-cnt:1) cost in 33 ms.
[arthas@24303]$ jad com.sun.proxy.$Proxy0
ClassLoader:
+-sun.misc.Launcher$AppClassLoader@4e0e2f2a
+-sun.misc.Launcher$ExtClassLoader@2745df7e
Location:
/*
* Decompiled with CFR.
*
* Could not load the following classes:
* Propose
*/
package com.sun.proxy;
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 Propose {
//所有的被代理的方法
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
public final void say() {
try {
this.h.invoke(this, m3, null);
return;
}
catch (Error | RuntimeException throwable) {
throw throwable;
}
catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
public $Proxy0(InvocationHandler invocationHandler) {
super(invocationHandler);//invocationHandler 是被代理对象
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m3 = Class.forName("Propose").getMethod("say", 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);
}
}
}
Affect(row-cnt:1) cost in 1597 ms.
这里我们就可以看出实际JDK内部在生成代理类的时候,将自动实现Propose
接口,并重写了say()
方法来调用InvocationHandler
实例对象的invoke方法。
JDK动态代理原理
我们通过Proxy.newProxyInstance()
方法入手:
查看getProxyClass0()
方法:
WeakCache.get()方法
:
Factory.get()方法
:
紧接着我们来查看valueFactory
是什么:
ProxyClassFactory.apply()
方法:
cglib动态代理
接口类:
public interface Propose {
void say();
}
被代理对象:
public class Test implements Propose {
private String animal;
public Test(String animal) {
this.animal = animal;
}
@Override
public void say() {
System.out.println(this.animal + "深情的向小红说:我要向你求婚。");
}
}
cglib代理拦截:
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class ProxyInterceptor implements MethodInterceptor {
private Propose propose;
public ProxyInterceptor(Propose propose){
this.propose = propose;
}
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.print("小王代");
Object object = method.invoke(propose, args);
return object;
}
//编译 javac -classpath ./asm-3.3.1.jar:./cglib-2.2.2.jar:./ ProxyInterceptor.java
//运行 java -classpath ./asm-3.3.1.jar:./cglib-2.2.2.jar:./ ProxyInterceptor
public static void main(String[] args) {
Test test = new Test("小明");
test.say();
//在指定目录下生成动态代理类,我们可以反编译看一下里面到底是一些什么东西
//System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "E:/");
//创建Enhancer对象,类似于JDK动态代理的Proxy类,下一步就是设置几个参数
Enhancer enhancer = new Enhancer();
enhancer.setInterfaces(new Class>[]{Propose.class});
enhancer.setCallback(new ProxyInterceptor(test));
//这里的creat方法就是正式创建代理类
Propose proxy = (Propose)enhancer.create();
//调用代理类的say方法
proxy.say();
}
}
我们通过arthas
来反编译:
[arthas@34686]$ jad Propose$$EnhancerByCGLIB$$87cc398d
ClassLoader:
+-sun.misc.Launcher$AppClassLoader@15db9742
+-sun.misc.Launcher$ExtClassLoader@ac55082
Location:
/root/cglib-2.2.2.jar
/*
* Decompiled with CFR.
*
* Could not load the following classes:
* Propose
*/
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;
public class Propose$$EnhancerByCGLIB$$87cc398d
implements Propose,
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$finalize$0$Method;
private static final MethodProxy CGLIB$finalize$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;
private static final Method CGLIB$say$5$Method;
private static final MethodProxy CGLIB$say$5$Proxy;
public final void say() {
MethodInterceptor methodInterceptor = this.CGLIB$CALLBACK_0;
if (methodInterceptor == null) {
Propose$$EnhancerByCGLIB$$87cc398d.CGLIB$BIND_CALLBACKS(this);
methodInterceptor = this.CGLIB$CALLBACK_0;
}
if (methodInterceptor != null) {
Object object = methodInterceptor.intercept(this, CGLIB$say$5$Method, CGLIB$emptyArgs, CGLIB$say$5$Proxy);
return;
}
super.say();
}
...
static void CGLIB$STATICHOOK1() {
CGLIB$THREAD_CALLBACKS = new ThreadLocal();
CGLIB$emptyArgs = new Object[0];
Class> class_ = Class.forName("Propose$$EnhancerByCGLIB$$87cc398d");
Class> class_2 = Class.forName("java.lang.Object");
Method[] arrmethod = ReflectUtils.findMethods(new String[]{"finalize", "()V", "equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, class_2.getDeclaredMethods());
CGLIB$finalize$0$Method = arrmethod[0];
CGLIB$finalize$0$Proxy = MethodProxy.create(class_2, class_, "()V", "finalize", "CGLIB$finalize$0");
CGLIB$equals$1$Method = arrmethod[1];
CGLIB$equals$1$Proxy = MethodProxy.create(class_2, class_, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$1");
CGLIB$toString$2$Method = arrmethod[2];
CGLIB$toString$2$Proxy = MethodProxy.create(class_2, class_, "()Ljava/lang/String;", "toString", "CGLIB$toString$2");
CGLIB$hashCode$3$Method = arrmethod[3];
CGLIB$hashCode$3$Proxy = MethodProxy.create(class_2, class_, "()I", "hashCode", "CGLIB$hashCode$3");
CGLIB$clone$4$Method = arrmethod[4];
CGLIB$clone$4$Proxy = MethodProxy.create(class_2, class_, "()Ljava/lang/Object;", "clone", "CGLIB$clone$4");
class_2 = Class.forName("Propose");
CGLIB$say$5$Method = ReflectUtils.findMethods(new String[]{"say", "()V"}, class_2.getDeclaredMethods())[0];
CGLIB$say$5$Proxy = MethodProxy.create(class_2, class_, "()V", "say", "CGLIB$say$5");
}
...
protected final void finalize() throws Throwable {
MethodInterceptor methodInterceptor = this.CGLIB$CALLBACK_0;
if (methodInterceptor == null) {
Propose$$EnhancerByCGLIB$$87cc398d.CGLIB$BIND_CALLBACKS(this);
methodInterceptor = this.CGLIB$CALLBACK_0;
}
if (methodInterceptor != null) {
Object object = methodInterceptor.intercept(this, CGLIB$finalize$0$Method, CGLIB$emptyArgs, CGLIB$finalize$0$Proxy);
return;
}
super.finalize();
}
public final boolean equals(Object object) {
MethodInterceptor methodInterceptor = this.CGLIB$CALLBACK_0;
if (methodInterceptor == null) {
Propose$$EnhancerByCGLIB$$87cc398d.CGLIB$BIND_CALLBACKS(this);
methodInterceptor = this.CGLIB$CALLBACK_0;
}
if (methodInterceptor != null) {
Object object2 = methodInterceptor.intercept(this, CGLIB$equals$1$Method, new Object[]{object}, CGLIB$equals$1$Proxy);
return object2 == null ? false : (Boolean)object2;
}
return super.equals(object);
}
public final String toString() {
MethodInterceptor methodInterceptor = this.CGLIB$CALLBACK_0;
if (methodInterceptor == null) {
Propose$$EnhancerByCGLIB$$87cc398d.CGLIB$BIND_CALLBACKS(this);
methodInterceptor = this.CGLIB$CALLBACK_0;
}
if (methodInterceptor != null) {
return (String)methodInterceptor.intercept(this, CGLIB$toString$2$Method, CGLIB$emptyArgs, CGLIB$toString$2$Proxy);
}
return super.toString();
}
public final int hashCode() {
MethodInterceptor methodInterceptor = this.CGLIB$CALLBACK_0;
if (methodInterceptor == null) {
Propose$$EnhancerByCGLIB$$87cc398d.CGLIB$BIND_CALLBACKS(this);
methodInterceptor = this.CGLIB$CALLBACK_0;
}
if (methodInterceptor != null) {
Object object = methodInterceptor.intercept(this, CGLIB$hashCode$3$Method, CGLIB$emptyArgs, CGLIB$hashCode$3$Proxy);
return object == null ? 0 : ((Number)object).intValue();
}
return super.hashCode();
}
protected final Object clone() throws CloneNotSupportedException {
MethodInterceptor methodInterceptor = this.CGLIB$CALLBACK_0;
if (methodInterceptor == null) {
Propose$$EnhancerByCGLIB$$87cc398d.CGLIB$BIND_CALLBACKS(this);
methodInterceptor = this.CGLIB$CALLBACK_0;
}
if (methodInterceptor != null) {
return methodInterceptor.intercept(this, CGLIB$clone$4$Method, CGLIB$emptyArgs, CGLIB$clone$4$Proxy);
}
return super.clone();
}
...
}
Affect(row-cnt:1) cost in 1375 ms.
由此看出跟JDK动态代理的原理也差不多,基本都是通过字节码织入来将代理对象的相关方法重写后调用intercept
或者invoke
方法来增强被代理对象原来的方法。区别也只是cglib多代理了finalize
和clone
方法。当然cglib也可以代理普通对象而不需要专门要求实现接口。
应用场景
当然一切技术脱离了实际的应用都是耍流氓。动态代理存在肯定有其意义。通过这个功能,我们可以实现RPC远程接口调用、Http请求调用以及方法日志调用等。如下面的案例:
public interface IApiService {
@FormUrlEncoded
@POST(LIVE_API+"/n/live/getEndSummary")
public QLivePushEndInfoResponse liveStopSummary(@Field("liveStreamId") String liveStreamId);
}
public class AppServiceHandler implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Class> proxyClass = method.getDeclaringClass();
// 如果调用的是从Object继承来的方法,则直接调用。
if (Object.class.equals(proxyClass)) {
return method.invoke(proxy, args);
}
//获取具体的方法
MethodInfo methodInfo = MethodHolder.getMethodInfo(proxyClass, method.getName());
if(methodInfo == null) {
throw new ProxyMethodNotFoundException(proxyClass , method);
}
try {
//本次的参数数据
String[] paramNames = methodInfo.getParamNames();
boolean[] paramMethods = methodInfo.getParamMethods();
ResponseStatus response = null;
//构造post参数
if(methodInfo.getType() == HttpMethod.POST) {
response = HttpUtils.post(paramNames);
} else {
response = HttpUtils.get(paramNames);
}
//解析结果
if(response == null || response.getStatusCode() != HttpStatus.SC_OK) {
throw new RuntimeException();
} else {
//记录日志
String body = response.getContent();
return JsonHelper.toObject(body, methodInfo.getReturnType());
}
} catch (HttpException | IOException e) {
throw new RuntimeException();
}
}
/**
* 获取目标对象的代理对象
* @param deviceManager - 设备管理对象
* @return T
*/
@SuppressWarnings("unchecked")
public static T newInstance(Class proxyInterface) {
//解析接口信息
MethodHolder.analyzeClass(proxyInterface);return (T)Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class>[]{proxyInterface}, new AppServiceHandler());
}
}
public class Test {
public static void main(String[] args) {
IApiService apiService = AppServiceHandler.newInstance(IApiService.class);
System.out.println(apiService.liveStopSummary("asdasd"));
}
}
这样就可以简单的实现一个Http的远程调用了。