前言
上一节中我们简单概述了JDK中的动态代理模式,但是难免心中存疑,我们可以很明确的知道最核心的地方就是生成代理类的过程,也就是我们的这一块代码:Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this) 针对于这个点,我们来说一下在JDK的动态代理模式中所存在的疑问:
- 1.这个clazz.getClassLoader是什么东西
- 2.这个clazz.getInterfaces()是什么东西
- 3.以及最后我们所传递的this(其实是一个InvocationHandler对象)这三个东西究竟是什么
我们这看到了两个比较重要的东西
- 1.this对象,在前一节中我们在阅读JavaDoc的时候就已经发现了,InvocationHandler这个对象非常重要
- 2.众所周知,类加载器是一个神气的东西,我在我的博客中Springboot启动方式中,已经说明了,在不违反规定的前提下,打破Jar in Jar的界限。。说的有点高大上了,我有点不好意思了,咳咳,就是去寻找我们的.class文件
这次,我们来重点实现下自定义InvocationHandler和类加载器 ,下面我们开始来看代码
下面我们开始看代码
Person类
/**
* @author Zerox
* @date 2019/6/1 19:23
* 首先这是一个接口,为了干什么呢?给广大单身男性提供方便的
*/
public interface Person {
/**
* 寻找真爱,
*/
public void findTrueLove();
}
接口实现类 —> XiaoWang
/**
* @author Zerox
* @date 2019/6/1 19:34
*/
public class XiaoWang implements Person {
private String name = "小王";
private String sex = "男";
/**
* 小王寻找真爱
*/
public void findTrueLove() {
System.out.println("我的名字是" + this.name + ",性别是:" + this.sex);
System.out.println("我想要做的事情是寻找真爱!" );
System.out.println("我的要求是: 肤白貌美大长腿");
}
}
下面我们来实现自定义的MeiPo类
/**
* @author Zerox
* @date 2019/6/2 13:16
*/
public class ConsumerMeiPo implements ConsumerInvocationHandler{
// 获取被代理的对象
private Person xiaoWang;
// 生成xiaoWang的代理对象
public Object getProxyInstance(Person xiaoWang) throws Exception {
// 赋值
this.xiaoWang = xiaoWang;
// 获取类对象
Class<?> clazz = xiaoWang.getClass();
// 返回代理对象
return ConsumerProxy.newProxyInstance(new ConsumerClassLoader("myConsumerClassLoader"),clazz.getInterfaces(),this);
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("开始进行海选");
System.out.println("---------------");
method.invoke(this.xiaoWang,args);
System.out.println("如果合适就准备办事!");
return null;
}
}
下面我们来实现自定义的ConsumerProxy类
/**
* @author Zerox
* @date 2019/6/3 9:34
*/
public class ConsumerProxy {
private static String ln = "\r\n";
public static Object newProxyInstance(ConsumerClassLoader loader, Class<?>[] interfaces, ConsumerInvocationHandler h)
throws Exception {
// 1. 生成源代码
String proxySrc = generateSrc(interfaces[0]);
// 2. 将生成的源代码输出到磁盘,保存为java文件
// 2.1 文件路径
String filePath = ConsumerProxy.class.getResource("").getPath();
// 2.2 将文件转换成byte[]字节数组,理论上IO的最大传输效率
byte[] data = new byte[1024*8];
// 2.3 将文件写入本地
data = proxySrc.getBytes();
File file = new File(filePath + "$Proxy0.java");
FileOutputStream fos = new FileOutputStream(file);
int len = 0;
fos.write(data);
fos.flush();
fos.close();
// 3. 编译源代码,并生成.class文件
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager manager = compiler.getStandardFileManager(null,null,null);
Iterable iterable = manager.getJavaFileObjects(file);
JavaCompiler.CompilationTask task = compiler.getTask(null,manager,null,null,null,iterable);
task.call();
manager.close();
//4. 将.class文件中的内容,动态加载到JVM中
Class proxyClass = loader.findClass("$Proxy0");
//5. 返回被代理后的对象
Constructor c = proxyClass.getConstructor(ConsumerInvocationHandler.class);
file.delete();
return c.newInstance(h);
}
/**
* 用代码生成源代码
* @param interfaces
* @return
*/
private static String generateSrc( Class<?> interfaces) {
StringBuilder src = new StringBuilder();
src.append("package blog.consumer;" + ln);
src.append("import java.lang.reflect.Method;" + ln);
src.append("public class $Proxy0 implements "+ interfaces.getName() + "{" + ln); //
src.append("ConsumerInvocationHandler h;" + ln);
src.append("public $Proxy0(ConsumerInvocationHandler h) {" + ln);
src.append("this.h = h;" + ln);
src.append("}" + ln );
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 ex){" + ln);
src.append("ex.printStackTrace();" + ln);
src.append("}" + ln);
src.append("}" + ln);
}
src.append("}");
return src.toString();
}
}
下面我们来实现自定义的ConsumerInvocationHandler接口
/**
* @author Zerox
* @date 2019/6/2 13:16
*/
public interface ConsumerInvocationHandler {
/**
* 实现接口里面的方法
* @param proxy 代理对象,这里面其实就是我们的ConsumerMeiPo
* @param method 代理对象实例重新编码以及重新分配的方法,参考上一讲中的super.h.invoke()
* @param args 代理实体方法里面的参数(在我们这里,暂时没啥用,我也没用到)
* @return
* @throws Throwable
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}
下面我们来实现自定义的ConsumerClassLoader接口
/**
* @author Zerox
* @date 2019/6/2 13:24
*/
public class ConsumerClassLoader extends ClassLoader{
private String classLoaderName;
private final String fileExtension = ".class";
public ConsumerClassLoader(ClassLoader parent, String classLoaderName) {
super(parent); // 显示指定该类加载器的父类加载器
this.classLoaderName = classLoaderName;
}
public ConsumerClassLoader(String classLoaderName) {
super(); // 将系统类加载器(AppClassLoader)当做该类的父加载器
this.classLoaderName = classLoaderName;
}
/**
* 加载类对象,我们将我们所学的JVM和自定义类加载器关联到一起吧
* @param className
* @return
* @throws ClassNotFoundException
*/
@Override
protected Class<?> findClass(String className) throws ClassNotFoundException {
byte[] data = this.loadClassData(className);
className = ConsumerClassLoader.class.getPackage().getName() + "." + className.replace("/",".");
return this.defineClass(className,data,0,data.length);
}
public byte[] loadClassData(String name) {
InputStream is = null;
byte[] data = null;
ByteArrayOutputStream baos = null;
try{
String filePath = ConsumerClassLoader.class.getResource("").getPath();
File file = new File(filePath+""+name + this.fileExtension);
is = new FileInputStream(file);
baos = new ByteArrayOutputStream();
byte[] buff = new byte[1024];
int ch = 0;
while(-1 !=(ch = is.read(buff))) {
baos.write(buff,0,ch);
}
data = baos.toByteArray();
}catch (Exception ex) {
ex.printStackTrace();
}finally {
try{
is.close();
baos.close();
}catch (Exception ex) {
ex.printStackTrace();
}
}
return data;
}
}
输出结果如下图:
具体的实现都在代码里面,不懂得留言,欢迎交流!