JDK动态代理
JDK动态代理主要利用了java.lang.reflect
包下的类来实现运行时增强代码的功能,Java的三种代理模式一文中的例子生动说明了代理模式,如上图,可以把代理对象当做经纪人,目标对象是明星,用户是邀请明星的客户,那么用户一般只和经纪人洽谈,明星只负责演出事项,其他的事都由经纪人去完成。
使用条件
- 目标对象(被代理对象)需要实现接口,这一约束主要是由于Java没有多继承机制导致的;
- 需要自定义实现
java.lang.reflect.InvocationHandler
接口的处理器,处理器用来增强目标对象的功能,该接口只有一个方法:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
该接口方法第一个参数为代理对象实例,第二个参数为代理对象的方法,第三个参数为方法的参数,
该接口的实现类会持有一个私有的目标对象实例,然后在接口方法里使用, - 每次通过代理对象调用被代理对象的接口方法与代理对象的
equals/hashCode/toString
时,会自动调用处理器的invoke
方法; - 代理对象是在运行时在内存中动态生成的,通过
java.lang.reflect.Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
获取,此时需要指定目标对象类加载器、其实现的接口类型和自定义的处理器,注意该方法返回的是一个Object
类型,因此需要强转;
代码示例
按照上面的使用条件,代码部分分为四部分,分别是接口类,实现类,处理器类和测试类。
目标对象的接口类:
/**
* 目标对象的接口类
*
*/
public interface MyInterface {
void first();
void second();
}
目标对象类:
public class MyImpl implements MyInterface {
@Override
public void first() {
// TODO Auto-generated method stub
System.out.println("first method is executing");
}
@Override
public void second() {
// TODO Auto-generated method stub
System.out.println("second method is executing");
}
}
处理器类:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class JDKProxyHandler implements InvocationHandler {
private MyInterface myInterface;
public JDKProxyHandler(MyInterface myInterface) {
this.myInterface = myInterface;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// TODO Auto-generated method stub
Object result;
if ("first".equals(method.getName())) {
System.out.println("before");
result = method.invoke(myInterface, args);
System.out.println("after");
} else {
System.out.println("-----------");
result = method.invoke(myInterface, args);
System.out.println("===========");
}
return result;
}
}
测试类:
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Proxy;
import sun.misc.ProxyGenerator;
public class ProxyMain {
public static void main(String[] args) {
MyInterface myInterface = new MyImpl();
JDKProxyHandler jdkProxy = new JDKProxyHandler(myInterface);
MyInterface proxy = (MyInterface) Proxy.newProxyInstance(myInterface.getClass().getClassLoader(),
myInterface.getClass().getInterfaces(), jdkProxy);
proxy.first();
proxy.second();
System.out.println("proxy.hashCode()执行结果:" + proxy.hashCode());
System.out.println("代理对象的类:" + proxy.getClass());
// createProxyClassFile();
}
// 用于将代理对象的class文件输出到指定文本
private static void createProxyClassFile() {
byte[] data = ProxyGenerator.generateProxyClass("$Proxy0.class", new Class[] { MyInterface.class });
try (FileOutputStream out = new FileOutputStream("$Proxy0.class")) {
out.write(data);
} catch (IOException e) {
e.printStackTrace();
}
}
}
// console result:
// before
// first method is executing
// after
// -----------
// second method is executing
// ===========
// -----------
// ===========
// proxy.hashCode()执行结果:1252169911
// 代理对象的类:class com.sun.proxy.$Proxy0
结果说明
这里代理类对目标类的first()
方法增强了功能,即在代理对象proxy
执行该方法前打印before
,方法执行后打印after
,可以看到输出结果的前三行符合预期;接下来调用了second()
方法,也按预期打印了指定字符;最后调用了代理对象的hashCode()
方法,也可以看到执行了增强的代码段。
原理分析
jdk动态代理是通过反射等方式在代码运行过程中动态生成代理对象的class字节码,该字节码存在于内存中,为了查看这个class,我将这个class写入到了文件里(createProxyClassFile()
方法即可),然后反编译这个文件,内容如下:
package $Proxy0;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import test.example.proxy.MyInterface;
public final class class
extends Proxy
implements MyInterface
{
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m4;
private static Method m0;
public class(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 void first()
{
try
{
this.h.invoke(this, m3, null);
return;
}
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);
}
}
public final void second()
{
try
{
this.h.invoke(this, m4, null);
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);
}
}
static
{
try
{
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
m3 = Class.forName("test.example.proxy.MyInterface").getMethod("first", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m4 = Class.forName("test.example.proxy.MyInterface").getMethod("second", 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());
}
}
}
可以看到,这个代理类先是继承了Proxy
类,然后实现了被代理对象的接口,每一个jdk代理类都会先继承Proxy
类,正是由于Java不支持多继承,因此被代理的类必须实现至少一个接口,这也是jdk动态代理为什么必须要求被代理类有接口的原因了。
除此之外,我们可以看到这个类有一个静态代码块,里面初始化了五个方法,其中有两个是接口MyInterface
的,还有三个分别是equals
\ toString
\ hashCode
,而这个类的构造器是传入了一个InvocationHandler
,通过父类Proxy
的源码,可以知道代理类有个内部变量h
,代理类的含参构造器即将h
赋值为客户端自定义的处理器,而在调用这个代理类的五个方法时,实际上会执行h
的invoke(Object proxy, Method method, Object[] args)
方法,每次调用时,都会把this
传入,然后执行自定义处理器的invoke
方法,这便是JDK的动态代理啦。