Java 动态代理及源码简析
Android 中的 Retrofit 框架以及 Spring AOP 的内部实现都是基于 Java 动态代理机制来实现的,所以理解 Java 动态代理机制有助于我们阅读框架源码以及理解其实现思路。
Java 有两种代理方式,一种是静态代理,另外一种就是本文所介绍的动态代理。
静态代理
静态代理比较简单,静态代理代理的是一个类,代理类和委托类(被代理类)都实现相同的接口,然后代理类内部持有委托类的引用,客户端只需要与代理类打交道,客户端调用代理类的方法时,代理类内部再调用委托类的方法,但在调用委托类方法的前后,代理类可以插入自己的代码,执行自己的逻辑,下面是一个静态代理的简单代码演示。
public interface IAnimal {
void say(String word);
}
public class Cat implements IAnimal {
@Override
public void say(String word) {
System.out.println("Cat: say()-->" + word);
}
}
public class StaticAnimalProxy implements IAnimal{
private IAnimal animal;
public StaticAnimalProxy(IAnimal animal) {
this.animal = animal;
}
@Override
public void say(String word) {
System.out.println("静态代理前置代码");
animal.say(word);
System.out.println("静态代理后置代码");
}
}
StaticAnimalProxy proxy = new StaticAnimalProxy(new Cat());
proxy.say("hello!");
输出:
静态代理前置代码
Cat: say()-->hello!
静态代理后置代码
动态代理
上面说到静态代理代理的是一个类,动态代理代理的是一个接口,要想实现动态代理,必须要实现 InvocationHandler
这个接口,这个实现类可以理解为是一个调用处理器 (Handler),通过这个 InvocationHandler
的实现类和 Proxy
类的 newProxyInstance()
方法可以动态生成任何接口的代理类,下面是一个实现动态代理的例子。
public class DynamicProxyHandler implements InvocationHandler {
// 被代理的对象
private Object target;
public DynamicProxyHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object object, Method method, Object[] args) throws Throwable {
System.out.println("动态代理前置代码");
// 这里真正调用被代理对象的方法,返回结果是方法的返回值,在这行代码前后可以实现额外的逻辑,实现代理功能。
Object result = method.invoke(target, args);
System.out.println("动态代理后置代码");
return result;
}
}
// 被代理的对象 cat
Cat cat = new Cat();
// 创建调用处理器,将 cat 作为参数传递进去,让处理器持有被代理对象 cat 的引用
DynamicProxyHandler proxyHandler = new DynamicProxyHandler(cat);
// 因为 Cat 实现了 IAnimal 接口,所以这里生成动态生成一个 IAnimal 的代理类
IAnimal animal = (IAnimal) Proxy.newProxyInstance(IAnimal.class.getClassLoader(), new Class[]{IAnimal.class}, proxyHandler);
animal.say("hello!");
// 返回的 animal 实际上不是真正的 animal,而是 animal 的代理类 com.sun.proxy.$Proxy0
System.out.println(animal.getClass().getCanonicalName());
动态代理前置代码
Cat: say()-->hello!
动态代理后置代码
com.sun.proxy.$Proxy0
静态代理和动态代理的比较
-
静态代理代理的是一个类,动态代理的是接口。
-
静态代理类在程序运行之前代理类 (.class) 就存在了,而动态代理是在程序运行时通过反射机制动态生成代理类。
StaticAnimalProxy 只能代理 IAnimal 的实现类。
DynamicProxyHandler 内部代理的是一个 Object,
Proxy.newProxyInstance
方法可以为任一接口代理生成代理类,上面的例子代理的是 IAnimal 的代理类,当然也可以代理其他接口。 -
相较于静态代理,动态代理的优点是实现无侵入式的代码扩展,可以在不修改源码的情况下去增强一些方法,在方法的前后做自己想做的事情,此外也还可以减少代码量,若使用静态代理,如果类的方法比较多的时候,需要手写大量的代码,而动态代理只需要在
InvocationHandler
的invoke()
方法中处理即可。 -
Java 的动态代理只能代理接口,若要动态代理类需要使用
CGLIB
库。
动态代理源码简析
从上面可以看到动态代理类是由 Proxy.newProxyInstance()
方法动态生成的,所以我们只需要从看这个方法就行了。
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException {
Objects.requireNonNull(h);
...
/*
* Look up or generate the designated proxy class.
* 生成代理类 Class 对象
*/
Class<?> cl = getProxyClass0(loader, intfs);
/*
* Invoke its constructor with the designated invocation handler.
* 反射获取构造器生成代理类实例对象返回给调用者
*/
try {
...
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
...
return cons.newInstance(new Object[]{h});
} catch (IllegalAccessException | InstantiationException e) {
throw new InternalError(e.toString(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString(), t);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
}
再主要看 getProxyClass0()
方法是如何生成代理类 Class 对象的。
/**
* 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);
}
proxyClassCache
缓存了代理类对象,方便复用,proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
proxyClassCache
是一个 WeakCache
对象,其构造器的 ProxyClassFactory
对象很重要,看名字就知道了,这个类就是专门来生成代理类的,上面的 proxyClassCache.get()
方法最终也是调用了其 apply()
方法来生成并返回的代理类,所以下面来看 ProxyClassFactory
的 apply()
方法的内部实现。
@Override
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
for (Class<?> intf : interfaces) {
/*
* Verify that the class loader resolves the name of this
* interface to the same Class object.
* 验证 Proxy.newProxyInstance() 传寄来的 classLoader 和 interfaces 是否有效
*/
Class<?> interfaceClass = null;
try {
interfaceClass = Class.forName(intf.getName(), false, loader);
} catch (ClassNotFoundException e) {
}
if (interfaceClass != intf) {
throw new IllegalArgumentException(
intf + " is not visible from class loader");
}
/*
* Verify that the Class object actually represents an
* interface.
* 这里为了验证要代理的是否为接口,若不是接口则会抛出异常
*/
if (!interfaceClass.isInterface()) {
throw new IllegalArgumentException(
interfaceClass.getName() + " is not an interface");
}
...
}
...
/*
* Choose a name for the proxy class to generate.
* 为代理类生成一个名字,这里 proxyClassNamePrefix 值为 "$Proxy" num 从 0 开始,
* 所以这里解释了为什么生成出来的代理类名字时 $Proxy0
*/
long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg + proxyClassNamePrefix + num;
/*
* Generate the specified proxy class.
* 最终生成代理类 class 二级制文件,并存储来了本地,也就是运行时动态生成出了 .class 文件
*/
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try {
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
/*
* A ClassFormatError here means that (barring bugs in the
* proxy class generation code) there was some other
* invalid aspect of the arguments supplied to the proxy
* class creation (such as virtual machine limitations
* exceeded).
*/
throw new IllegalArgumentException(e.toString());
}
}
当然我们也可以自己手动调用 ProxyGenerator.generateProxyClass()
方法来生成 .class
文件,然后反编译查看里面的内容,内部其实生成了接口的各个方法,然后再各个方法内部调用了 h (InvocationHandler)
的 invoke(Object proxy, Method method, Object[] args)
方法,转到了调用层。
proxy
: 代理类method
: 调用的方法信息args
: 调用该方法的参数
以上就是 Java 动态代理的介绍与分析,Spring 的 AOP 以及 Android Retrofit 框架的接口注解实现就是基于动态代理实现的,好像是基于 CGLIB,不过原理大多都是想通的。