理解Java动态代理
Posted aspook
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了理解Java动态代理相关的知识,希望对你有一定的参考价值。
概念理解
动态代理类就是在运行时创建的实现了一系列特定接口的类。
为了更好行文,还需要先明确三个概念:
- 代理类——在本文中指动态代理类
- 代理接口——在本文中指动态代理类实现的接口
- 代理实例——在本文中指动态代理类的一个实例
动态代理的一些特性:
- 代理类是
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<ClassLoader, Map<List<String>, 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<Class<?>, Void> proxyClasses =
Collections.synchronizedMap(new WeakHashMap<Class<?>, 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 <i>invocation handler</i> of a proxy instance.
*
* <p>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<Class<?>> 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<String> key = Arrays.asList(interfaceNames);
/*
* Find or create the proxy class cache for the class loader.
*/
// 为类加载器查找或创建代理类缓存
Map<List<String>, 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<Method> 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<Class<?>[]> 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<Class<?>>(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了");
输出结果:
invoke start
上网: VPN方式,可以访问Google了
invoke end
注意第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> T create(Class<T> service)
return (T) Proxy.newProxyInstance(service.getClassLoader(),
new Class[]service, h);
// 这里有两个代理接口,GoodService代表另一个接口,源码未给出
public <T> 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");
最终输入结果如下:
invoke
proxy: class com.sun.proxy.$Proxy0
method: sayHello
args length: 1
args : hahaha
custom logic: hahaha
由输出结果可以看到代理全路径名称为com.sun.proxy.$Proxy0
,跟前文中的理论一致,同时可以得到方法名和方法参数,并可执行自定义逻辑。
此示例并没有实现被代理类,多个接口的响应均统一在invoke
中处理,然后实现具体逻辑,而不必像静态代理那样单独实现每一个接口。Retrofit也是利用了此原理。
注意事项:
如果代理接口有多个,如下面代码有
HelloService
和GoodService
两个接口public <T> T 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单继承的限制,因此只能实现多个接口了。
以上是关于理解Java动态代理的主要内容,如果未能解决你的问题,请参考以下文章