您的位置:首页 > 编程语言 > Java开发

JDK动态代理的实现和原理解析(基于JDK1.7)

2017-08-29 20:18 1066 查看

一、什么是代理

代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问。代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。详细介绍请参考:java设计模式之代理模式。为了保持行为的一致性,代理类和委托类通常会实现相同的接口,所以在访问者看来两者没有丝毫的区别。通过代理类这中间一层,能有效控制对委托类对象的直接访问,也可以很好地隐藏和保护委托类对象,同时也为实施不同控制策略预留了空间,从而在设计上获得了更大的灵活性。Java 动态代理机制以巧妙的方式近乎完美地实践了代理模式的设计理念。

二、动态代理相关类的介绍

在java的动态代理机制中,有两个重要的类或接口,一个是 InvocationHandler(Interface)、另一个则是 Proxy(Class),这一个类和接口是实现我们动态代理所必须用到的。

1、InvocationHandler

每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联了一个handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke 方法来进行调用。我们来看看InvocationHandler这个接口的唯一一个方法 invoke 方法:

Object invoke(Object proxy, Method method, Object[] args) throws Throwable


我们看到这个方法一共接受三个参数,那么这三个参数分别代表如下:

proxy:  指代JDK动态生成的最终代理对象

method: 指代的是我们所要调用真实对象的某个方法的Method对象

args:   指代的是调用真实对象某个方法时接受的参数

2、Proxy

Proxy这个类的作用就是用来动态创建一个代理对象的类,它提供了许多的方法,但是我们用的最多的就是 newProxyInstance 这个方法:

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,  InvocationHandler h)  throws IllegalArgumentException


这个方法的作用就是得到一个动态的代理对象,其接收三个参数,我们来看看这三个参数所代表的含义:

loader:  一个ClassLoader对象,定义了由哪个ClassLoader来对生成的代理对象进行加载

interfaces:  一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了

一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上

其实我们所说的DynamicProxy(动态代理类)是这样一种class:它是在运行时生成的class,在生成它时你必须提供一组interface给它,然后该class就宣称它实现了这些 interface。如此一来,我们可以把该class的实例当作这些interface中的任何一个来用(可以强转为相应的接口类型)。当然,这个DynamicProxy其实就是一个Proxy,它不会做实质性的工作,在生成它的实例时你必须提供一个handler,由它接管实际的工作。

三、实例演示

1、定义接口

通过JDK实现的代理对象必须是一个接口的实现。

package com.kang.proxy;

//需要动态代理的接口
public interface Subject {

public void hello(String name);
public String bye();

}


2、被代理的类(实际处理业务的类)

这个类实现上面定义的接口。

package com.kang.proxy;

//被代理类
public class RealSubject implements Subject{

@Override
public void hello(String name) {
System.out.println("hello "+name);
}

@Override
public String bye() {
System.out.println("bye");
return "bye";
}

}


3、InvocationHandler实现类

package com.kang.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

//每次生成动态代理类对象时都需要指定一个实现了InvocationHandler接口的调用处理器对象
public class InvocationHandlerImpl implements InvocationHandler {

private Object subject; // 这个就是我们要代理的真实对象,也就是真正执行业务逻辑的类

public InvocationHandlerImpl(Object subject) {// 通过构造方法传入这个被代理对象
this.subject = subject;
}

/**
* 该方法负责集中处理动态代理类上的所有方法调用。 调用处理器根据这三个参数进行预处理或分派到委托类实例上反射执行
*
* @param proxy
*            最终生成的代理类实例
* @param method
*            被调用的方法对象
* @param args
*            调用上面method时传入的参数
* @return method对应的方法的返回值
* @throws Throwable
*/

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
System.out.println("可以在调用实际方法前做一些事情");
System.out.println("当前调用的方法是" + method.getName());
// 当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用
result = method.invoke(subject, args);// 需要指定被代理对象和传入参数
System.out.println(method.getName() + "方法的返回值是" + result);
System.out.println("可以在调用实际方法后做一些事情");
System.out.println("------------------------");
return result;// 返回method方法执行后的返回值
}

}


4、测试类

package com.kang.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

public class Test {
public static void main(String[] args) {
// 被代理的对象
Subject realSubject = new RealSubject();

/**
* 通过InvocationHandlerImpl的构造器生成一个InvocationHandler对象,
* 需要传入被代理对象作为参数
*/
InvocationHandler handler = new InvocationHandlerImpl(realSubject);

ClassLoader loader = realSubject.getClass().getClassLoader();
Class[] interfaces = realSubject.getClass().getInterfaces();

// 需要指定类装载器、一组接口及调用处理器生成动态代理类实例

Subject subject = (Subject) Proxy.newProxyInstance(loader, interfaces, handler);

System.out.println("动态代理对象的类型:" + subject.getClass().getName());
subject.hello("Tom");
subject.bye();
}

}


5、测试结果



6、解析

首先解释一下JDK动态代理的大致流程,JDK主要会做以下工作:

1. 获取 RealSubject上的所有接口列表;

2. 确定要生成的代理类的类名;

3. 根据需要实现的接口信息,在代码中动态创建该Proxy类的字节码;

4. 将对应的字节码转换为对应的class 对象;

5. 创建InvocationHandler 实例handler,用来处理Proxy所有方法调用;

6. Proxy 的class对象 以创建的handler对象为参数,实例化一个proxy对象

下面来分析一下打印结果。

我们首先来看看 $Proxy0 ,这个是由

System.out.println("动态代理对象的类型:"+subject.getClass().getName());


这条语句打印出来的,那么为什么我们返回的这个代理对象的类名是这样的呢?

Subject subject = (Subject) Proxy.newProxyInstance(loader, interfaces, handler);


首先我们解释一下为什么我们这里可以将其转化为Subject类型的对象?原因就是在newProxyInstance这个方法的第二个参数上,我们给这个代理对象提供了一组什么接口,那么我这个代理对象就会实现了这组接口,这个时候我们当然可以将这个代理对象强制类型转化为这组接口中的任意一个,因为这里的接口是Subject类型,所以就可以将其转化为Subject类型了。同时我们需要明白,通过 Proxy.newProxyInstance 创建的代理对象是在jvm运行时动态生成的一个对象,而在运行是动态生成的一个对象,命名方式都是这样的形式,以$开头,proxy为中,最后一个数字表示对象的标号。

接着我们来看看这两句

subject.hello("Tom");
subject.bye();


我们看到,在真正通过代理对象来调用真实对象的方法的时候,我们可以在该方法前后添加自己的一些操作。当我通过代理对象来调用方法的时候,其实际就是委托由其关联到的 handler 对象的invoke方法中来调用,并不是自己来真实调用,而是通过代理的方式来调用的。这就是我们的java动态代理机制。通过这种方式,被代理的对象(RealSubject)可以在运行时动态改变,需要控制的接口(Subject接口)可以在运行时改变,控制的方式(DynamicSubject类)也可以动态改变,从而实现了非常灵活的动态代理关系。

四、底层实现原理

subject.hello("Tom");
subject.bye();


为什么会自动调用InvocationHandlerImpl的invoke方法?因为JDK生成的最终真正的代理类,它继承自Proxy并实现了我们定义的Subject接口,在实现Subject接口方法的内部,通过反射调用了

InvocationHandlerImpl的invoke方法。那么JDK是如何实现的呢?

1、newProxyInstance方法的关键实现

/**
* loader:类加载器
* interfaces:目标对象实现的接口
* h:InvocationHandler的实现类
*/
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
if (h == null) {
throw new NullPointerException();
}

/*
*关键实现:生成代理对象的class对象
*/
Class cl = getProxyClass(loader, interfaces);

/*
* Invoke its constructor with the designated invocation handler.
*/
try {
// 调用代理对象的构造方法(也就是$Proxy0(InvocationHandler h))
Constructor cons = cl.getConstructor(constructorParams);
// 生成代理类的实例并把MyInvocationHandler的实例传给它的构造方法
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());
}
}


上述代码的关键语句是
Class cl = getProxyClass(loader, interfaces);


2、getProxyClass方法的关键实现

public static Class<?> getProxyClass(ClassLoader loader,   Class<?>... interfaces)
throws IllegalArgumentException
{
// 如果目标类实现的接口数大于65535个则抛出异常
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}

// 声明代理对象所代表的Class对象
Class proxyClass = null;

String[] interfaceNames = new String[interfaces.length];

Set interfaceSet = new HashSet();   // for detecting duplicates

// 遍历目标类所实现的接口
for (int i = 0; i < interfaces.length; i++) {

// 拿到目标类实现的接口的名称
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");
}

// 中间省略部分代码 .......

// 把目标类实现的接口代表的Class对象放到Set中
interfaceSet.add(interfaceClass);

interfaceNames[i] = interfaceName;
}

// 把目标类实现的接口名称作为缓存(Map)中的key
Object key = Arrays.asList(interfaceNames);

Map cache;

synchronized (loaderToCache) {
// 从缓存中获取cache
cache = (Map) loaderToCache.get(loader);
if (cache == null) {
// 如果获取不到,则新建地个HashMap实例
cache = new HashMap();
// 把HashMap实例和当前加载器放到缓存中
loaderToCache.put(loader, cache);
}

}

synchronized (cache) {

do {
// 根据接口的名称从缓存中获取对象
Object value = cache.get(key);
if (value instanceof Reference) {
proxyClass = (Class) ((Reference) value).get();
}
if (proxyClass != null) {
// 如果代理对象的Class实例已经存在,则直接返回
return proxyClass;
} else if (value == pendingGenerationMarker) {
try {
cache.wait();
} catch (InterruptedException e) {
}
continue;
} else {
cache.put(key, pendingGenerationMarker);
break;
}
} while (true);
}

try {
// 中间省略部分代码 .......

// 这里就是动态生成代理对象的最关键的地方
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces);
try {
// 根据代理类的字节码生成代理类的实例
proxyClass = defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
throw new IllegalArgumentException(e.toString());
}
}
// add to set of all generated proxy classes, for isProxyClass
proxyClasses.put(proxyClass, null);

}
// 中间省略部分代码 .......

return proxyClass;
}


那么上面这一大段代码看起来很庞杂,无处下手。我们不必分析其具体实现,只分析其关键实现。比如这句:

byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces);


3、generateProxyClass的关键实现

public static byte[] generateProxyClass(final String name,
Class[] interfaces)
{
ProxyGenerator gen = new ProxyGenerator(name, interfaces);
// 这里动态生成代理类的字节码
final byte[] classFile = gen.generateClassFile();

// 如果saveGeneratedFiles的值为true,则会把所生成的代理类的字节码保存到硬盘上
if (saveGeneratedFiles) {
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<Void>() {
public Void run() {
try {
FileOutputStream file =
new FileOutputStream(dotToSlash(name) + ".class");
file.write(classFile);
file.close();
return null;
} catch (IOException e) {
throw new InternalError(
"I/O exception saving generated file: " + e);
}
}
});
}

// 返回代理类的字节码
return classFile;
}


至此,我们已经明白了JDK是如何生成最终的代理类的,其实最根本的是通过动态生成代理类的字节码,然后通过这些字节码实现代理类的生成。

结合我们的例子,就是说通过如下这句语句得到的代理类是JVM动态生成的,其具体实现我们是无法直接查看的。如果我们能得到其具体实现,结合源码我们就能搞清楚为什么会自动调用InvocationHandlerImpl的invoke方法。幸运的是,我们可以通过保存代理类的字节码到磁盘,然后通过java反编译工具得到其实现。

Subject subject = (Subject) Proxy.newProxyInstance(loader, interfaces, handler);


4、通过反编译工具得到代理类的实现

首先通过如下程序得到代理类的字节码文件

package com.kang.proxy;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

import sun.misc.ProxyGenerator;

public class Demonstration {
public static void main(String[] args) {
// 代理的真实对象
Subject realSubject = new RealSubject();

InvocationHandler handler = new InvocationHandlerImpl(realSubject);

ClassLoader loader = handler.getClass().getClassLoader();
Class[] interfaces = realSubject.getClass().getInterfaces();

Subject subject = (Subject) Proxy.newProxyInstance(loader, interfaces, handler);

System.out.println("动态代理对象的类型:" + subject.getClass().getName());

subject.hello("Tom");
// 将生成的字节码保存到本地,
createProxyClassFile();
}

private static void createProxyClassFile() {
String name = "ProxySubject";
byte[] data = ProxyGenerator.generateProxyClass(name, new Class[] { Subject.class });
FileOutputStream out = null;
try {
out = new FileOutputStream(name + ".class");
System.out.println((new File("hello")).getAbsolutePath());
out.write(data);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (null != out)
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}

}


然后通过反编译工具得到代理类的实现如下:

import com.kang.proxy.Subject;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

//继承自Proxy并实现了我们定义的Subject接口
public final class ProxySubject
extends Proxy
implements Subject
{
private static Method m1;
private static Method m4;
private static Method m3;
private static Method m0;
private static Method m2;

public ProxySubject(InvocationHandler paramInvocationHandler)
{
super(paramInvocationHandler);
}

public final boolean equals(Object paramObject)
{
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);
}
}

public final String bye()
{
try
{
return (String)this.h.invoke(this, m4, null);
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}

public final void hello(String paramString)
{
try
{
this.h.invoke(this, m3, new Object[] { paramString });
return;
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}

public final int hashCode()
{
try
{
return ((Integer)this.h.invoke(this, m0, null)).intValue();
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}

public final String toString()
{
try
{
return (String)this.h.invoke(this, m2, null);
}
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") });
m4 = Class.forName("com.kang.proxy.Subject").getMethod("bye", new Class[0]);
m3 = Class.forName("com.kang.proxy.Subject").getMethod("hello", new Class[] { Class.forName("java.lang.String") });
m0 = Class.forName("java.lang.Object").getMethod("hashCode", 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());
}
}
}


这就是最终真正的代理类,它继承自Proxy并实现了我们定义的Subject接口。我们通过

Subject subject = (Subject) Proxy.newProxyInstance(loader, interfaces, handler);


得到的最终代理类对象就是上面这个类的实例。那么我们执行如下语句:

subject.hello("Tom");


实际上就是执行上面类的相应方法,也就是:

public final void hello(String paramString)
{
try
{
this.h.invoke(this, m3, new Object[] { paramString });
//就是调用我们自定义的InvocationHandlerImpl的 invoke方法:
return;
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}


注意这里的
this.h.invoke
中的h,它是类Proxy中的一个属性

protected InvocationHandler h;


因为这个代理类继承了Proxy,所以也就继承了这个属性,而这个属性值就是我们定义的

InvocationHandler handler = new InvocationHandlerImpl(realSubject);


同时我们还发现,invoke方法的第一参数在底层调用的时候传入的是
this
,也就是最终生成的代理对象ProxySubject,这是JVM自己动态生成的,而不是我们自己定义的代理对象。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: