手写动态代理(抄的)

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

以上是关于手写动态代理(抄的)的主要内容,如果未能解决你的问题,请参考以下文章

代理模式-3(手写实现JDK动态代理)

代理模式-3(手写实现JDK动态代理)

jdk动态代理: 从源码,到字节码,到自己手写动态代理

根据动态代理手写一个AOP

根据动态代理手写一个AOP

手写实现JDK的动态代理