jdk提供的动态代理,依赖接口 InvocationHandler ,并且实现其中的Invoke 方法。
底层实现的具体过程
- jdk 首先需要获取到被代理类的接口。
- jdk 会动态创建一个代理类,并且实现被代理类的即可
- 同时,这个新代理类获取到 被代理类的引用,生成java 类文件。 会额外创建一个java文件,后期删除掉即可
- 通过编译生成 class 文件。 会额外创建一个 class文件,classLoader 将其加载到jvm中,并且获取到类的定义后,删除即可。
- 将class 文件里的类 加载到 jvm 中。实例化代理类对象。
- 代理类就可以执行了。
自定义实现简单的动态代理
依照例子,实现自己的简单代理。需要以下几个部分。
一、 几个外围类
- 依赖的接口,Person
package com.zxy.st.lv1.jdk;/*** @author : wb-zxy450245* @date : 2019/10/10*/public interface Person {void findLove();}
- 被代理的求婚者 小星星
package com.zxy.st.lv1.jdk;
/**
* @author : wb-zxy450245
* @date : 2019/10/10
*/
public class Xiaoxingxing implements Person {
@Override
public void findLove() {
System.out.println("我叫小新星,我的择偶标准是:");
System.out.println("高富帅");
System.out.println("180高,体重70公斤左右");
System.out.println("有车有房");
}
}
- 调用代理的main 方法
package com.zxy.st.lv1.jdk;
import com.zxy.st.lv1.jdk.custom.GPMeipo;
import sun.misc.ProxyGenerator;
import java.io.FileOutputStream;
/**
* @author : wb-zxy450245
* @date : 2019/10/10
*/
public class TestFindLove {
public static void main(String[] args) {
//new Xiaoxingxing().findLove(); //直接调用
/* 使用jdk自带的动态代理 调用目标方法
Person meipo = (Person) new Meipo().getInstance(new Xiaoxingxing());
meipo.findLove();*/
//new GPMeipo().getInstance(new Xiaoxingxing());
//自定义动态代理实现调用目标方法
Person meipo = (Person) new GPMeipo().getInstance(new Xiaoxingxing());
meipo.findLove();
/*
* 原理:
* 1. 拿到 被代理的类实例,并且获取它的接口
* 2. 创建新的代理类,并且实现被代理类的接口
* 3. 并且同时获取到被代理类的引用
* 4. 编译生成 新代理类的 class字节码
* 5. 并加载到jvm中
* */
/*try {
//获取字节码内容,这个字节码是 jvm自己创建的代理对象的自己码,自动生成的代理类
byte[] data = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{Person.class});
FileOutputStream os = new FileOutputStream("D:\\studyProject\\fate-second\\src\\main\\java\\com\\zxy\\st\\lv1\\$Proxy0.class");
os.write(data);
os.close();
} catch (Exception ex) {
ex.printStackTrace();
}*/
}
}
二、以下为核心自定义代码
- 自定义 接口 InvocationHandler -》 GPInvocationHandler
需定义 invke方法,与jdk源码中的定义相同。
package com.zxy.st.lv1.jdk.custom;
import java.lang.reflect.Method;
/**
* @author : wb-zxy450245
* @date : 2019/10/10
*/
public interface GPInvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}
- 自定义的 代理公共类 GPProxy
里面需要做:
- 生成新代理类代码,通过反射技术,将代码拼接后,写入新的java类文件
- 通过 proxy 自带的功能,编译java文件,生成class 文件。
- 通过自定义的 GPClassLoader, 加载class 文件,并且将class文件中的类信息,加载到jvm中,并且自动生成的代理类的定义。
- 通过 动态代理类的定义 获取到 构造方法,然后进行实例化,获取到代理类的实例,并且返回
package com.zxy.st.lv1.jdk.custom;
import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
/**
* 生成代理对象的代码
*
* @author : wb-zxy450245
* @date : 2019/10/10
*/
public class GPPorxy {
private static String ln = "\r\n";
public static Object newProxyInstance(GPClassLoader classLoader, Class<?>[] interfaces, GPInvocationHandler h) throws IllegalArgumentException {
try {
// 1. 生成源代码
String proxySrc = generateSrc(interfaces[0]);
//2. 将生成的源代码 输出到磁盘,保存为.java 文件
String filePath = GPPorxy.class.getResource("").getPath();
File f = new File(filePath + "$Proxy0.java");
FileWriter fw = new FileWriter(f);
fw.write(proxySrc);
fw.flush();
fw.close();
//3. 将.java文件编译成 class 文件
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager manager = compiler.getStandardFileManager(null, null, null);
Iterable iterable = manager.getJavaFileObjects(f);
//编译 代码。 最终获得 class 文件。
JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, null, null, iterable);
task.call();
manager.close();
//4. 将class 文件内容动态加载到 jvm中
Class proxyClass = classLoader.findClass("$Proxy0");
//4. 返回被代理后的 代理对象
Constructor c = proxyClass.getConstructor(GPInvocationHandler.class);
f.delete();
return c.newInstance(h);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 生成源代码
*
* @return
*/
private static String generateSrc(Class<?> interfaces) {
StringBuffer src = new StringBuffer();
//package com.zxy.st.lv1.jdk.custom
src.append("package com.zxy.st.lv1.jdk.custom;" + ln);
src.append("import java.lang.reflect.Method; " + ln);
src.append("public class $Proxy0 implements " + interfaces.getName() + "{" + ln);
src.append("GPInvocationHandler h;" + ln);
src.append("public $Proxy0(GPInvocationHandler h) {" + ln);
src.append("this.h = h ;" + ln);
src.append("}");
for (Method m : interfaces.getMethods()) {
src.append("public " + m.getReturnType().getName() + " " + m.getName() + "(){" + ln);
src.append("try{" + ln);
src.append("Method m=" + interfaces.getName() + ".class.getMethod(\"" + m.getName() + "\",new Class[]{});" + ln);
src.append("this.h.invoke(this,m,null);" + ln);
src.append("}catch(Throwable e){e.printStackTrace();}" + ln);
/*if (m.getReturnType().getName().equals("void")){
src.append("return null")
}*/
src.append("}" + ln);
}
src.append("}");
return src.toString();
}
}
- 自定义的 clssLoader 类 GPClassLoder
- 获取到 代理公共类 生成的代理类的class 文件。
- 将文件内容读取,并且将文件中的类加载到jvm中。
- 返回类的定义 ``` package com.zxy.st.lv1.jdk.custom;
import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException;
/**
- class 文件。已经获得。
- 编译
- 重新load 到 jvm *
- @author : wb-zxy450245
@date : 2019/10/10 */ public class GPClassLoader extends ClassLoader {
private File baseDir;
public GPClassLoader() {
String basePath = GPClassLoader.class.getResource("").getPath(); this.baseDir = new File(basePath);}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
String className = GPClassLoader.class.getPackage().getName() + "." + name;
if (baseDir.exists()) {
//找到编译好的class 文件
File classFile = new File(baseDir, name.replaceAll("\\.", "/") + ".class");
if (classFile.exists()) {
//如果源文件存在。则进行加载到 jvm中
FileInputStream in = null;
ByteArrayOutputStream out = null;
try {
in = new FileInputStream(classFile);
out = new ByteArrayOutputStream();
byte[] buff = new byte[1024];
int len;
while ((len = in.read(buff)) != -1) {
out.write(buff, 0, len);
}
classFile.delete();
//jvm 提供 class 加载到 jvm的方法
return defineClass(className, out.toByteArray(), 0, out.size());
} catch (Exception e) {
e.printStackTrace();
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (null != out) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
return super.findClass(name);
}
}
4. 代理类 媒婆 GPMeipo
模仿 正常情况下写的代理类,他继承了自定义的 handler 接口,并且完成以下能力
- 获取到 系统创建的新的代理类的实例。
- 定义了 invke 方法。 method.invoke(this.target, args); 执行了被代理类的 方法。
package com.zxy.st.lv1.jdk.custom;
import com.zxy.st.lv1.jdk.Person;
import java.lang.reflect.Method; import java.lang.reflect.Proxy;
/**
- @author : wb-zxy450245
@date : 2019/10/10 */ public class GPMeipo implements GPInvocationHandler {
private Person target;
public Object getInstance(Person target) {
this.target = target; Class<? extends Person> clazz = target.getClass(); return GPPorxy.newProxyInstance(new GPClassLoader(), clazz.getInterfaces(), this); // return null;}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("invoke 被执行-开始");
method.invoke(this.target, args);
System.out.println("invoke 被执行-结束");
return null;
}
}
```
以上是自定义的 动态代理实现的原理。
