码迷,mamicode.com
首页 > 其他好文 > 详细

手写动态代理(抄的)

时间:2019-09-23 22:27:44      阅读:134      评论:0      收藏:0      [点我收藏+]

标签:const   def   tar   bytearray   you   并且   iterable   odex   nec   

如图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不需要接口的。

手写动态代理(抄的)

标签:const   def   tar   bytearray   you   并且   iterable   odex   nec   

原文地址:https://www.cnblogs.com/codlover/p/11575209.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!