手写动态代理(抄的)

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

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

时间: 2024-11-11 10:27:11

手写动态代理(抄的)的相关文章

&lt;数据结构系列1&gt;封装自己的数组——手写动态泛型数组

哈哈,距离上一次写博客已经快过去半个月了,这这这,好像有点慢啊,话不多说,开始我们的手写动态泛型数组 首先是我们自己写一个自己的动态数组类,代码如下所示: public class Array<E> { //成员变量:数据,大小 private E[] data; private int size; //构造函数,传入数组的容量capacity public Array(int capacity) { data=(E[])new Object[capacity]; size=0; } //无参

css手写动态加载中...

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" con

springmvc 动态代理 JDK实现与模拟JDK纯手写实现。

首先明白 动态代理和静态代理的区别: 静态代理:①持有被代理类的引用  ② 代理类一开始就被加载到内存中了(非常重要) 动态代理:JDK中的动态代理中的代理类是动态生成的.并且生成的动态代理类为$Proxy0 静态代理实例1.创建一个接口: package proxy; public interface People { public void zhaoduixiang()throws Throwable; } 2.创建一个实现类,张三,张三能够吃饭,张三可以找对象 package proxy;

设计模式(23)-----代理设计模式-----动态代理

一:自己手写动态代理 1.1,把一串静态的字符串(代理类)加载输出,编译,加载到内存,并且反射获取这个对象 public interface Moveable { public void move(); } 1 package com.DesignPatterns.al.Dynamic5; 2 3 import java.util.Random; 4 5 public class Tank implements Moveable { 6 7 @Override 8 public void mov

java反射与动态代理的理解

一.什么是反射机制? 反射的官方定义是这样的:在运行状态中,对于任意的一个类,都能够知道这个类的所有属性和方法,对任意一个对象都能够通过反射机制调用一个类的任意方法,这种动态获取类信息及动态调用类对象方法的功能称为java的反射机制. 讲的通俗一点的话就是,对于jvm来说,.java文件必须要先编译为.class文件才能够被jvm执行,所以在编译为.class文件的过程中,对象的类型都会被指定好,比如说 User user.那么如果说我想在代码运行的过程中获取到对象的类型呢?或者说程序在运行过程

动态代理工具类

我刚刚想着每次写动态代理的时候都会写非常多的代码,有点不值得,所以我写了一个实现动态代理的工具类.用户能够用JDK动态代理也能够使用CGLIB动态代理,我的ProxyUtils中提供了三种开发动态代理的方式.在实际用的过程中,能够继承Intercepter这个抽象类实如今逻辑代码前后加入控制代码.假设控制代码返回true那么证明能够通过,假设控制代码返回false说明验证不通过,假设不通过那么就返回你逻辑代码中返回的"0"值,假设你逻辑代码返回对象.那么会返回null,假设是其它类型则

JDK动态代理深入理解分析并手写简易JDK动态代理(上)

原文引用https://www.dazhuanlan.com/2019/08/26/5d6300df6f20f/ 博客真的是好几个月没更了,2019新年第一篇,继续深入动态代理,前两篇简单分析了动态代理的实现原理之后,这次继续深入了解具体的实现方式,并手写一套简易的动态代理已加强理解: 本博客关于Java动态代理相关内容直达链接: JDK动态代理浅析 Cglib动态代理浅析 JDK动态代理深入理解分析并手写简易JDK动态代理(上) JDK动态代理深入理解分析并手写简易JDK动态代理(下) 博客真

关于利用动态代理手写数据库连接池的异常 java.lang.ClassCastException: com.sun.proxy.$Proxy0 cannot be cast to java.sql.Connection

代码如下: final Connection conn=pool.remove(0); //利用动态代理改造close方法 Connection proxy= (Connection) Proxy.newProxyInstance(conn.getClass().getClassLoader(), conn.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object pro

设计模式 - 动态代理原理及模仿JDK Proxy 写一个属于自己的动态代理

本篇文章代码内容较多,讲的可能会有些粗糙,大家可以选择性阅读. 本篇文章的目的是简单的分析动态代理的原理及模仿JDK Proxy手写一个动态代理以及对几种代理做一个总结. 对于代理模式的介绍和讲解,网上已经有很多优质的文章,我这里就不会再过多的介绍了,这里推荐几篇优质的文章作为参考: 给女朋友讲解什么是代理模式 轻松学,Java 中的代理模式及动态代理 另外,我的 github 仓库对应目录中也有相关的基础示例代码:https://github.com/eamonzzz/java-advance