一、前言

  笔者平时开发使用“动态代理”不多,最近在看设计模式的时候,“动态代理”又在面前晃了几次,所以这次想从源码的角度去分析动态代理的实现原理,以窥探其精妙~

二、正文

2.1 静态代理

   本文源码基于:jdk1.6.0_33

   在正式剖析动态代理的源码之前,我们可以先来看看“静态代理”(就是我们普通的代理模式)的UML图:

  从上图可以看出,代理类“ProxySubject”和被代理类“RealSubject”实现相同接口,并且“ProxySubject”关联“RealSubject”,客户端调用的时候,实际是调用“ProxySubject”,然后“ProxySubject”调用“RealSubject”同名方法,方法的最终实现是由“RealSubject”提供,“ProxySubject”可以实现对“RealSubject”的访问控制,也可以对对应的方法进行“增强”。下面举个简单的例子说明一下:

// Subject
public interface Subject {
public void doSomething();
} //ProxySubject
public class ProxySubject implements Subject {
private Subject realSubject = new RealSubject();
@Override
public void doSomething() {
System.out.println("before doSomething~");
realSubject.doSomething();
System.out.println("after doSomething~");
} } //RealSubject
public class RealSubject implements Subject { @Override
public void doSomething() {
System.out.println("小杜比亚在写博客");
} } //Client
public class Client { public static void main(String[] args) {
Subject subject = new ProxySubject();
subject.doSomething();
} } //输出
before doSomething~
小杜比亚在写博客
after doSomething~

  那么动态代理和这个静态代理有什么区别?这就体现在一个“动”字,从上面的代码我们知道,静态代理的“代理类”是程序员写好的,而动态代理的“代理类”是在程序运行期间动态生成的~有些刚开始接触Java编程的读者可能会很好奇,Java的类可以动态生成?当然可以,我之前有篇关于“类”加载的博文:深入探讨Java类加载机制里面有写到,程序运行期间,类加载器会将java字节码文件加载进内存,这边的“加载”是从磁盘上加载,java程序只要字节码文件,至于你的来源(网络、或者按照字节码文件格式进行动态生成),人家是不管的。

  直接切入正题,“动态代理”中牵扯到两个类:java.lang.reflect.InvocationHandler和java.lang.reflect.Proxy,我们先看下一个简单的动态代理例子:

 //Subject
package com.proxy.main;
public interface Subject {
public void doSomething();
} //RealSubject
package com.proxy.main;
public class RealSubject implements Subject { @Override
public void doSomething() {
System.out.println("小杜比亚还在写博客,有点儿想玩游戏了,坚持~");
}
} //ProxyHandler
package com.proxy.main;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method; public class ProxyHandler implements InvocationHandler {
private Object proxiedObj;
public ProxyHandler(Object proxiedObj){
this.proxiedObj = proxiedObj;
}
/**
* @author caoyg
* @date 2017-05-06
* @description设置“被代理”对象
* @param proxiedObj
*/
public void setProxiedObject(Object proxiedObj){
this.proxiedObj = proxiedObj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {
return method.invoke(proxiedObj, args);
} } //Client
package com.proxy.main;
import java.lang.reflect.Proxy; public class Client { public static void main(String[] args) {
Subject sb = (Subject) Proxy.newProxyInstance(Subject.class.getClassLoader(),
new Class[]{Subject.class} ,
new ProxyHandler(new RealSubject()));
sb.doSomething();
} } //输出
小杜比亚还在写博客,有点儿想玩游戏了,坚持~

  上面代码第37行,invoke方法三个参数含义如下:

 /**
该方法负责集中处理动态代理类上的所有方法调用。
第一个参数是动态生成的代理类实例,
第二个参数是被调用的方法对象
第三个方法是方法调用参数。
调用处理器根据这三个参数进行预处理或分派到委托类实例上执行
*/
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;

2.2 动态代理机制

  1、先创建一个InvocationHandler实现类,并且将需要“被代理”的对象传递给它(类似下面代码)

  

2、Proxy通过方法newProxyInstance接收classLoader和一组interfaces(“被代理”对象实现的接口)以及InvocationHandler的实现类实例(以下简称“h”),通过classLoader和interfaces构造动态代理类的Class(下面代码是Proxy的源码部分)

 Class cl = getProxyClass(loader, interfaces);
............
public static Class<?> getProxyClass(ClassLoader loader,Class<?>... interfaces)throws IllegalArgumentException
{
//......省略
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces);
try {
proxyClass = 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());
}
//......省略
21 return proxyClass;
}

  3、Proxy通过反射机制,将传入的h作为参数,调用动态代理对应的构造函数,返回最终的动态代理实例

  在调用动态代理类的方法时,动态代理将方法的调用分派转发给InvocationHandler的invoke方法,InvocationHandler的invoke方法又将方法调用分派给它本身引用的“被代理”类对应的方法。

2.3 动态代理注意点

  1、包:代理接口都是public修饰的,则代理类被定义在顶层包(package为空),如果访问修饰符是默认(default),那么代理类被定义在该接口所在包下面

     /*
* 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('.');
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,
  proxyPkg = ""; // use the unnamed package
}

  2、生成的代理类为public final,不能被继承

3、类名:格式是“$ProxyN”,N是逐一递增的数字,代表Proxy被第N次动态生成的代理类,要注意,对于同一组接口(接口的排列顺序也相同),不会重复创建动态代理类,而是返回一个先前已经创建并缓存了的代理类对象。提高了效率

      /*
* Choose a name for the proxy class to generate.代理类类名生成规则
*/
long num;
synchronized (nextUniqueNumberLock) {
num = nextUniqueNumber++;
}
String proxyName = proxyPkg + proxyClassNamePrefix + num;
 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.
*/
do {
Object value = cache.get(key);
if (value instanceof Reference) {
proxyClass = (Class) ((Reference) value).get();
}
if (proxyClass != null) {
// proxy class already generated: return it
return proxyClass;
} else if (value == 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 {
/*
* 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);
}

  4、类继承关系

  

  Proxy 类是它的父类,这个规则适用于所有由 Proxy 创建的动态代理类。(也算是java动态代理的一处缺陷,java不支持多继承,所以无法实现对class的动态代理,只能对于Interface的代理,cglib解决了这个问题)而且该类还实现了其所代理的一组接口,这就是为什么它能够被安全地类型转换到其所代理的某接口的根本原因。

  5、代理类的根类 java.lang.Object 中有三个方法也同样会被分派到调用处理器的 invoke 方法执行,它们是 hashCode,equals 和 toString,代码在反编译中

 //ProxyGenerator.class
private byte[] generateClassFile()
{
addProxyMethod(hashCodeMethod, Object.class);
addProxyMethod(equalsMethod, Object.class);
addProxyMethod(toStringMethod, Object.class);
.....................
}

2.4 Proxy源码分析

  主要关注一些静态变量和两个方法,我们先来看看Proxy中的静态变量:

     /** prefix for all proxy class names */
//类名前缀
private final static String proxyClassNamePrefix = "$Proxy"; /** parameter types of a proxy class constructor */
//proxy类构造函数的参数类型,这个在通过反射生成动态代理类的时候用到
private final static Class[] constructorParams = { InvocationHandler.class }; /** maps a class loader to the proxy class cache for that loader */
private static Map loaderToCache = new WeakHashMap(); /** marks that a particular proxy class is currently being generated */
private static Object pendingGenerationMarker = new Object(); /** next number to use for generation of unique proxy class names */
//下个proxy的序号
private static long nextUniqueNumber = 0;
private static Object nextUniqueNumberLock = new Object(); /** set of all generated proxy classes, for isProxyClass implementation */
private static Map proxyClasses = Collections.synchronizedMap(new WeakHashMap()); /**
* the invocation handler for this proxy instance.
* @serial
*/
protected InvocationHandler h;

  接下来是两个重要方法:getProxyClass和newProxyInstance,这个newProxyInstance就是我们在前面的动态代理实例里面直接使用的方法,这个方法调用getProxyClass获取动态代理类的Class对象,然后通过反射生成动态代理类实例并返回:

  public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)throws IllegalArgumentException
{
if (h == null) {
throw new NullPointerException();
} /*
* Look up or generate the designated proxy class.
*/
//生成动态代理Class
Class cl = getProxyClass(loader, interfaces); /*
* Invoke its constructor with the designated invocation handler.
*/
try {
Constructor cons = cl.getConstructor(constructorParams);
//通过反射生成动态代理类实例对象,并返回
return (Object) cons.newInstance(new Object[] { h });
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString());
} catch (IllegalAccessException e) {
throw new InternalError(e.toString());
} catch (InstantiationException e) {
throw new InternalError(e.toString());
} catch (InvocationTargetException e) {
throw new InternalError(e.toString());
}
}

  在Proxy整个源码中,主要工作都集中在方法getProxyClass中:

 public static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces)throws IllegalArgumentException
{
//接口数的限制
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]; Set interfaceSet = new HashSet(); // for detecting duplicates
/*下面这个for循环做三项安全验证:
*1、传入的所有接口是否对loader可见,如果不可见,抛出异常
*2、传入的所有”接口“是否真的都是interface,如果不是,抛出异常
*3、传入的所有接口是否有重复,如果有,抛出异常
*/
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.
*/
//接口名称列表(List类型)
Object key = Arrays.asList(interfaceNames); /*
* Find or create the proxy class cache for the class loader.(查询或者创建代理类的缓存,缓存未找到或者已经失效,那么重新创建一个)
*/
Map cache;
synchronized (loaderToCache) {
cache = (Map) 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.
*/
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.
*/
do {
//接口名称列表作为key,查找代理类缓存中是否已经存在创建好的动态代理类
Object value = cache.get(key);
if (value instanceof Reference) {
proxyClass = (Class) ((Reference) value).get();
}
//代理类如果已经创建,直接返回
if (proxyClass != null) {
// proxy class already generated: return it
return proxyClass;
} else if (value == 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 {
/*
* 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.
*/
/*如果接口名称列表映射的动态代理类还未创建,那么将pendingGenerationMarker先设置到动态代理类的缓存中,
*作为key的映射,表示接下来准备要开始动态代理类的创建了,并且退出当前循环
*/
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.
*/
/*接下来这个循环,就是生成动态代理类的包名。首先校验所有interface是否都是public类型,如果声明为
*public的,那么动态代理类的包名为"",如果存在不止一个interface的接口声明为非public类型(不写修饰符的情况),
*那么,所有这些非public访问声明的接口必须在同一个包中,否则抛出错误;如果这些非public访问声明的接口都在同
*一个包中,那么动态代理类的包名和这些非public访问声明的接口的包名一致
*/
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('.');
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,
proxyPkg = ""; // use the unnamed package
} {
/*
* Choose a name for the proxy class to generate.
*/
long num;
//生成下一个动态代理类的序号
synchronized (nextUniqueNumberLock) {
num = nextUniqueNumber++;
}
//动态代理类的类名:包名+proxyClassNamePrefix + num;
String proxyName = proxyPkg + proxyClassNamePrefix + num;
/*
* Verify that the class loader hasn't already
* defined a class with the chosen name.
*/ /*
* Generate the specified proxy class.
*/
//这个方法别人说有点儿复杂,反编译进去看确实挺复杂,这边笔者解释不了,这边就是生成动态代理类的字节
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces);
try {
proxyClass = 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());
}
}
// 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.
*/
synchronized (cache) {
if (proxyClass != null) {
/*如果动态代理类创建成功,那么将动态代理类缓存中key对应的值更新成new WeakReference(proxyClass),原先
*设置的是pendingGenerationMarker
*/
cache.put(key, new WeakReference(proxyClass));
} else {
//如果创建不成功,那么将key对应的映射移除
cache.remove(key);
}
//不管创建成功不成功,将阻塞在代理类缓存上面的线程唤醒
cache.notifyAll();
}
}
return proxyClass;
}

  以上内容已经大致将动态代理的内部实现交代清楚,下面的程序将动态代理的实例写入class文件,然后反编译出来看下,动态代理类的实例到底长什么样:

  生成动态代理类的Class文件代码:

 package com.proxy.main;

 import java.io.FileOutputStream;
import java.lang.reflect.Proxy; import sun.misc.ProxyGenerator; public class Client { public static void main(String[] args) {
Subject sb = (Subject) Proxy.newProxyInstance(Subject.class.getClassLoader(),
new Class[]{Subject.class} ,
new ProxyHandler(new RealSubject()));
sb.doSomething();
createProxyClassFile();
} public static void createProxyClassFile(){
try{
String proxyName = "MyProxy";
byte[] data = ProxyGenerator.generateProxyClass(proxyName, new Class[]{Subject.class});
FileOutputStream out = new FileOutputStream( proxyName + ".class" );
out.write(data);
out.close();
}
catch(Exception e){
e.printStackTrace();
}
} }

  动态代理类反编译出来的代码:

 //因为传进去的interface数据中,所有interface的声明都是public,所以,动态代理类的包名为""
import com.proxy.main.Subject;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
//继承自Proxy
public final class MyProxy extends Proxy
implements Subject
{
private static Method m1;
private static Method m0;
private static Method m3;
private static Method m2; public MyProxy(InvocationHandler paramInvocationHandler)
throws
{
super(paramInvocationHandler);
} public final boolean equals(Object paramObject)
throws
{
try
{
return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
}
catch (RuntimeException localRuntimeException)
{
throw localRuntimeException;
}
catch (Throwable localThrowable)
{
}
throw new UndeclaredThrowableException(localThrowable);
} public final int hashCode()
throws
{
try
{
return ((Integer)this.h.invoke(this, m0, null)).intValue();
}
catch (RuntimeException localRuntimeException)
{
throw localRuntimeException;
}
catch (Throwable localThrowable)
{
}
throw new UndeclaredThrowableException(localThrowable);
} public final void doSomething()
throws
{
try
{
this.h.invoke(this, m3, null);
return;
}
catch (RuntimeException localRuntimeException)
{
throw localRuntimeException;
}
catch (Throwable localThrowable)
{
}
throw new UndeclaredThrowableException(localThrowable);
} public final String toString()
throws
{
try
{
return (String)this.h.invoke(this, m2, null);
}
catch (RuntimeException localRuntimeException)
{
throw localRuntimeException;
}
catch (Throwable localThrowable)
{
}
throw new UndeclaredThrowableException(localThrowable);
} 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("com.proxy.main.Subject").getMethod("doSomething", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
return;
}
catch (NoSuchMethodException localNoSuchMethodException)
{
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
}
catch (ClassNotFoundException localClassNotFoundException)
{
}
throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
}
}

三、链接

http://blog.csdn.net/scplove/article/details/52451899

http://www.cnblogs.com/flyoung2008/archive/2013/08/11/3251148.html

四、联系本人

  为方便没有博客园账号的读者交流,特意建立一个企鹅群,读者如果有对博文不明之处,欢迎加群交流:261746360,小杜比亚-博客园

最新文章

  1. Unity贴图锯齿
  2. MySQL_采购入库价格与在线售价监控_20161213
  3. (原创)即使最可怕的自然力量,也不失美丽&mdash;&mdash;火山喷发(摄影,欣赏)
  4. 使用SMSManager短信管理器实现短信群发
  5. sql 行专列 列转行 普通行列转换
  6. poj1160 post office
  7. Codeforces Round #218 (Div. 2) C. Hamburgers
  8. CSS中zoom:1的作用
  9. DEDE提高生成HTmL的速度
  10. Linux企业级项目实践之网络爬虫(13)——处理user-agent
  11. (转)Apache+Tomcat集群配置
  12. Android NumberPicker和DatePicker分割线颜色设置
  13. css3新属性的学习使用
  14. outline
  15. python函数练习题
  16. colormap中的内嵌彩色模块和调用方式
  17. OpenCV中 常用 函数 的作用
  18. matplotlib&amp;numpy画图
  19. 5个python爬虫教材,让小白也有爬虫可写,含视频教程!
  20. 通过cmd调用Powershell脚本

热门文章

  1. Python3集成安装xadmin
  2. &lt;逆向学习第二天&gt;如何手动脱UPX、Aspack壳
  3. ABAP术语-Authorization
  4. datatable根据条件设置表格行中字的颜色或背景
  5. python核心编程2 第十三章 练习
  6. 利用nginx使ftp可以通过http访问
  7. 一次 group by + order by 性能优化分析
  8. unity独立游戏开发日志2018/09/22
  9. 基于vue来开发一个仿饿了么的外卖商城(二)
  10. [BZOJ1040][ZJOI2008]骑士(树形DP)