===========================================

    原文链接: JDK动态代理实现机制   转载请注明出处!

===========================================

本文只对JDK动态代理的底层实现进行分析,如需了解代理模式和动态代理的使用请移步:设计模式—代理模式     动态代理的两种实现方式(JDK/Cglib)

在读代码之前先清楚两个概念:

  1、Class类存储的是类的所有信息,包括类的所有方法、属性、实现接口等。每个类对应一个Class对象(单例),Class对象是由classLoader加载出来的,使用双亲委派模型来保证class只会被加载一次。

  2、classLoader在加载类的时候不管class文件是从哪里来的,无论是从.class文件、网络、数据库类加载器都不关心。他只关心给他的class二进制流是不是能够通过校验。

说明:以下测试代码和 动态代理的两种实现方式(JDK/Cglib)相同

使用JDK动态代理需要实现InvocationHandler接口,同时实现invoke()方法。

package com.zpj.proxy.jdk;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy; /**
* Created by Perkins on 2017/4/2.
*/
public class JDKProxy implements InvocationHandler {
private Object person;
public Object getInstance(Object person) {
this.person = person;
return Proxy.newProxyInstance(person.getClass().getClassLoader(), person.getClass().getInterfaces(), this);
} @Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("doSomething---------start");
method.invoke(person, args);
System.out.println("doSomething---------end");
return null;
}
}

测试代码如下:

package com.zpj.proxy.jdk;

/**
* Created by Perkins on 2017/4/2.
*/
public class Run { public static void main(String[] args) {
Person person = (Person) new JDKProxy().getInstance(new MrLi());
person.doWork();
}
}

运行的时候在person处打断点可看到person的类名为$Proxy0而不是Person或者MrLi,则说明该返回对象是Person的实现类

我们添加如下代码把person的class中的方法打印出来

package com.zpj.proxy.jdk;

/**
* Created by Perkins on 2017/4/2.
*/
import java.lang.reflect.Method;
public class Run {
private Method method;
public static void main(String[] args) {
Person person = (Person) new JDKProxy().getInstance(new MrLi());
Method [] methods = person.getClass().getMethods();
for(int i =0; i<methods.length;i++){
System.out.println(methods[i].getName());
}
person.doWork();
}
}

结果如下,很明显红框中的方法不属于Person也不属于Object中的方法。这更进一步说明返回的person并不是Person的实例。

下面就进入底层代码对JDK动态代理进行解析。

这里直接对Proxy.newProxyInstance(person.getClass().getClassLoader(), person.getClass().getInterfaces(), this);进行分析。

public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces, InvocationHandler h)throws IllegalArgumentException{
if (h == null) {
//验证InvocationHandler不允许为null
throw new NullPointerException();
}
/*
* Look up or generate the designated proxy class.
*/
//调用getProxyClass()获取Class实例,该实例便是返回的代理Person的实例,此方法为重点!!!
Class cl = getProxyClass(loader, interfaces);
/*
* Invoke its constructor with the designated invocation handler.
*/
try {
//利用反射机制从Class中取出构造器创建对象
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());
}
}

在上面方法中调用public static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces)throws IllegalArgumentException获取了Class实例,下面进入该方法进行分析。

public static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces) throws IllegalArgumentException {
if (interfaces.length > 65535) {
//验证接口数量不允许超过65535
throw new IllegalArgumentException("interface limit exceeded");
}
//************开始对interface进行循环验证,验证通过则加入interfaceNames中***************************
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 (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 {
//根据名称获取接口的Class
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;
}
//************结束对interface进行循环验证,存储于interfaceNames中***************************
/*
* 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.
*/
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 {
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);
}
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.
*/
//寻找到package的包路径,为构建代理类做准备。同时要保证所有的非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++;
}
//这里构建了Class的全类名:包名+$Proxy+num,这里的proxyClassNamePrefix=“$Proxy”
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.
*/
//该方法为核心方法,获取代理类的字节码数据流。也即是proxyName.class文件的数据流
//因为interface的所有class文件都不已经被加载,所以这里只需要根据名称就可以从JVM中读取出所有的二进制数据
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces);
try {
//调用native方法从class字节码文件中创建代理类的Class实例。这里不再进入分析,由JVM负责实现
//获取到代理类的Class实例后,程序就可以根据代理类Class通过反射进行对象创建
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) {
cache.put(key, new WeakReference(proxyClass));
} else {
cache.remove(key);
}
cache.notifyAll();
}
}
//返回代理类的Class实例
return proxyClass;
}
}

在上面的方法中,核心就在于如何构建代理类的class字节码文件。因为该字节码文件是由代理类和目标类组合而成,这也即是在运行期间动态创建class的方法。

即:byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces);该方法的具体实现,看下面代码分析

 public static byte[] generateProxyClass(final String proxyName, Class[] interfaces) {
//创建代理生成器
ProxyGenerator proxyGenerator = new ProxyGenerator(proxyName, interfaces);
//调用generateClassFile()生成class的二进制数据流
final byte[] classFile = proxyGenerator.generateClassFile();
//该参数可以在运行时配置,当为true时则程序会生成代理类的class文件保存在磁盘中,即: $Proxy0.class
if(saveGeneratedFiles) {
AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
try {
FileOutputStream var1 = new FileOutputStream(ProxyGenerator.dotToSlash(proxyName) + ".class");
var1.write(classFile);
var1.close();
return null;
} catch (IOException var2) {
throw new InternalError("I/O exception saving generated file: " + interfaces);
}
}
});
}
return classFile;
}

继续进入proxyGenerator.generateClassFile()分析

 private byte[] generateClassFile() {
//注意这三个方法,hashCodeMethod、equalsMethod、toStringMethod
//这三个方法来自于Object,代理类同样需要Object的这三个方法
//把这三个方法名称和与之匹配的Object,class进行缓存
this.addProxyMethod(hashCodeMethod, Object.class);
this.addProxyMethod(equalsMethod, Object.class);
this.addProxyMethod(toStringMethod, Object.class); int var1;
int var3;
//这里循环对目标类所实现的所有接口中的方法进行缓存
for(var1 = 0; var1 < this.interfaces.length; ++var1) {
//获取接口中的方法
Method[] var2 = this.interfaces[var1].getMethods();
for(var3 = 0; var3 < var2.length; ++var3) {
this.addProxyMethod(var2[var3], this.interfaces[var1]);
}
}
Iterator var7 = this.proxyMethods.values().iterator();
List var8;
//把代理类需要创建的方法缓存在var8中。这里说明一下,因为该源码是从class中读取出来的,所以变量名在进行编译的时候被更改了,这里阅读的时候需要注意一些
while(var7.hasNext()) {
var8 = (List)var7.next();
checkReturnTypes(var8);
}
Iterator var11;
try {
//添加构造器方法,至此代理类中所需要添加的方法添加完成
this.methods.add(this.generateConstructor());
var7 = this.proxyMethods.values().iterator();
//循环把需要的变量和方法添加在fileds和methods中缓存
while(var7.hasNext()) {
var8 = (List)var7.next();
var11 = var8.iterator();
while(var11.hasNext()) {
ProxyGenerator.ProxyMethod var4 = (ProxyGenerator.ProxyMethod)var11.next();
this.fields.add(new ProxyGenerator.FieldInfo(var4.methodFieldName, "Ljava/lang/reflect/Method;", 10));
this.methods.add(var4.generateMethod());
}
}
this.methods.add(this.generateStaticInitializer());
} catch (IOException var6) {
throw new InternalError("unexpected I/O Exception");
}
//对方法和参数进行安全性验证
if(this.methods.size() > '\uffff') {
throw new IllegalArgumentException("method limit exceeded");
} else if(this.fields.size() > '\uffff') {
throw new IllegalArgumentException("field limit exceeded");
} else {
this.cp.getClass(dotToSlash(this.className));
this.cp.getClass("java/lang/reflect/Proxy");
for(var1 = 0; var1 < this.interfaces.length; ++var1) {
this.cp.getClass(dotToSlash(this.interfaces[var1].getName()));
}
this.cp.setReadOnly();
//构建缓冲流存放动态生成的字节码文件数据
ByteArrayOutputStream var9 = new ByteArrayOutputStream();
DataOutputStream var10 = new DataOutputStream(var9);
try {
//这里就是按照class文件格式进行封装,这里不再详解
var10.writeInt(-889275714);
var10.writeShort(0);
var10.writeShort(49);
this.cp.write(var10);
var10.writeShort(49);
var10.writeShort(this.cp.getClass(dotToSlash(this.className)));
var10.writeShort(this.cp.getClass("java/lang/reflect/Proxy"));
var10.writeShort(this.interfaces.length);
for(var3 = 0; var3 < this.interfaces.length; ++var3) {
//添加所有方法的字节码数据
var10.writeShort(this.cp.getClass(dotToSlash(this.interfaces[var3].getName())));
}
var10.writeShort(this.fields.size());
var11 = this.fields.iterator();
while(var11.hasNext()) {
//添加所有变量的字节码数据
ProxyGenerator.FieldInfo var12 = (ProxyGenerator.FieldInfo)var11.next();
var12.write(var10);
}
var10.writeShort(this.methods.size());
var11 = this.methods.iterator();
while(var11.hasNext()) {
ProxyGenerator.MethodInfo var13 = (ProxyGenerator.MethodInfo)var11.next();
var13.write(var10);
}
var10.writeShort(0);
//动态组合class二进制字节码结束,进行返回。这里存储的就是一个完整class文件数据。调用处据此生成Class对象实例
return var9.toByteArray();
} catch (IOException var5) {
throw new InternalError("unexpected I/O Exception");
}
}
}

至此,JDK动态代理的实现结束。由以上可见,其核心就是动态的生成代理类的class字节码数据,然后调用native方法从字节码数据中创建代理对象的Class实例,拿到Class实例后通过Java的反射技术生成代理类

那么该代理类的结果到底如何?我们可以在main方法中添加 System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");配置,这样在程序运行的时候会把代理类的字节码文件保存在类路径下。通过反编译可以读取到代理类的具体详情。

下面看反编译后的代理类文件

package com.sun.proxy;

import com.zpj.proxy.jdk.Person;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException; //注意这里$Proxy0 extends Proxy implements Person,这里也即是目标类必须实现的有接口的原因
/**
* 代理类名称:$Proxy0
* 继承:Proxy
* 实现:Person
*/
public final class $Proxy0 extends Proxy implements Person {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0; public $Proxy0(InvocationHandler paramInvocationHandler) throws {
super(paramInvocationHandler);
} //继承自Object的方法
public final boolean equals(Object paramObject) throws {
try {
return ((Boolean) this.h.invoke(this, m1, new Object[]{paramObject})).booleanValue();
} catch (Error | RuntimeException localError) {
throw localError;
} catch (Throwable localThrowable) {
throw new UndeclaredThrowableException(localThrowable);
}
}
//继承自Object的方法
public final String toString()throws {
try {
return (String) this.h.invoke(this, m2, null);
} catch (Error | RuntimeException localError) {
throw localError;
} catch (Throwable localThrowable) {
throw new UndeclaredThrowableException(localThrowable);
}
}
//继承自Object的方法
public final int hashCode()throws {
try {
return ((Integer) this.h.invoke(this, m0, null)).intValue();
} catch (Error | RuntimeException localError) {
throw localError;
} catch (Throwable localThrowable) {
throw new UndeclaredThrowableException(localThrowable);
}
}
//实现Person的方法
public final void doWork()throws {
try {
//由此处可以看出目标方法的调用循序
/**
* 代理类先调用实现接口的方法,在该方法中调用InvocationHandler的invoke方法。
* 而在invoke中由通过注入进去的methods通过反射调用目标类的目标方法doWork()
*/
this.h.invoke(this, m3, null);
return;
} catch (Error | RuntimeException localError) {
throw localError;
} catch (Throwable localThrowable) {
throw new UndeclaredThrowableException(localThrowable);
}
} static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m3 = Class.forName("com.zpj.proxy.jdk.Person").getMethod("doWork", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
return;
} catch (NoSuchMethodException localNoSuchMethodException) {
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
} catch (ClassNotFoundException localClassNotFoundException) {
throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
}
}
}

------------end

JDK动态代理实现机制的更多相关文章

  1. java JDK动态代理的机制

    一:前言 自己在稳固spring的一些特性的时候在网上看到了遮掩的一句话“利用接口的方式,spring aop将默认通过JDK的动态代理来实现代理类,不适用接口时spring aop将使用通过cgli ...

  2. MyBatis Mapper 接口如何通过JDK动态代理来包装SqlSession 源码分析

    我们以往使用ibatis或者mybatis 都是以这种方式调用XML当中定义的CRUD标签来执行SQL 比如这样 <?xml version="1.0" encoding=& ...

  3. 静态代理、jdk动态代理、cglib动态代理

    一.静态代理 Subject:抽象主题角色,抽象主题类可以是抽象类,也可以是接口,是一个最普通的业务类型定义,无特殊要求. RealSubject:具体主题角色,也叫被委托角色.被代理角色.是业务逻辑 ...

  4. 深度剖析JDK动态代理机制

    摘要 相比于静态代理,动态代理避免了开发人员编写各个繁锁的静态代理类,只需简单地指定一组接口及目标类对象就能动态的获得代理对象. 代理模式 使用代理模式必须要让代理类和目标类实现相同的接口,客户端通过 ...

  5. 利用JDK动态代理机制实现简单拦截器

    利用JDK动态代理机制实现简单的多层拦截器 首先JDK动态代理是基于接口实现的,所以我们先定义一个接口 public interface Executer { public Object execut ...

  6. 深度剖析java中JDK动态代理机制

    https://www.jb51.net/article/110342.htm 本篇文章主要介绍了深度剖析java中JDK动态代理机制 ,动态代理避免了开发人员编写各个繁锁的静态代理类,只需简单地指定 ...

  7. JDK动态代理

    一.基本概念 1.什么是代理? 在阐述JDK动态代理之前,我们很有必要先来弄明白代理的概念.代理这个词本身并不是计算机专用术语,它是生活中一个常用的概念.这里引用维基百科上的一句话对代理进行定义: A ...

  8. 静态代理和利用反射形成的动态代理(JDK动态代理)

    代理模式 代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问.在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用. 静态代理 1.新建 ...

  9. Spring中的JDK动态代理

    Spring中的JDK动态代理 在JDK1.3以后提供了动态代理的技术,允许开发者在运行期创建接口的代理实例.在Sun刚推出动态代理时,还很难想象它有多大的实际用途,现在动态代理是实现AOP的绝好底层 ...

随机推荐

  1. ZooKeeper的不同类型的Znodes

    在ZooKeeper中有3个不同类型的znodes:persistent, ephemeral, sequential Persistent Znodes(默认)在ZooKeeper中,这是默认的zn ...

  2. Unity中的万能对象池

    本文为博主原创文章,欢迎转载.请保留博主链接http://blog.csdn.net/andrewfan Unity编程标准导引-3.4 Unity中的万能对象池 本节通过一个简单的射击子弹的示例来介 ...

  3. 什么是DOCTYPE?

    一.DOCTYPE是什么? DOCTYPE是文档类型的速记(文档.网页中指定页面的XHTML或HTML版本类型).使符合标准的页面,一个关键组成部分是DOCTYPE声明.只有确定了正确的XHTML D ...

  4. JAVA设计模式:代理模式

    最近公司从新开发一个项目,为此对老的代码进行评估分析发现有些地方是可以采用代理模式来进行的,为此对代理模式进行了一次理解: 代理模式:即为指定的目标对象提供一个代理商,由代理商来完成对目标对象的操作. ...

  5. 关于ReentrantLock和Condition的用法

    这篇博客是过年后的第一篇博客,2.13正式上班,之前在家休年假.上班第一天公司说有个紧急的项目需要上线,所以我们连续加了两个星期的班,直到上个周六还在加班,终于成功上线了.今天是2月的最后的一天,继续 ...

  6. http服务搭建

    http服务器搭建 主配置文件在 /etc/httpd/conf/httpd.conf 安装http  yum install httpd -y 启动http服务器  systemctl start ...

  7. 【转】Spring源码编译

    原文地址: http://www.flyoung.me/2013/08/02/springcodecompile/ 参考资料: https://github.com/spring-projects/s ...

  8. Tomcat使用Memcached Session Manager管理Session

    Tomcat使用Memcached Session Manager管理Session 废话不多说,直接进入主题.项目使用阿里云负载均衡+ECS服务器集群进行部署,Tomcat使用8.5版本.阿里云负载 ...

  9. 数字化工厂解决方案——OA办公自动化与ERP

    移动办公APP/即时通讯 通过集成手机应用,将移动办公引入到企业信息化管理中,能随时随地的完成审批.查询.警报.知会.公告发布.KPI统计和信息推送.系统已经支持苹果系统.安卓系统和微软WP8系统.企 ...

  10. WebStorm 自定义字体+颜色+语法高亮+导入导出用户设置

    WebStorm :是jetbrains公司旗下一款JavaScript 开发工具.被广大中国JS开发者誉为“Web前端开发神器”.“最强大的HTML5编辑器”.“最智能的JavaScript IDE ...