动态代理
动态代理的时候,定义一个接口,需要代理人和被代理类实现这个接口,这样不够灵活,代理类能够代理的类只有实现这个接口的类。非常不灵活,假如被代理人的类没有实现这个接口,那么就需重新写一个代理类。对于日志、事务这些操作是不区分业务的,即不需要规定都实现某接口。因此,出现了动态代理
java种的动态代理生成的方式大概有三种JDK动态代理、instrument动态代理、cglib动态代理。其中,前两种是JDK自带的,cglib是需要第三方依赖使用的。
- JDK动态代理:在程序运行的过程种动态的生成代理类,这个代理类实现被代理类的接口,因此使用时候被代理类必须实现接口;
- instrument动态代理:是通过Class字节文件在加载至内存的过程种添加拦截器,动态的修改字节文件实现的;
- cglib:在程序运行的过程种动态生成代理类,这个代理类是通过继承被代理类实现的,因此不能代理被final修饰的类
JDK动态代理和cglib动态代理的底层都是ASM;
JDK动态代理
先看这样一个程序
/** 接口*/
public interface Mobile {
void move();
}
/** 被代理类*/
public class Car implements Mobile{
@Override
public void move() {
System.out.println("move...");
}
}
/** 使用动态代理*/
public class Test {
public static void main(String[] args) {
Car car = new Car();
System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles",true);
Mobile proxyInstance = (Mobile) Proxy.newProxyInstance(Car.class.getClassLoader(), Car.class.getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long before = System.currentTimeMillis();
System.out.println(method.getName()+"方法被执行前。。。");
Object invoke = method.invoke(car, args);
System.out.println(method.getName()+"方法被执行后。。。");
long after = System.currentTimeMillis();
System.out.println("耗时:" + (after - before));
return null;
}
});
proxyInstance.move();
}
}
创建代理类需要调用Proxy.newProxyInstance()方法,其中有三个参数
- 第一个参数:一个类加载器,一般使用被代理类的类加载器
- 第二个参数:被代理类实现的接口
- 第三个参数:一个InvocationHandler接口的实现类
可以把动态代理的创建改成这样:自己写一个类实现InvocationHandler。
public class Test {
public static void main(String[] args) {
Car car = new Car();
System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");
Mobile proxyInstance = (Mobile) Proxy.newProxyInstance(Car.class.getClassLoader(), Car.class.getInterfaces(), new MyInvocationHandler(car));
proxyInstance.move();
}
}
class MyInvocationHandler implements InvocationHandler{
private Car car;
public MyInvocationHandler(Car car) {
this.car = car;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long before = System.currentTimeMillis();
System.out.println(method.getName()+"方法被执行前。。。");
Object invoke = method.invoke(car, args);
System.out.println(method.getName()+"方法被执行后。。。");
long after = System.currentTimeMillis();
System.out.println("耗时:" + (after - before));
return null;
}
}
要想搞懂JDK动态代理必须查看动态生成代理类,通过设置JDK动态代理生成的类是否保存的一个属性将生成的代理类保存下来,通过在程序启动前加上:
System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles",true);
代理类的调用过程
生成的代理类
public final class $Proxy0 extends Proxy implements Mobile {
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 move() throws {
try {
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.shaoby.basic.proxy.JdkProxy.Mobile").getMethod("move");
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());
}
}
}
- 可以看到代理类继承了Proxy,并实现了我们定义的Mobile接口
- 构造方法传入InvocationHandler类型,并赋值给父类Proxy中的h参数,可以猜测这个就在获取代理类时候的第三个参数;
- 代理类中通过反射生成了四个方法,除了Object中的equals、toString、hashCode外另外一个就是我们要代理的方法move;
- 在move方法中调用了 super.h.invoke(this, m3, (Object[])null),即MyInvocationHandler中的invoke方法。
- 因此在调用代理类的invoke方法时,调用的就是MyInvocationHandler中的invoke方法。
Method类
这个类是一个方法的类,说白了就是方法的模板,在这个类中有一个方法invoke,传入两个参数,一个是调用方法的对象,另一个是方法的入参
InvocationHandler中的invoke方法
可以看到在代理类中invoke方法的调用为 super.h.invoke(this, m3, (Object[])null);
MyInvocationHandler中的invoke方法:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long before = System.currentTimeMillis();
System.out.println(method.getName()+"方法被执行前。。。");
Object invoke = method.invoke(car, args);
System.out.println(method.getName()+"方法被执行后。。。");
long after = System.currentTimeMillis();
System.out.println("耗时:" + (after - before));
return null;
}
参数:
- 第一个参数:this,即代理类本身
- 第二个参数:m3,即move方法
- 第三个参数:第二个方法的入参
返回值:
这个返回值其实是被代理方法的返回值,如果没有返回值就返回null;
这里最重要的是method.invoke(car, args),就是用car对象调用move方法。
ASM
在上述分析中,动态代理类的构造方法中参数是我们猜测的。其实这里是用ASM实现的。在生成代理类时调用的Proxy.newInstance()传入了InvocationHandler接口的实现类,Proxy.neInstance()会动态的生成代理类,并产生一个代理对象。这里后续再研究。
ASM是Java字节码操控框架,它能够以二进制形式修改已有类,或动态生成类,ASM 可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为。ASM 从类文件中读入信息后,能够改变类行为,分析类信息,甚至能够根据用户要求生成新类。
cglib动态代理
public class Test {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Plane.class);
enhancer.setCallback(new MyMethodInterceptor());
Plane o = (Plane)enhancer.create();
o.move();
}
}
class MyMethodInterceptor implements MethodInterceptor{
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
long before = System.currentTimeMillis();
System.out.println(method.getName()+"方法被执行前。。。");
Object result = methodProxy.invokeSuper(o, objects);
System.out.println(method.getName()+"方法被执行后。。。");
long after = System.currentTimeMillis();
System.out.println("耗时:" + (after - before));
return result;
}
}
cglib动态代理的原理是:
- 指定代理类的父类
- 生成代理类
- 调用代理类父类的method