文章目录
动态代理的一个小实验【有助于理解动态代理的原理】
接口
package com.pig.spring.aop.proxy;
public interface Vehicle {
public void run();
}
接口实现类
package com.pig.spring.aop.proxy;
public class Car implements Vehicle {
@Override
public void run() {
System.out.println("汽车在公路上行驶");
}
}
单独写的一个类 VehicleInvocationHandler
该类实现了 InvocationHandler 接口,相当于VehicleInvocationHandler 就是 InvocationHandler 的子类,故在调用Proxy.newInstance() 可以传入该类的对象。
package com.pig.spring.aop.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class VehicleInvocationHandler<T> implements InvocationHandler {
/**
* 传入的目标对象
*/
private Vehicle target;
public VehicleInvocationHandler(T t) {
this.target = (Vehicle) t;
}
public VehicleInvocationHandler() {
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 在调用被代理的方法前,进行某些处理
System.out.println("交通工具启动了");
// 通过反射调用被代理的方法 ==> invoke() 传入目标对象(即被代理的对象),args 为参数列表
Object invokeResult = method.invoke(target, args);
// 在调用被代理的方法后,进行某些处理
System.out.println("交通工具停止了");
return invokeResult;
}
}
测试类
@Test
public void testVehicleInvocationHandler() throws Throwable {
VehicleInvocationHandler<Car> invocationHandler = new VehicleInvocationHandler<>(new Car());
Vehicle proxyInstance = (Vehicle)Proxy.newProxyInstance(VehicleTest.class.getClassLoader(),
new Class[]{Vehicle.class}, invocationHandler);
proxyInstance.run();
System.out.println("************************************************");
invocationHandler.invoke(proxyInstance, Car.class.getDeclaredMethod("run"), null);
}
输出结果
思考
为什么会输出两次同样的结果呢?
这说明:
proxyInstance.run();
和invocationHandler.invoke(proxyInstance, Car.class.getDeclaredMethod("run"), null);
的输出是完全一样的。
也就是说,proxyInstance
这个代理对象调用run()
方法,最终还是会调用invocationHandler.invoke
这个方法,而invoke()
这个方法的内容,正是我们实现了InvocationHandler
,重写的invoke()
方法呀!!
再进一步思考
为什么proxyInstance
调用run()
方法,最终会执行到invocationHandler.invoke()
那里去了呢?
探索
网上有很多通过生成代理类的.class 文件的方法。这里介绍一种最简单的
-
编辑运行的设置
-
将VM options 的值改为
-Dsun.misc.ProxyGenerator.saveGeneratedFiles=true
-
编译运行后,在项目根目录下会生成
$Proxy0
文件
代理类经过IDEA的反编译后,如下
public final class P r o x y 0 e x t e n d s P r o x y i m p l e m e n t s S m a r t A n i m a l = = > 说 明 这 个 代 理 类 的 名 字 是 ‘ Proxy0 extends Proxy implements SmartAnimal ==> 说明这个代理类的名字是` Proxy0extendsProxyimplementsSmartAnimal==>说明这个代理类的名字是‘Proxy0
,注意,前面的$ 不是内部类哦!!!代理类名字前面就是加了一个$!
P r o x y 0 ‘ 继 承 了 P r o x y 类 , 实 现 了 V e h i c l e 接 口 ! ! ! ! 所 以 我 们 执 行 ‘ p r o x y I n s t a n c e . r u n ( ) ‘ 的 时 候 , 会 动 态 绑 定 到 ‘ Proxy0` 继承了 Proxy类,实现了 Vehicle 接口!!!! 所以我们执行`proxyInstance.run()` 的时候,会动态绑定到` Proxy0‘继承了Proxy类,实现了Vehicle接口!!!!所以我们执行‘proxyInstance.run()‘的时候,会动态绑定到‘Proxy0` 的run() 方法!!!这就是动态代理的精髓所在!!
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package com.sun.proxy;
import com.pig.spring.aop.proxy.Vehicle;
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 Vehicle {
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 run() throws {
try {
// 重点在这!!!!!!!!!!!!!!!!!
// m3 = Class.forName("com.pig.spring.aop.proxy.Vehicle").getMethod("run");
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.pig.spring.aop.proxy.Vehicle").getMethod("run");
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());
}
}
}
梳理一下逻辑
/* 我们通过下面获取到的 proxyInstance ,它的运行类型其实是$Proxy0!! 只不过我们在这里用Vehicle 这个接口类型来接受罢了,但是这样不利于分析底层原理*/
Vehicle proxyInstance = (Vehicle)Proxy.newProxyInstance(VehicleTest.class.getClassLoader(),
new Class[]{Vehicle.class}, invocationHandler);
/* 如果写成下面这样,我想你会理解得更彻底!但是,之所以用接口类型来接收proxyInstance,我个人得看法是:
第一,$Proxy0 是在运行时底层生成得一个临时得代理类,我们在编译阶段没办法获取到,或者说我们编写代码的时候,根本就不存在这个类。另外,它有可能不叫$Proxy0,而是叫其他名字,比如$Proxy7...;
第二,由于动态绑定机制的存在,即运行时动态绑定到运行类型,又$Proxy0 实现了Vehicle接口,所以我用Vehicle 接收代理对象,是可以的,并且运行时动态绑定到$Proxy0 的run() 方法,妙哉!
第三,多态 + 反射 + 动态绑定 妙哉妙哉*/
$Proxy0 proxyInstance = ($Proxy0)Proxy.newProxyInstance(VehicleTest.class.getClassLoader(),
new Class[]{Vehicle.class}, invocationHandler);
-
看看
Proxy.newProxyInstance(classLoader, interfaces, invocationHandler)
这个方法到底发生了什么?解读:我们传入 三个参数:
classLoader, interfaces, invocationHandler
,分别进行了相应的处理final Class<?>[] intfs = interfaces.clone();
Class<?> cl = getProxyClass0(loader, intfs);
final InvocationHandler ih = h;
Proxy 类里面定义了
protected InvocationHandler h;
这个属性,其实之后我们写的InvocationHandler 的实现子类VehicleInvocationHandler
会传给这个属性/** * the invocation handler for this proxy instance. * @serial */ protected InvocationHandler h;
下面这张图是debug newInstance()
的结果,我们可以看到,Proxy类的 InvocationHandler h
这个属性值正是我们的VehicleInvocationHandler
这是newProxyInstance() 方法的源码
public 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);
}
}
摘取$Proxy0 关键部分进行分析
- 可以看出,m3 就是我们通过反射获得的接口方法的Method 对象
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m0;
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m3 = Class.forName("com.pig.spring.aop.proxy.Vehicle").getMethod("run");
m2 = Class.forName("java.lang.Object").getMethod("toString");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
-
super.h.invoke(this, m3, (Object[])null);
方法解读// 重点在这!!!!!!!!!!!!!!!!!!!!!!! public final void run() throws { try { // 重点在这!!!!!!!!!!!!!!!!! // m3 = Class.forName("com.pig.spring.aop.proxy.Vehicle").getMethod("run"); super.h.invoke(this, m3, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } }
- super 指的是Proxy类,因为代理对象
$Proxy0
继承了 Proxy 类 - super.h 的h 正好是Proxy 类定义的
protected InvocationHandler h
这个属性,此时,这个h 传入的是我们的VehicleInvocationHandler
- invoke() 方法,就是我们
VehicleInvocationHandler
中重写的invoke() 方法!@Override public Object invoke(Object proxy, Method method, Object[] args)
- 传入的参数 this,正是$Proxy 代理对象本身!
- 传入的 method,此处是m3,正好是接口方法的Method 对象,即要被代理的方法对象
- 传入的args,是接口方法的参数
到此,为什么
@Override public Object invoke(Object proxy, Method method, Object[] args)
,为什么这个invoke() 方法的第一个参数传入的是代理对象$Proxy,应该懂了吧!!到此,我们也解答了:为什么
proxyInstance
调用run()
方法,最终会执行到invocationHandler.invoke()
那里去这个疑惑。底层使用了多态,动态代理,反射等技术! - super 指的是Proxy类,因为代理对象
总结一下
@Test
public void testVehicleInvocationHandler() throws Throwable {
VehicleInvocationHandler<Car> invocationHandler = new VehicleInvocationHandler<>(new Car());
Vehicle proxyInstance = (Vehicle)Proxy.newProxyInstance(VehicleTest.class.getClassLoader(),
new Class[]{Vehicle.class}, invocationHandler);
proxyInstance.run();
System.out.println("************************************************");
invocationHandler.invoke(proxyInstance, Car.class.getDeclaredMethod("run"), null);
}
proxyInstance.run()
最终是执行到了 super.h.invoke(this, m3, (Object[])null)
这个方法!而super.h.invoke(this, m3, (Object[])null)
执行的就是我们重写的invoke() 方法:invocationHandler.invoke(proxyInstance, Car.class.getDeclaredMethod("run"), null);
所以输出的结果都一样。
因为我们懂了动态代理的原理,所以我们敢直接这么用invocationHandler.invoke(proxyInstance, Car.class.getDeclaredMethod("run"), null)
,并且正确输出。这只是一个实验而已,实际开发中,用proxyInstance.run()
就行了!我们这里只不过是将底层封装的方法,提上来直接调用而已啦!