要讲解动态代理,就要先说代理模式,这是一种最常用的设计模式。该模式中有以下三个角色。
抽象角色:通过接口或抽象类声明真实角色实现的业务方法,即被代理类与代理类都要实现的接口。
代理角色:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。
真实角色:实现抽象角色,定义真实所要实现的业务逻辑,供代理角色调用。
静态代理需要为每个被代理类(真实角色)都创建特定的代理类(代理角色),这些类需要程序员自己编写,这样会带来极大的不便。
因此出现了动态代理,动态代理是基于JDK的反射机制。
假如我们现在要实现一个日志增强类,在各种类调用各种方法前和方法执行结束后打印日志。LogHandler实现InvocationHandler,并且需要自己实现invoke方法,该方法中需要执行日志打印语句和原本的业务逻辑方法。
public class LogHandler implements InvocationHandler{
private Object target;
public LogHandler(Object target) {
this.target = target;
}
//生成代理对象
public Object bind(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("log before");//增强日志语句
method.invoke(target, args);//调用被代理类的业务逻辑方法
System.out.println("log after");//增强日志语句
return null;
}
}
假如现在我们有一个User接口及其实现类UserImpl。
public interface User {
void hello();
}
public class UserImpl implements User{
public void hello(){
System.out.println("hello");
}
}
要通过LogHandler类来获得UserImpl的日志代理类,怎么办呢?
public class Main {
public static void main(String[] args) {
UserImpl user = new UserImpl();
LogHandler logHandler = new LogHandler(user);
User proxy = (User) logHandler.bind();
proxy.hello();
}
}
关于到底是如何生成代理类的,我们要仔细看一下bind方法中的Proxy.newProxyInstance(......)方法,这个方法里面有一切我们想知道的玄机。我们来看一下源代码中最核心的一些东西(一些关于安全性合法性检查的语句就不说了)
Class> cl = getProxyClass0(loader, intfs);
参数里的loader指的是被代理的类加载器,intfs指的是被代理类所实现的接口集合,getProxyClass0方法根据这两个参数构造了一个类对象,该类继承Proxy类(因此JDK动态代理只支持接口代理!),实现了被代理类所实现的接口。
接着进入getProxyClass0方法
private static Class> getProxyClass0(ClassLoader loader,Class>... interfaces) {
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
// If the proxy class defined by the given loader implementing
// the given interfaces exists, this will simply return the cached copy;
// otherwise, it will create the proxy class via the ProxyClassFactory
return proxyClassCache.get(loader, interfaces);
}
这里可以看得很清楚,如果再之前已经生成过“拥有相同接口集合的类”的代理类,则可以直接从缓存中取出该代理类对象,否则通过代理类工厂创建。在获取到代理类之后,就要创建代理对象了,通过Java的反射机制,直接通过Contructor类的newInstance方法创建并且返回。
return cons.newInstance(new Object[]{h});
这里new Object[]{h}是构造器参数,因为我们的代理类是继承Proxy的,而Proxy提供了一个受保护的构造器和一个私有构造器,我们来看一下受保护的构造器。
protected Proxy(InvocationHandler h) {
Objects.requireNonNull(h);
this.h = h;
}
其中的参数正是InvocationHandler,因此其子类代理类一定会有一个有参构造器,且有传入InvocationHandler对象。为什么要传入这个参数呢?因为Proxy有个属性就是InvocationHandler,这个属性有什么用呢?我们来看一下生成的代理类的代码(来自网络)。
public final class $Proxy0 extends Proxy implements UserManager {
private static Method m1;
private static Method m0;
private static Method m3;
private static Method m2;
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals",
new Class[] { Class.forName("java.lang.Object") });
m0 = Class.forName("java.lang.Object").getMethod("hashCode",
new Class[0]);
m3 = Class.forName("cn.edu.jlu.proxy.UserManager").getMethod("addUser",
new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString",
new Class[0]);
} catch (NoSuchMethodException nosuchmethodexception) {
throw new NoSuchMethodError(nosuchmethodexception.getMessage());
} catch (ClassNotFoundException classnotfoundexception) {
throw new NoClassDefFoundError(classnotfoundexception.getMessage());
}
}
public $Proxy0(InvocationHandler invocationhandler) {
super(invocationhandler);
}
@Override
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);
}
}
@Override
public final int hashCode() {
try {
return ((Integer) super.h.invoke(this, m0, null)).intValue();
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
@Override
public final String toString() {
try {
return (String) super.h.invoke(this, m2, null);
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
@Override
public void addUser() {
try {
super.h.invoke(this, m3, null);
return;
} catch (Error e) {
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
}
可以看到,所有的方法都会调用super.h.invoke方法!因此,当我们调用代理类的很多方法时,其实都是指向了invoke方法。