前言
动态代理几乎是面试必问题,因为Spring AOP的实现原理之一就是动态代理,此外,动态代理相较其他设计模式而言,它还比较特殊,动态代理需要动态生成一个类,而且它还有两种动态生成类的实现方式;
示例
先写一个JDK动态代理,一个演员需要有经纪人,我们把演员当成被代理类,经纪人当成代理类;
演员类Actor.java:
package com.lw.designpattern.proxy.dynamic;
public class Actor {
public void act() {
System.out.println("某毯星戛纳走红毯");
}
}
调用处理类ActorInvocation.java,为什么要设计这么一个类,后面讲
package com.lw.designpattern.proxy.dynamic;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class ActorInvocation<T> implements InvocationHandler {
T target;
public ActorInvocation(T target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("经纪人准备");
method.invoke(this.target, args);
System.out.println("经纪人善后");
return null;
}
}
测试类:
package com.lw.designpattern.proxy.dynamic;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class DynamicProxyTest {
public static void main(String[] args) {
Actor actor = new Actor();
InvocationHandler actorHandler = new ActorInvocation<Actor>(actor);
Actor actorProxy = (Actor) Proxy.newProxyInstance(Actor.class.getClassLoader(), new Class<?>[] { Actor.class }, actorHandler);
actorProxy.act();
}
}
测试结果:
Exception in thread "main" java.lang.IllegalArgumentException: com.lw.designpattern.proxy.dynamic.Actor is not an interface
at java.lang.reflect.Proxy$ProxyClassFactory.apply(Proxy.java:590)
at java.lang.reflect.Proxy$ProxyClassFactory.apply(Proxy.java:557)
at java.lang.reflect.WeakCache$Factory.get(WeakCache.java:230)
at java.lang.reflect.WeakCache.get(WeakCache.java:127)
at java.lang.reflect.Proxy.getProxyClass0(Proxy.java:419)
at java.lang.reflect.Proxy.newProxyInstance(Proxy.java:719)
at com.lw.designpattern.proxy.dynamic.DynamicProxyTest.main(DynamicProxyTest.java:12)
咦,报错了。。。。提示Actor类不是一个接口,原因是啥,暂且按下不表,先改成接口再说
package com.lw.designpattern.proxy.dynamic;
public interface Actor {
public void act();
}
增加实现类:
package com.lw.designpattern.proxy.dynamic;
public class BBFan implements Actor {
@Override
public void act() {
System.out.println("某毯星戛纳走红毯");
}
}
测试结果:
经纪人准备
某毯星戛纳走红毯
经纪人善后
OK,搞定,下面讲讲实现原理
源码刨析
在说原理之前,先搞清楚概念,所谓的动态,其实就是动态生成一个代理类Class,相比较静态代理模式的手写代理类,动态代理在这方面可谓是更加灵活。下面先看看生成的代理类是个什么样子。
代理类我是直接通过ProxyGenerator类生成的,这也是JDK源码使用的类,所以生成的代理类中没有包含自定义的代理功能。
当然也可以在测试方法中加上这句:
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
byte[] proxyClassFile = ProxyGenerator.generateProxyClass("ActorProxy", new Class[]{Actor.class});
try {
FileOutputStream f = new FileOutputStream("ActorProxy.class");
f.write(proxyClassFile);
f.flush();
f.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
生成代理类class的反编译源码:
import com.lw.pattern.proxy.dynamic.Actor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class ActorProxy extends Proxy implements Actor {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
public ActorProxy(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 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 void act() throws {
try {
// 这边通过父类的h属性调用实际业务方法
super.h.invoke(this, m3, (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"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("com.lw.pattern.proxy.dynamic.Actor").getMethod("act");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
从这个源码我们来解释一下上面遗留的两个问题:
1,代理类继承了JDK的Proxy Class,因为Java规范设计一个类只能继承一个父类,且新代理类与被代理类属于兄弟关系,所以只能通过接口来标识两者的关系,这也就解释了为什么JDK的动态代理要求被代理类一定要实现接口。
2,Invocation的设计是为了动态代理的调用,我们可以在Proxy类中找到关于Invocation的属性定义:
/**
* the invocation handler for this proxy instance.
* @serial
*/
protected InvocationHandler h;
有了这个实例变量的定义,我们就可以把这个Invocation看作是动态代理的模板方法,这样的话,每new一个新的被代理类,就不需要像静态动态代理那样修改代理类。
看到这里,就会发现,动态代理真的没有什么高深的东西,本质上同静态代理的实现思路也一致。
CGLib动态代理
CGlib 动态代理模式的实现原理是对代理目标类生成一个子类,并覆盖其中方法实现增强,因为底层是基于创建被代理类的一个子类,所以它避免了JDK动态代理类必须要有接口的缺陷。同时,由于是继承方式,如果是 static方法,private方法,final方法等描述的方法是不能被代理的,cglib动态代理底层是借助asm字节码技术;