前言:
Java中常见的两种代理方式:静态代理和动态代理。那什么是代理模式呢,代理模式就是给某一个对象提供一个代理,并由代理对象来控制对真实对象的访问,代理模式是一种结构型的设计模式。 而代理模式按照职责来进行分类,可以分为:
- 动态代理
- 虚拟代理
- Copy-on-Write 代理
- 保护代理
- Cache代理
- 防火墙代理
- 同步化代理
- 只能引用代理
而根据字节码创建时机来进行分类,可以分为静态代理和动态代理:
- 静态就是在程序运行钱就已经存在代理类的字节码文件,代理类和真实主题角色的关系在运行前就决定了。
- 动态代理的源码是在程序运行期间有jvm根据反射等机制动态生成,所处在运行钱并不存在代理类的字节码文件。
从一个例子开始:
// 接口函数 也就是上图中的Subject
public interface Test {
int add(int a,int b); // 上图中的doaction
}
// 接口的实现类,也就是上图中的 RealSubject
static class TestDemo implements Test{
@Override
public int add(int a, int b) {
return a+b;
}
}
// 对应的handler
static class TestHandler implements InvocationHandler {
Object target;
public TestHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before...");
Object object = method.invoke(target,args);
System.out.println("after...");
return object;
}
}
public static void main(String[] args) {
// 创建实现类 realsubject
TestDemo demo = new TestDemo();
// 创建handler
TestHandler handler = new TestHandler(demo);
//加上这句将会产生一个$Proxy0.class文件,这个文件即为动态生成的代理类文件
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
// 创建对应的proxy
Test proxy = (Test) Proxy.newProxyInstance(demo.getClass().getClassLoader(), demo.getClass().getInterfaces(), handler);
// 调用proxy的接口方法。
System.out.println(proxy.add(1,2));
}
运行结果:
before...
after...
3
Process finished with exit code 0
带着问题去看整个过程:
- invoke是在什么时候调用的?
- Proxy类必然是一个实现了接口的类,而这个类在哪儿呢?
关键函数newProxyInstance
// Proxy.java
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h) {
/*
* Look up or generate the designated proxy class.
*/
Class<?> cl = getProxyClass0(loader, intfs);
return cons.newInstance(new Object[]{h});
}
上面我们只保留了关键的代码,首先创建了一个class,然后通过这个class的一参构造函数,创建了一个proxy对象返回了回去,但是还没有告诉我们具体invoke是什么时候调用的,以及这个类是怎么来的。我们接着看getProxyClass0
getProxyClass0
/**
* Generate a proxy class. Must call the checkProxyAccess method
* to perform permission checks before calling this.
*/
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);
}
/**
* a cache of proxy classes
*/
private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
这下好了,从注释中我们就可以看到,是创建了一个proxy类,但是需要查看是否超过65535这个上限。再往下看,是通过weakcache来获取到对应的class。此处的weakcache,我们也可以看到是传入了两个工厂类。至于其中的weakcache如何获取到value,不是本篇需要讨论的内容。只要知道是通过后面的factory也就是ProxyClassFactory进行创建出来的就可以,我们查看factory的代码:
@Override
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
/*
* Generate the specified proxy class.
*/
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
}
其中的关键函数就是创建对应的proxy字节码,创建的过程不表,我们来看一下对应的创建出来的字节码:
public final class $Proxy0 extends Proxy implements Test {
private static Method m1;
private static Method m2;
private static Method m3;
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 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 add(int var1, int var2) throws {
try {
return (Integer)super.h.invoke(this, m3, new Object[]{var1, var2});
} catch (RuntimeException | Error var4) {
throw var4;
} catch (Throwable var5) {
throw new UndeclaredThrowableException(var5);
}
}
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.company.Test").getMethod("add", Integer.TYPE, Integer.TYPE);
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
可以看到,确实是实现了test的接口,同时,构造函数也有一个是接受InvocationHandler作为参数,传给了super的。
所以此时我们就解决了第二个问题:Proxy的创建是在获取weakcache的时候进行创建的,如果没有,就通过接口,动态的创建一个proxy类,同时将handler传入。
那么此时再查看其中的函数,可以发现,其实都是调用的handler的invoke方法,那么其实也就解决了第一个问题:就是在创建的Proxy中,所有的方法都是通过调用handler的invoke方法来实现的。
这也就是为什么handler中需要一个具体的接口实现的原因。
总结:
在newProxyInstance的时候,动态的创建了proxy类来实现接口,而proxy中的所有方法同时通过传入的invocationhandler的invoke方法来实现具体功能。而此时其实并没有具体的接口实现类,所以我们需要在handler中接受一个具体的接口实现,在invoke中通过调用具体的实现来完成功能。
通过这样的解析,我们也就知道了:
- 动态代理的效率比较低,因为在创建的时候需要生成字节码,同时运用的是反射调用
- invoke的调用是在proxy的生成类中每个方法中进行调用的
- proxy的生成是在程序运行的时候进行生成的