概念理解
动态代理类就是在运行时创建的实现了一系列特定接口的类。
为了更好行文,还需要先明确三个概念:
- 代理类——在本文中指动态代理类
- 代理接口——在本文中指动态代理类实现的接口
- 代理实例——在本文中指动态代理类的一个实例
动态代理的一些特性:
- 代理类是
public或final的,不能是abstract - 代理类均继承自
java.lang.reflect.Proxy类 - 代理类在创建时按顺序实现了所指定的接口
- 代理类的名称前缀为
$Proxy - 代理类有一个
public构造函数,参数为实现了InvocationHandler的对象 - 每个代理实例都有一个指定的调用处理器
InvocationHandler,InvocationHandler作为其构造函数的参数传入 - 代理实例的方法调用会触发
InvocationHandler接口的invoke方法
源码简析
Proxy类概览
Proxy类位于java.lang.reflect包中,本文以Android SDK中的Proxy类来分析,与Java SDK中的Proxy可能稍有不同。
Proxy提供了一些静态方法用来生成动态代理类及其实例,同时Proxy类是这些动态代理类的父类。
Proxy的主要变量如下,具体请看注释说明:
/ 代理类名称前缀 */ private final static String proxyClassNamePrefix = "$Proxy"; / 代理类构造函数的参数类型*/ private final static Class[] constructorParams = { InvocationHandler.class }; / 类加载器与对应的代理类缓存映射 */ private static Map
, Object>> loaderToCache =
new WeakHashMap<>();
/ 标记某个特定的代理类正在生成 */
private
static Object pendingGenerationMarker =
new Object();
/ next number to use for generation of unique proxy class names */
private
static
long nextUniqueNumber =
0;
private
static Object nextUniqueNumberLock =
new Object();
/ 所有代理类的存储集合,在isProxyClass方法中会用到 */
private
static Map
, Void> proxyClasses = Collections.synchronizedMap(
new WeakHashMap
, Void>());
/ * 代理类实例的调用处理器 * @serial */
protected InvocationHandler h;
检查某个特定类是否为动态代理类:
public static boolean isProxyClass(Class
cl) { if (cl == null) { throw new NullPointerException(); } return proxyClasses.containsKey(cl); }
可以看到用到了上面定义的proxyClasses。
获取代理实例的调用处理器:
public static InvocationHandler getInvocationHandler(Object proxy) throws IllegalArgumentException { /* * Verify that the object is actually a proxy instance. */ if (!(proxy instanceof Proxy)) { throw new IllegalArgumentException("not a proxy instance"); } return ((Proxy) proxy).h; }
Proxy类本身还有一个invoke方法:
private static Object invoke(Proxy proxy, Method method, Object[] args) throws Throwable { InvocationHandler h = proxy.h; return h.invoke(proxy, method, args); }
其他Proxy提供的方法在“动态代理的使用流程分析”一节细说。
InvocationHandler接口
接下来看一下上面多次提到的InvocationHandler接口
/ * {@code InvocationHandler} is the interface implemented by * the invocation handler of a proxy instance. * * Each proxy instance has an associated invocation handler. * When a method is invoked on a proxy instance, the method * invocation is encoded and dispatched to the {@code invoke} * method of its invocation handler. */
public interface InvocationHandler {
/ * Processes a method invocation on a proxy instance and returns * the result. This method will be invoked on an invocation handler * when a method is invoked on a proxy instance that it is * associated with. */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable; }
注释写的非常清楚,每个代理实例都有一个InvocationHandler对象,实例方法调用时会触发该接口的invoke方法,并返回结果。invoke方法的三个参数分别为代理实例、方法实例(此处的方法实质是代理接口中所定义的方法)、方法参数。
通常我们会自定义一个类实现InvocationHandler接口,如上文示例中的MyInvocationHandler。
动态代理的使用流程分析
官方文档给出的生成动态代理类的示例:
// 1.为Foo接口生成动态代理类的步骤 InvocationHandler handler = new MyInvocationHandler(...); Class proxyClass = Proxy.getProxyClass(Foo.class.getClassLoader(), new Class[] { Foo.class }); Foo f = (Foo) proxyClass.getConstructor(new Class[] { InvocationHandler.class }).newInstance(new Object[] { handler }); // 2.更简单的一种调用方式,实际上是对上面几步的整合 Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(), new Class[] { Foo.class }, handler);
根据上述两种使用方式,我们来跟踪一下源码。
先看getProxyClass:
public static Class
getProxyClass(ClassLoader loader, Class
... interfaces) throws IllegalArgumentException { return getProxyClass0(loader, interfaces); }
此方法获取一个代理类,参数传入一个类加载器和一个接口数组,最终调到getProxyClass0(ClassLoader loader, Class,稍后再来看这个方法的具体实现。
... interfaces)
上面得到代理类之后通过getConstructor得到其构造方法,上面说过代理类有一个public构造函数,参数为实现了InvocationHandler的对象,得到构造函数后,然后调用newInstance得到一个代理实例,并将其转为对应的接口对象。
再看第2种简单的实现方式,Proxy.newProxyInstance的实现:
public static Object newProxyInstance(ClassLoader loader, Class
[] interfaces, InvocationHandler h) throws IllegalArgumentException { // 先检验调用处理器 if (h == null) { throw new NullPointerException(); } /* * 获取代理类,也是通过调用getProxyClass0 */ Class
cl = getProxyClass0(loader, interfaces); /* * Invoke its constructor with the designated invocation handler. */ try { // 获取构造函数 final Constructor
cons = cl.getConstructor(constructorParams); return newInstance(cons, h); } catch (NoSuchMethodException e) { throw new InternalError(e.toString()); } }
newInstance(cons, h)的实现为:
private static Object newInstance(Constructor
cons, InvocationHandler h) { try { // 最终还是走Constructor的newInstance方法 return cons.newInstance(new Object[] {h} ); } catch (IllegalAccessException | InstantiationException e) { throw new InternalError(e.toString()); } catch (InvocationTargetException e) { Throwable t = e.getCause(); if (t instanceof RuntimeException) { throw (RuntimeException) t; } else { throw new InternalError(t.toString()); } } }
上面两种方式本质都是一样的,只不过第2种方式对用户使用来说更加简单。
上述两种方式的流程很清楚,接下来看最关键的地方,即动态代理类的生成过程,主要是getProxyClass0的实现,其源码较长,具体看注释吧,如下:
private static Class
getProxyClass0(ClassLoader loader, Class
... interfaces) { // 校验接口数组的长度 if (interfaces.length > 65535) { throw new IllegalArgumentException("interface limit exceeded"); } Class
proxyClass = null; /* collect interface names to use as key for proxy class cache */ // 用于存储代理类实现的所有接口的名称 String[] interfaceNames = new String[interfaces.length]; // for detecting duplicates // 存储不重复的接口类对象 Set
> interfaceSet =
new HashSet<>();
// 循环遍历所有接口
for (
int i =
0; i < interfaces.length; i++) {
/* * Verify that the class loader resolves the name of this * interface to the same Class object. */
// 接口名 String interfaceName = interfaces[i].getName(); Class
interfaceClass =
null;
try {
// 通过接口名及类加载器得到一个接口类对象 interfaceClass = Class.forName(interfaceName,
false, loader); }
catch (ClassNotFoundException e) { }
// 校验
if (interfaceClass != interfaces[i]) {
throw
new IllegalArgumentException( interfaces[i] +
" 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"); }
/* * Verify that this interface is not a duplicate. */
// 校验接口是否重复,不重复的话加入集合
if (interfaceSet.contains(interfaceClass)) {
throw
new IllegalArgumentException(
"repeated interface: " + interfaceClass.getName()); } interfaceSet.add(interfaceClass);
// 存储接口名称到数组 interfaceNames[i] = interfaceName; }
/* * Using string representations of the proxy interfaces as * keys in the proxy class cache (instead of their Class * objects) is sufficient because we require the proxy * interfaces to be resolvable by name through the supplied * class loader, and it has the advantage that using a string * representation of a class makes for an implicit weak * reference to the class. */
// 上面已经有英文注释了,简单解释一下:使用字符串作(代理接口的名称)作为代理类缓存的key是足够的,因为我们使用类加载器加载时也是使用名称,使用字符串的另一个优势是它对类是隐式的弱引用。 List
key = Arrays.asList(interfaceNames);
/* * Find or create the proxy class cache for the class loader. */
// 为类加载器查找或创建代理类缓存 Map
, Object> cache;
synchronized (loaderToCache) { cache = loaderToCache.get(loader);
if (cache ==
null) { cache =
new HashMap<>(); loaderToCache.put(loader, cache); }
/* * This mapping will remain valid for the duration of this * method, without further synchronization, because the mapping * will only be removed if the class loader becomes unreachable. */ }
/* * Look up the list of interfaces in the proxy class cache using * the key. This lookup will result in one of three possible * kinds of values: * null, if there is currently no proxy class for the list of * interfaces in the class loader, * the pendingGenerationMarker object, if a proxy class for the * list of interfaces is currently being generated, * or a weak reference to a Class object, if a proxy class for * the list of interfaces has already been generated. */
// 3种查找结果:(1)null(2)pendingGenerationMarker对象(3)一个类对象的弱引用
synchronized (cache) {
/* * Note that we need not worry about reaping the cache for * entries with cleared weak references because if a proxy class * has been garbage collected, its class loader will have been * garbage collected as well, so the entire cache will be reaped * from the loaderToCache map. */
// 如果一个代理类被GC了,它对应的类加载器也会被GC do { Object value = cache.get(key);
// 结果1:返回弱引用,从中得到代理类
if (value
instanceof Reference) { proxyClass = (Class
) ((Reference) value).get(); }
if (proxyClass !=
null) {
// proxy class already generated: return it
// 代理类已经生成,并返回
return proxyClass; }
else
if (value == pendingGenerationMarker) {
// 结果2:返回pendingGenerationMarker,表示代理类正在生成
// proxy class being generated: wait for it
try { cache.wait(); }
catch (InterruptedException e) {
/* * The class generation that we are waiting for should * take a small, bounded time, so we can safely ignore * thread interrupts here. */ }
continue; }
else {
// 结果3:返回null,表示目前还没生成代理类,先在缓存中存储一个标记
/* * No proxy class for this list of interfaces has been * generated or is being generated, so we will go and * generate it now. Mark it as pending generation. */ cache.put(key, pendingGenerationMarker);
break; } }
while (
true); }
try {
// 代理类的包名 String proxyPkg =
null;
// package to define proxy class in
/* * Record the package of a non-public proxy interface so that the * proxy class will be defined in the same package. Verify that * all non-public proxy interfaces are in the same package. */
// 如果为非公有接口,则代理类与代理接口在同一个包内
for (
int i =
0; i < interfaces.length; i++) {
int flags = interfaces[i].getModifiers();
if (!Modifier.isPublic(flags)) { String name = interfaces[i].getName();
int n = name.lastIndexOf(
'.');
// 举个列子,如果name=com.abc.Testinterface,则pkg=com.abc. String pkg = ((n == -
1) ?
"" : name.substring(
0, n +
1));
if (proxyPkg ==
null) { proxyPkg = pkg; }
else
if (!pkg.equals(proxyPkg)) {
throw
new IllegalArgumentException(
"non-public interfaces from different packages"); } } }
if (proxyPkg ==
null) {
// if no non-public proxy interfaces, use the default package.
// 默认包名为com.sun.proxy proxyPkg =
""; } {
// Android-changed: Generate the proxy directly instead of calling
// through to ProxyGenerator.
// 请注意上面注释,Android中有变动,直接调用Native方法生成代理类而不是通过ProxyGenerator
// 下面方法通过递归获取接口中的所有方法 List
methods = getMethods(interfaces);
// 根据前面及类型排序 Collections.sort(methods, ORDER_BY_SIGNATURE_AND_SUBTYPE);
// 对方法返回类型进行校验,Throws if any two methods in {@code methods} have the same name and parameters but incompatible return types. validateReturnTypes(methods);
// Remove methods that have the same name, parameters and return type. This computes the exceptions of each method; this is the intersection of the exceptions of equivalent methods. List
[]> exceptions = deduplicateAndGetExceptions(methods);
// 转成方法数组 Method[] methodsArray = methods.toArray(
new Method[methods.size()]); Class
[][] exceptionsArray = exceptions.toArray(
new Class
[exceptions.size()][]);
/* * Choose a name for the proxy class to generate. */
final
long num;
synchronized (nextUniqueNumberLock) { num = nextUniqueNumber++; }
// 生成的动态代理类名,如com.sun.proxy.$Proxy0,com.sun.proxy.$Proxy1,com.sun.proxy.$Proxy2 String proxyName = proxyPkg + proxyClassNamePrefix + num;
// generateProxy为Native方法 proxyClass = generateProxy(proxyName, interfaces, loader, methodsArray, exceptionsArray); }
// add to set of all generated proxy classes, for isProxyClass proxyClasses.put(proxyClass,
null); }
finally {
/* * We must clean up the "pending generation" state of the proxy * class cache entry somehow. If a proxy class was successfully * generated, store it in the cache (with a weak reference); * otherwise, remove the reserved entry. In all cases, notify * all waiters on reserved entries in this cache. */
// 代理类生成之后,清除之前Cache中的标记状态,并将其以弱引用的方式保存
synchronized (cache) {
if (proxyClass !=
null) { cache.put(key,
new WeakReference
>(proxyClass)); }
else { cache.remove(key); }
// 唤醒前面cache.wait() cache.notifyAll(); } }
// 返回动态代理类
return proxyClass; }
动态代理示例
示例1
先定义一个代理接口,描述如何”上网冲浪“:
package com.aspook.dynamicproxy2; public interface SurfInternet {
void surf(String content); }
再定义一个委托类:
public class Normal implements SurfInternet {
@Override public void surf(String content) { System.out.println("上网: " + content); } }
创建一个自定义的InvocationHandler:
public class VPNInvocationHandler implements InvocationHandler {
private SurfInternet base; public VPNInvocationHandler(SurfInternet obj) { base = obj; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("invoke start"); // 会调用委托类的对应方法,从而实现代理 method.invoke(base, args); System.out.println("invoke end"); return null; } }
测试类:
public class Test {
public static void main(String[] args) { // 被代理类,或叫做委托类 Normal normal = new Normal(); InvocationHandler h = new VPNInvocationHandler(normal); SurfInternet vpn = (SurfInternet) Proxy.newProxyInstance(normal.getClass().getClassLoader(), normal.getClass().getInterfaces(), h); vpn.surf("VPN方式,可以访问Google了"); } }
输出结果:
注意第2行的输出,实质上是被代理类的功能,代理类一般用于不想将被代理类暴露出去,而是转用代理类来实现被代理类的功能,即所谓的代理。在上面的例子有一个被代理类Normal,与静态代理模式相比(本质上是代理类持有一个被代理类的实例,代理类与被代理类均实现相同接口),动态代理在运行时才会生成。
示例2
先定义一个代理接口:
package com.aspook.dynamicproxy; public interface HelloService { void sayHello(String content); }
注意此接口为public,位于com.aspook.dynamicproxy包下,按上文分析,生成的动态代理类应该位于默认包中,包名为com.sun.proxy,稍后验证。
再来实现一个逻辑类ProxyClassTest,具体请看注释:
public class ProxyClassTest {
private InvocationHandler h; // 初始化一个InvocationHandler public void prepareInvocationHandler() { h = new MyInvocationHandler(); } // 只有一个代理接口示例 public
T
create(Class
service) {
return (T) Proxy.newProxyInstance(service.getClassLoader(),
new Class[]{service}, h); }
// 这里有两个代理接口,GoodService代表另一个接口,源码未给出
public
T
create() {
return (T) Proxy.newProxyInstance(HelloService.class.getClassLoader(),
new Class[]{HelloService.class, GoodService.class}, h); }
// 代理实例方法调用触发invoke回调后可执行的个人逻辑
private
void
doLogic(String firstArg) { System.out.println(firstArg); }
// InvocationHandler的实现类,当然也可以用匿名内部类 class MyInvocationHandler implements InvocationHandler {
@Override
public Object
invoke(Object proxy, Method method, Object[] args)
throws Throwable { System.out.println(
"invoke"); System.out.println(
"proxy: " + proxy.getClass()); System.out.println(
"method: " + method.getName()); System.out.println(
"args length: " + args.length);
// 由于只是示例,仅供说明,因此下面两行未做越界校验 System.out.println(
"args : " + args[
0].toString()); doLogic(args[
0].toString());
// 注意返回值要跟接口方法返回值相对应
return
null; } } }
最后看下调用类:
public class Test {
public static void main(String[] args) { ProxyClassTest test = new ProxyClassTest(); test.prepareInvocationHandler(); HelloService service = test.create(); service.sayHello("hahaha"); } }
最终输入结果如下:
由输出结果可以看到代理全路径名称为com.sun.proxy.$Proxy0,跟前文中的理论一致,同时可以得到方法名和方法参数,并可执行自定义逻辑。
此示例并没有实现被代理类,多个接口的响应均统一在invoke中处理,然后实现具体逻辑,而不必像静态代理那样单独实现每一个接口。Retrofit也是利用了此原理。
注意事项:
- 如果代理接口有多个,如下面代码有
HelloService和GoodService两个接口publicT create() { return (T) Proxy.newProxyInstance(HelloService.class.getClassLoader(), new Class[]{HelloService.class, GoodService.class}, h); } 只要有一个接口为非
public的,则生成的动态代理类就跟接口同包,如com.aspook.dynamicproxy.$Proxy0。 - 如果多个接口返回类型不同,可以在
invoke中根据方法的返回类型来return不同的值。 - 如果跨包引用一个代理接口,则该接口必然为
public的,如果只有这一个接口则动态代理类位于默认包中;如果还有其他同包的代理接口,只要有一个为非public的,则生成的动态代理类就跟非public的接口同包,如果无论跨包同包所有接口均为public,则生成的动态代理类在默认包中。 - 由于动态代理类本身已经继承了
Proxy,根据Java单继承的限制,因此只能实现多个接口了。
发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/210330.html原文链接:https://javaforall.net
