深入理解JDK动态代理
目录
1. JDK动态代理Demo
1.1 Moveable接口
package jdkproxy;
/**
* @author zhaobin11@baidu.com
*/
public interface Moveable {
void move(int i);
}
1.2 Car实现类
package jdkproxy;
import java.util.Random;
/**
* @author zhaobin11@baidu.com
*/
public class Car implements Moveable {
@Override
public void move(int i) {
try {
Thread.sleep(new Random().nextInt(1000));
System.out.println("汽车行驶中..." + i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
1.3 TimeHandler实现InvocationHandler
package jdkproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* @author zhaobin11@baidu.com
*/
public class TimeHandler implements InvocationHandler {
private Object target;
public TimeHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long start = System.currentTimeMillis();
System.out.println("汽车开始行驶...");
method.invoke(target, args);
System.out.println("汽车结束行驶,行驶时间为:" + (System.currentTimeMillis() - start) + "毫秒");
return null;
}
}
1.4 测试类Test
package jdkproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
/**
* @author zhaobin11@baidu.com
*/
public class Test {
public static void main(String[] args) {
Car car = new Car();
InvocationHandler h = new TimeHandler(car);
/*
* loader:类加载器
* interfaces:目标对象实现的接口
* h:InvocationHandler的实现类
*/
Moveable m = (Moveable) Proxy.newProxyInstance(car.getClass().getClassLoader(), car.getClass().getInterfaces(), h);
m.move(1);
// ProxyGeneratorUtils.writeProxyClassToHardDisk("C:\\Users\\sakura\\Downloads\\study\\src\\jdkproxy\\$Proxy0.class");
}
}
运行Test,可以看到
汽车开始行驶...
汽车行驶中...1
汽车结束行驶,行驶时间为:870毫秒
总结一下JDK动态代理的步骤:
- 需要定义一个接口,因为JDK的代理是基于接口实现的
- 接着定义一个类,实现步骤1中定义的接口
- 接着定义一个Handler类,实现InvocationHandler接口,并且把步骤2中定义的类的引用传进来,然后重写invoke方法就可以调用步骤2中类的方法并增强方法
- 根据被代理类的类加载器、被代理类实现的接口及步骤3中的实现类就可以生成代理类,代理类的类型就是被代理类的接口类型,然后就可以调用方法了
接下来我们看一下生成的代理类到底长什么样
1.5 ProxyGeneratorUtils类
package jdkproxy;
import sun.misc.ProxyGenerator;
import java.io.FileOutputStream;
/**
* @author zhaobin11@baidu.com
*/
public class ProxyGeneratorUtils {
public static void writeProxyClassToHardDisk(String path) {
byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", Car.class.getInterfaces());
try (FileOutputStream out = new FileOutputStream(path)) {
out.write(classFile);
out.flush();
} catch (Exception e) {
e.printStackTrace();
}
}
}
ProxyGenerator可以生成代理类文件
然后把1.4Test类中的注释打开再运行一下
// ProxyGeneratorUtils.writeProxyClassToHardDisk("C:\\Users\\sakura\\Downloads\\study\\src\\jdkproxy\\$Proxy0.class");
在C:\Users\sakura\Downloads\study\src\jdkproxy目录下就可以看到生成的$Proxy0代理类
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import jdkproxy.Moveable;
// 1. 代理类$Proxy0继承自Proxy,实现了被代理类实现的接口Moveable接口
public final class $Proxy0 extends Proxy implements Moveable {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
public $Proxy0(InvocationHandler var1) throws {
// 2. $Proxy0在初始化调用构造函数时,会把实现InvocationHandler接口的类传进去,也就是初始化$Proxy0的父类Proxy里面的InvocationHandler
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
// 3. 调用代理类的move方法时,实际上是调用的实现InvocationHandler接口的类也就是TimeHandler的invoke方法,参数Proxy就是this即当前代理对象,参数method就是Moveable接口的方法move,参数args就是代理类调用move方法时传入的1
public final void move(int var1) throws {
try {
super.h.invoke(this, m3, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
// 3. $Proxy0通过反射拿到了Moveable接口的方法move
m3 = Class.forName("jdkproxy.Moveable").getMethod("move", Integer.TYPE);
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
可以看到
- 代理类$Proxy0继承自Proxy,实现了被代理类实现的接口Moveable接口
- $Proxy0在初始化调用构造函数时,会把实现InvocationHandler接口的类传进去,也就是初始化$Proxy0的父类Proxy里面的InvocationHandler
- $Proxy0通过反射拿到了Moveable接口的方法move,然后当我们调用代理类的move方法时,实际上是调用的实现InvocationHandler接口的类也就是TimeHandler的invoke方法,参数Proxy就是this即当前代理对象,参数method就是Moveable接口的方法move,参数args就是代理类调用move方法时传入的1
2. 模拟实现JDK动态代理
这一部分主要是用反射模拟Proxy的newProxyInstance过程
2.1 Moveable接口
package jdkproxy2;
/**
* @author zhaobin11@baidu.com
*/
public interface Moveable {
void move();
}
2.2 Car实现类
package jdkproxy2;
import java.util.Random;
/**
* @author zhaobin11@baidu.com
*/
public class Car implements Moveable {
@Override
public void move() {
try {
Thread.sleep(new Random().nextInt(1000));
System.out.println("汽车行驶中...");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
2.3 InvocationHandler接口
package jdkproxy2;
import java.lang.reflect.Method;
/**
* @author zhaobin11@baidu.com
*/
public interface InvocationHandler {
void invoke(Object o, Method m);
}
2.4 TimeHandler实现类
package jdkproxy2;
import java.lang.reflect.Method;
/**
* @author zhaobin11@baidu.com
*/
public class TimeHandler implements InvocationHandler {
private Object target;
public TimeHandler(Object target) {
this.target = target;
}
@Override
public void invoke(Object proxy, Method method) {
try {
long start = System.currentTimeMillis();
System.out.println("汽车开始行驶...");
method.invoke(target);
System.out.println("汽车结束行驶,行驶时间为:" + (System.currentTimeMillis() - start) + "毫秒");
} catch (Exception e) {
e.printStackTrace();
}
}
}
2.5 Proxy类
package jdkproxy2;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
/**
* @author zhaobin11@baidu.com
*/
public class Proxy {
public static Object newProxyInstance(Class infce, InvocationHandler h) throws Exception {
String rt = "\r\n";
String methodStr = "";
for (Method m : infce.getMethods()) {
methodStr +=
" @Override" + rt +
" public void " + m.getName() + "() {" + rt +
" try{" + rt +
" Method md=" + infce.getName() + ".class.getMethod(\"" + m.getName() + "\");" + rt +
" h.invoke(this,md);" + rt +
" }catch(Exception e){" + rt +
" e.printStackTrace();" + rt +
" }" + rt +
" }";
}
String str =
"package jdkproxy2;" + rt +
"import java.lang.reflect.Method;" + rt +
"public class $Proxy0 implements " + infce.getName() + " {" + rt +
" public $Proxy0(InvocationHandler h){" + rt +
" this.h=h;" + rt +
" }" + rt +
" private InvocationHandler h;" + rt +
methodStr + rt +
"}";
// 1. 产生代理类的java文件
String fileName = System.getProperty("user.dir") + "/src/jdkproxy2/$Proxy0.java";
File file = new File(fileName);
FileUtils.writeStringToFile(file, str);
// 2. load到内存
Class clazz = ClassLoader.getSystemClassLoader().loadClass("jdkproxy2.$Proxy0");
Constructor ctr = clazz.getConstructor(InvocationHandler.class);
return ctr.newInstance(h);
}
}
2.6 测试类Test
package jdkproxy2;
/**
* @author zhaobin11@baidu.com
*/
public class Test {
public static void main(String[] args) throws Exception {
Car car = new Car();
InvocationHandler h = new TimeHandler(car);
Moveable m = (Moveable) Proxy.newProxyInstance(Moveable.class, h);
m.move();
}
}
运行Test,可以看到
汽车开始行驶...
汽车行驶中...
汽车结束行驶,行驶时间为:806毫秒
在C:\Users\sakura\Downloads\study\src\jdkproxy2目录下可以看到生成的$Proxy0代理类
package jdkproxy2;
import java.lang.reflect.Method;
public class $Proxy0 implements jdkproxy2.Moveable {
public $Proxy0(InvocationHandler h) {
this.h = h;
}
private InvocationHandler h;
@Override
public void move() {
try {
Method md = jdkproxy2.Moveable.class.getMethod("move");
h.invoke(this, md);
} catch (Exception e) {
e.printStackTrace();
}
}
}
其实这个代理类就是用反射拼出来的,然后再反射调用