一、前言
在我接触到spring框架的核心之一aop时,我就好奇aop这玩意怎么这么神奇呢!还能在不伤及原有代码筋骨的基础上,对它不留痕迹的增强。这中间我不断琢磨,查找相关资料,借助这么好的平台,我给大家分享一下我所理解的和我整理的-动态代理。
二、正文
1、静态代理
代理模式简单分为两种:动态代理和静态代理。要更清晰理解动态代理,我们先得从静态代理入手。
//定义交通工具类
public interface Vehicle {
//公共内容
void commonContent();
//这个方法回头再看
void m(String str);
}
//用车类实现交通工具
public class Car implements Vehicle {
//车名
String name;
public Car(String name) {
this.name = name;
}
@Override
public void commonContent() {
System.out.println(name+"行驶1000米");
}
//这个方法回头再看
@Override
public void m(String str) {
System.out.println(str);
}
}
//创建代理类
public class MyProxy implements Vehicle {
//被代理对象
private Vehicle vehicle;
public MyProxy(Vehicle vehicle) {
this.vehicle = vehicle;
}
@Override
public void commonContent() {
long start = System.currentTimeMillis();
System.out.println("起跑时间:" + start + "毫秒");
//调用公共内容
vehicle.commonContent();
long end = System.currentTimeMillis();
System.out.println("完成时间:" + end + "毫秒");
//耗时时间
System.out.println("消耗" + (end - start) + "毫秒");
}
//这个方法回头再看
@Override
public void m(String str) {
vehicle.m(str);
}
}
//测试类
public class Test {
public static void main(String[] args) {
//创建实例
Vehicle myProxy = new MyProxy(new Car("奔驰gls450"));
myProxy.commonContent();
System.out.println("================================================");
//这个方法回头再看
myProxy.m("mmmmmmmmmmm");
}
}
执行结果:
小结:这里,我们就使用MyProxy (代理类)对Car(被代理类)中的commonContent方法,实现了静态方式的增强。大家在这里,有没有感觉这种方式跟装饰者模式有点类似;还有,这里的例子中
对应的是aop的Before advice通知类型,
对应的是aop的After returning advice通知类型。
2.动态代理(JDK)
动态代理和静态代理的最大区别:静态代理在程序运行之前就已经编译完成;动态代理,代理类并不是在Java代码中定义的,而是在运行时根据我们在Java代码中的“指示”动态生成的。
带着这个结论,我们一起看看Java在java.lang.reflect包下,为我们提供的Proxy类和InvocationHandler接口。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* 先定义一个我的调用处理程序MyInvocationHandler,去实现java.lang.reflect包下
* 的接口InvocationHandler
*/
public class MyInvocationHandler<T> implements InvocationHandler {
//被代理类对象
T t;
public MyInvocationHandler(T t) {
this.t = t;
}
/**
* @param proxy 代理类对象
* @param method 被代理类中的被增强方法(可看成commonContent方法)
* @param args 传给被增强方法的参数
* @return 被增强方法有返回值则返回;被增强方法返回值为void则返回null,即result为null
* @throws Throwable 接住被增强方法可能抛出的异常
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long start = System.currentTimeMillis();
System.out.println("起跑时间:" + start + "毫秒");
//调用公共内容(commonContent)
Object result = method.invoke(t, args);
long end = System.currentTimeMillis();
System.out.println("完成时间:" + end + "毫秒");
//耗时时间
System.out.println("消耗" + (end - start) + "毫秒");
return result;
}
}
//测试类
public class Test {
public static void main(String[] args) {
//使用构造方法将被代理对象car传入
MyInvocationHandler<Vehicle> car = new MyInvocationHandler<>(new Car("奔驰gls450"));
//借助java.lang.reflect包下的Proxy类生成代理类和代理类对象
Vehicle vehicleProxy = (Vehicle) Proxy.newProxyInstance(Vehicle.class.getClassLoader(),
Car.class.getInterfaces(), car);
//调用代理类对象中的公共内容方法commonContent,这里的commonContent方法是增强之后的
vehicleProxy.commonContent();
System.out.println("===================================================");
vehicleProxy.m("mmmmmmmmmm");
}
}
执行结果:
这里,我们可以再回头看看m()方法了,对比上下两个结果,发现使用jdk动态代理,m()方法也给我们增强了,这也是静态与动态代理之间的一个小区别。这是怎么一回事?为了更加直观的看到,我们将代理类字节码文件输出后再反编译(XJad反编译工具,在我的码云-工具包)
public static void main(String[] args) {
//使用这句代码就能接收到代理类字节码byte数组
byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", Car.class.getInterfaces());
//指定输出位置
String path = "F:/$Proxy0.class";
//写入文件
try (FileOutputStream fos = new FileOutputStream(path)) {
fos.write(classFile);
fos.flush();
System.out.println("代理类class文件写入成功");
} catch (Exception e) {
System.out.println("写入文件错误");
}
}
反编译结果:
// Decompiled by Jad v1.5.8e2. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://kpdus.tripod.com/jad.html
// Decompiler options: packimports(3) fieldsfirst ansi space
import java.lang.reflect.*;
//生成的代理类
public final class $Proxy0 extends Proxy
implements Vehicle {
//被代理方法
private static Method m1;
private static Method m2;
private static Method m4;
private static Method m0;
private static Method m3;
//利用反射获取到方法
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{
Class.forName("java.lang.Object")
});
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m4 = Class.forName("Vehicle").getMethod("commonContent", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
m3 = Class.forName("Vehicle").getMethod("m", new Class[]{
Class.forName("java.lang.String")
});
} catch (NoSuchMethodException nosuchmethodexception) {
throw new NoSuchMethodError(nosuchmethodexception.getMessage());
} catch (ClassNotFoundException classnotfoundexception) {
throw new NoClassDefFoundError(classnotfoundexception.getMessage());
}
}
//利用构造方法给Proxy类中的protected InvocationHandler h;赋值
public $Proxy0(InvocationHandler invocationhandler) {
/**
* 看到这,再结合下面的super.h.invoke(this, m4, null)就能知道,
* 为何代理对象vehicleProxy调用方法都是在执行InvocationHandler中的invoke方法,
* 也就解释了为何Vehicle中m()方法也被增强了。
* 而MyInvocationHandler持有一个被代理对象的实例(T t;),
* 这样就完成了对被代理对象的整个代理过程。
*/
super(invocationhandler);
}
//代理方法
public final void commonContent() {
try {
//调用InvocationHandler中的invoke方法
//不难看出InvocationHandler被当作了一个中转站
super.h.invoke(this, m4, null);
return;
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
//代理方法
public final void m(String s) {
try {
super.h.invoke(this, m3, new Object[]{
s
});
return;
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
//代理方法
public final boolean equals(Object obj) {
try {
return ((Boolean) super.h.invoke(this, m1, new Object[]{
obj
})).booleanValue();
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
//代理方法
public final String toString() {
try {
return (String) super.h.invoke(this, m2, null);
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
//代理方法
public final int hashCode() {
try {
return ((Integer) super.h.invoke(this, m0, null)).intValue();
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
}
小结:jdk动态代理为我们的生成了一个叫$Proxy0(这个名字后面的0是编号,有多个代理类会依次递增)的代理类,这个代理类文件存放在内存中,我们在创建代理对象时,就是通过反射获得这个代理类的构造方法,然后创建的代理实例。这也就是Proxy.newProxyInstance为我们做的所有事。
如果将
改为
生成的代理类是这样的
这也是jdk动态代理(代理接口)和cglib动态代理(代理父类)最明显的区别。理解过程中,可以认为jdk动态代理就是通过代码操作了模板而产生的。
三、结尾
感谢这篇文章Java动态代理实现与原理详细分析,对我的帮助。如果你有不同的理解,欢迎留言。