手写动态代理(抄的)
Posted codlover
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了手写动态代理(抄的)相关的知识,希望对你有一定的参考价值。
如图1所示,动态代理的使用
这篇文章讲解动态代理的原理,以及如何手写动态代理。
以下是有关动态代理的使用,这是JDK默认帮我们实现的动态代理。
public class Main implements InvocationHandler
static Person person=new PersonImp();
public static void main(String[] args) throws Throwable
Person o = (Person)Proxy.newProxyInstance(person.getClass().getClassLoader(),
person.getClass().getInterfaces(),new Main());
o.marry();
/**
* 动态代理生成的类调用的方法
* @param proxy
* @param method
* @param args
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
System.out.println("我是媒婆");
method.invoke(person,null);
System.out.println("我去帮你找");
return null;
通过调试看到报道查看对象不是个人类,而是一个经过处理后的类,让我们看看内部做了什么工作。
通过ProxyGenerator生成人的代理类,并且输出到本地磁盘上。
//生成通过代理生成器生成代理class
byte[] $Proxy0s = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]person.getClass());
//输出到本地磁盘上
FileOutputStream fileOutputStream=new FileOutputStream("$Proxy0.class");
fileOutputStream.write($Proxy0s);
fileOutputStream.close();
通过反编译工具查看生成的代理类(代理类的内容删减过,避免内容过长),可??以看出我们调用的是自定义实现的InvocationHander类的调用方法。
可以看出我们调用的结婚方法也是经过代理类改写的方法。
public final class $Proxy0 extends Proxy implements PersonImp
//反射方法
private static Method m3;
//实例化代理类时,把InvocationHander装入
public $Proxy0(InvocationHandler var1) throws
super(var1);
//生成的方法,执行的是InvocationHander的invoke方法
public final void marry() throws
try
super.h.invoke(this, m3, (Object[])null);
catch (RuntimeException | Error var2)
throw var2;
catch (Throwable var3)
throw new UndeclaredThrowableException(var3);
//初始化方法,初始化方法类
static
try
m3 = Class.forName("com.chumo.marry.PersonImp").getMethod("marry");
catch (NoSuchMethodException var2)
throw new NoSuchMethodError(var2.getMessage());
catch (ClassNotFoundException var3)
throw new NoClassDefFoundError(var3.getMessage());
了解了这些后我们开始手写动态代理的实现原理
2,手写动态代理
1,自定义的的的InvocationHandler的接口
public interface CMInvocationHandler
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
2,实现的的的InvocationHandler的接口
public class CMInvocationHandlerImpl implements CMInvocationHandler
private Person person;
//获取代理类对象
public Object getInstance(Person person) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException
this.person=person;
Class<? extends Person> aClass = person.getClass();
System.out.println("被代理的对象是"+aClass);
//自定义的代理类的java和class的生成、装载到JVM中,并且实例化返回给调用者
return CMPorxy.newProxyInstance(new CMClassLoader(),aClass.getInterfaces(),this);
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
System.out.println("我是媒婆");
method.invoke(person,null);
System.out.println("我帮你找媳妇");
return null;
3,自定义代理类,实现的方法的的的newProxyInstance(请注意gengerateSrc方法,这是生成代理类的的java的的文件的核心点)
/**
生成代理对象的代码
*/
public class CMPorxy
private static String ln="\r\n";
public static Object newProxyInstance(CMClassLoader loader,
Class<?>[] interfaces,
CMInvocationHandler h)
throws IllegalArgumentException, IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException
//1、生成源代码
String proxySrc=gengerateSrc(interfaces);
//2、将生成的源代码输出到磁盘,保存为.java文件
String fileNmae=CMPorxy.class.getResource("").getPath();
File f=new File(fileNmae+"$Proxy0.java");
try
FileWriter fw=new FileWriter(f);
fw.write(proxySrc);
fw.flush();
fw.close();
catch (IOException e)
e.printStackTrace();
//3、编译源代码,并生成.class文件
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager manager = compiler.getStandardFileManager(null, null, null);
Iterable<? extends JavaFileObject> objects = manager.getJavaFileObjects(f);JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, null, null, objects); task.call(); manager.close(); //4、将.class文件中的内容,动态加载到JVM中来 Class<?> $Proxy0 = loader.findClass("$Proxy0");//获得类 //5、返回被代理的对象 Constructor<?> constructor = $Proxy0.getConstructor(CMInvocationHandler.class);//获取构造方法 f.delete();//删除java文件 return constructor.newInstance(h);//实例化
//生成java文件,这是关键点,通过这里可以看出如何创建一个代理类
private static String gengerateSrc(Class<?>[] interfaces)
StringBuffer src=new StringBuffer();
src.append("package com.chumo.proxy;"+ln);
src.append("import java.lang.reflect.InvocationHandler;\n" +ln+
"import java.lang.reflect.Method;\n" +ln+
"import java.lang.reflect.Proxy;\n" +ln+
"import java.lang.reflect.UndeclaredThrowableException;"+ln);
src.append("public final class $Proxy0 implements "+interfaces[0].getName()+" "+ln);src.append("CMInvocationHandler h;"+ln); src.append("public $Proxy0(CMInvocationHandler h)"+ln); src.append("this.h=h;"+ln); src.append(""+ln); for (Method method : interfaces[0].getMethods()) src.append("public "+method.getReturnType().getName()+" "+method.getName()+" () throws Throwable"+ln); src.append("Method m="+"Class.forName(\""+interfaces[0].getName()+"\").getMethod(\""+method.getName()+"\",new Class[]);"+ln); src.append("this.h.invoke(this,m,null);"+ln); src.append(""+ln); src.append(""); return src.toString();
如图4所示,我们需要使用自定义的类加载器,加载自定义路径的类文件装入到JVM中。
/**
代码生成、编译、重新load到JVM中
*/
public class CMClassLoader extends ClassLoader
private File baseDir;
public CMClassLoader()
//获取当前文件的所在路径
String path = CMClassLoader.class.getResource("").getPath();
this.baseDir=new File(path);
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException
String className=CMClassLoader.class.getPackage().getName()+"."+name;
if (baseDir!=null)
//获取class文件,使用类加载器加载到JVM
File classFile=new File(baseDir,name.replace("\.","/")+".class");
if (classFile.exists())
try (FileInputStream fileInputStream = new FileInputStream(classFile);ByteArrayOutputStream outputStream=new ByteArrayOutputStream();)
byte[] bytes=new byte[1024];
int len;
while ((len=fileInputStream.read(bytes))!=-1)
outputStream.write(bytes,0,len);
//装入class
return defineClass(className,outputStream.toByteArray(),0,outputStream.size());
catch (FileNotFoundException e)
e.printStackTrace();
catch (IOException e)
e.printStackTrace();
finally
classFile.delete();//删除class文件
return super.findClass(name);
5,运行,成功
(PS:如果代码不能运行,请检查路径,包名是否正确,以及代理的的java的的文件和类文件生成的位置,已经类加载器类加载器是否能读取类文件)
3,总结
通过以上代码得出,JAVA动态代理的原理,通过遍历被代理类的所有方法,调用调用的引用方法调用执行。
也就是说我们使用的人类的结婚方法其实是被重写过的。
并且代理类的的的的的Java和类文件的生成使用结束后,都会删除,保证了程序员的无感知的使用。
总结:1,JDK动态代理实现了类的增强,但是通过原理可以看出,过多的使用动态代理,效率是不高的。
? ? ? ? ? ?2,JDK动态代理是通过接口实现的,而出名的春天中的cjlib不需要接口的。
以上是关于手写动态代理(抄的)的主要内容,如果未能解决你的问题,请参考以下文章