Java JDK动态代理原理详解:
参考文章:https://blog.youkuaiyun.com/qq_31859365/article/details/82902349
动态代理介绍
Java动态代理与静态代理相对,静态代理是在编译期就已经确定代理类和真实累的关系,并且声称代理类的,二动态代理是在运行期利用jvm的反射机制声称代理类,这里是直接生成类的字节码,然后通过类加载器将字节码文件加载到Java虚拟机并执行的一种技术。现在主流的Java动态代理有两种实现方式:一种是jdk自带的,就是我们所说的jdk动态代理,一种是开源社区的开源项目CGLIB,本文主要讲jdk代理的实现原理;
jdk动态代理的实现是在运行时,根据一组接口定义,使用Proxy、InvocationHandler等工具生成一个代理类和代理类实例;
如果还理解不了什么是动态代理,请看这里:Java代理模式详解
JDK动态代理相关API
- Java 编译API:
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
- InvocationHandler(接口):
该接口是proxy代理实例的调用处理程序实现的一个接口,每个proxy代理实例都有一个关联的调用处理程序;在代理实例调用方法时,方法调用被编发分配到调用处理程序的invoke方法; - proxy类:
该类就是用来创建一个代理对象的类,它提供了很多方法,但是我们最常用的是newProxyInstance方法;该方法有三个参数,具体参数介绍如下:
1)、CLassLoader loader:一个ClassLoader对象,定义了由那个classloader对象生成的代理类进行加载;
2)、Class<?> interfaces:一个interfaces对象数组,表示我们将要给我们的代理独享提供一组什么样的接口,如果我们提供了这样的一个接口对象数组,那么也就是声明了代理类实现了这些接口,代理类就可以调用接口中声明的所有方法;
3)、InvocationHandler h:一个InvocationHandler对象,表示的是当动态代理对象调用方法的时候会关联到哪一个InvocationHandler对象上,并最终由其调用;
JDK动态代理过程
- 定义一个接口:Movable,接口中有方法:move();
- 定义一个方法调用接口InvocationHandler;
- 定义一个方法调用接口实现类,用于调用真实对象的方法的同时添加自己的业务逻辑代码;
- 实现接口的目标类(它要被代理);
- 定义一个处理器TimeHandler,重载构造方法,重写invoke函数,添加自己的处理逻辑;
- 代理逻辑实现过程编写:
1)、定义方法字符串,用于保存方法信息;
2)、接口方法获取;
3)、生成实现类的对象信息
4)、动态生成 $Proxy.java文件;
5)、加载生成的 $Proxy.class字节码指令到内存中;
6)、返回生成对象的引用;
代码详解
- 定义一个接口:
//定义一个接口;
public interface Moveable {
void move();
}
- 实现接口的目标类
package com.example.springbootdemotest.proxy.principle;
//实现接口的目标类
public class Tank implements Moveable
{
@Override
public void move() {
System.out.println("Tank Move...");
}
}
- 定义一个方法调用接口
package com.example.springbootdemotest.proxy.principle;
import java.lang.reflect.Method;
//定义一个方法调用接口
public interface InvocationHandler {
void invoke(Object o, Method m);
}
- 定义一个调用接口方法实现类
package com.example.springbootdemotest.proxy.principle;
import java.lang.reflect.Method;
public class TimeHandler implements InvocationHandler{
private Object target;
public TimeHandler(Object target){
this.target = target;
}
@Override
public void invoke(Object o, Method m) {
//此处自己编写执行真实对象方法“前”业务逻辑
long start = System.currentTimeMillis();
System.out.println("StartTime:"+start);
System.out.println(o.getClass().getName());
try {
m.invoke(target);
}catch (Exception e){
e.printStackTrace();
}
//此处自己编写执行真实对象方法“后”业务逻辑
long end = System.currentTimeMillis();
System.out.println("EndTime:"+end);
System.out.println("time:"+(end-start));
}
}
- 编写接口实现过程:
package com.example.springbootdemotest.proxy.principle;
import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
//代理对象,
public class Proxy {
public static Object newProxyInstance(Class infce, InvocationHandler h) throws Exception {
//1、定义方法字符串,用于保存方法信息;
String methodStr = "";
String rt = "\r\n";
//2、接口方法获取;
Method[] methods = infce.getMethods();
for (Method m : methods) {
//生成方法信息;
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){e.printStackTrace();}" + rt +
"}";
}
//3、生成实现类的对象信息
String src ="package com.example.springbootdemotest.proxy.principle;"+ rt +
"import java.lang.reflect.Method;" +rt +
"public class $Proxy1 implements "+ infce.getName() +"{" +rt +
" public $Proxy1(InvocationHandler h){" +rt +
" this.h = h;" +rt +
" }" +rt +
" com.example.springbootdemotest.proxy.principle.InvocationHandler h;" +rt +
methodStr+
"}";
//4、动态生成$Proxy.java文件;
//D:\software\gitRespository\springbootdemo\src\main\java\com\example\springbootdemotest\proxy\principle\$Proxy1.java
String fileName = System.getProperty("user.dir")+"/springbootdemo-test/src/main/java/com/example/springbootdemotest/proxy/principle/$Proxy1.java";
File f = new File(fileName);
FileWriter fw = new FileWriter(f);
fw.write(src);
fw.flush();
fw.close();
//5、执行动态编译过程;
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null,null,null);
Iterable units = fileMgr.getJavaFileObjects(fileName);
JavaCompiler.CompilationTask t = compiler.getTask(null,fileMgr,null,null,null,units);
t.call();
fileMgr.close();
//6、加载生成的$Proxy.class到内存中;
//D:\software\gitRespository\springbootdemo\springbootdemo-test\src\main\java\com\example\springbootdemotest\proxy\clibPackage\MainClass.java
//URL[] urls = new URL[]{new URL("file:"+System.getProperty("user.dir")+"/src/main/java")};
URL[] urls = new URL[]{new URL("file:"+"D:/software/gitRespository/springbootdemo/springbootdemo-test/src/main/java")};
URLClassLoader ul = new URLClassLoader(urls);
Class c = ul.loadClass("com.example.springbootdemotest.proxy.principle.$Proxy1");
System.out.println(c);
Constructor ctr = c.getConstructor(InvocationHandler.class);
Object m = ctr.newInstance(h);
//m.move();
return m;
}
public static void main(String[] args) throws Exception{
Tank tank = new Tank();
InvocationHandler h = new TimeHandler(tank);
Moveable m = (Moveable) Proxy.newProxyInstance(Moveable.class,h);
m.move();
}
}
- 执行结果如下:
class com.example.springbootdemotest.proxy.principle.$Proxy1
StartTime:1576233182481
com.example.springbootdemotest.proxy.principle.$Proxy1
Tank Move...
EndTime:1576233182482
time:1
Process finished with exit code 0
- 上述过程完整模拟jdk动态代理过程,且相关类名同jdk动态代理类名相同,如有疑问,请看:Java代理模式详解