JDK动态代理的简单实现

Wesley13
• 阅读 526

1. 先理一下动态代理实现的思路:

    实现功能: 自己定义一个类 Proxy, 通过Proxy的静态方法 newProxyInstance(Class intface,InvocationHandler h)返回代理对象, intface: 被代理类的接口对象, h: InvocationHandler的实例对象

    1). 声明一段动态代理类的源码( 动态产生代理类 )

    2). 编译动态代理类的源码( JDK Compiler API ), 产生代理类

    3). 通过 ClassLoader 加载这个代理类, 创建一个代理类的实例对象

    4). return 返回这个代理对象

2. 代码实现: 

为什么代理类的类名为 $Proxy0?

这是因为 Java中动态代理, 生成的代理类的类名就是 $Proxy0, 依葫芦画瓢而已, 

可以用一个动态代理对象 proxy, 来验证:

System.out.println(proxy.getClass().getName());    //输出com.sun.proxy.$Proxy0

public class Proxy {
    /**
     * @param intface
     *            被代理类的接口的类对象
     * @param h
     *            InvocationHandler的实例对象
     * @return proxy 生成的动态代理对象
     * @throws Exception
     */
    @SuppressWarnings("unchecked")
    public static <T> T newProxyInstance(Class<T> intface,InvocationHandler h) throws Exception {
        String srcStr = "";    // 代理类$Proxy0的源码, 字符串形式
        String methodStr = "";    // 代理类$Proxy0的所有代理方法, 字符串形式
        String rt  = "\r\n";    // Windows平台下的换行符
        
        // 动态生成代理类$Proxy0的所有代理方法
        for(Method m : intface.getMethods()) {
            methodStr += 
            "    @Override" + rt +
            "    public void " + m.getName() + "() {" + rt +
            "        try {" + rt +
            "            Method method = " + intface.getName() + ".class.getMethod(\"" + m.getName() + "\");" + rt +
            "            h.invoke(this, method, null);" + rt +    // 暂时只支持无参方法
            "        } catch (Throwable e) { e.printStackTrace(); }" + rt +
            "    }";
        }
        
        // 拼接代理类$Proxy0的源码
        srcStr +=
        "package proxy.my;" + rt +
        "import java.lang.reflect.Method;" + rt +
        "import java.lang.reflect.InvocationHandler;" + rt +
        "public class $Proxy0 implements " + intface.getName() + "{" + rt +
        "    private InvocationHandler h;" + rt +
        "    public $Proxy0(InvocationHandler h) {" + rt +
        "        this.h = h;" + rt +
        "    }" + rt +
        "  "+methodStr + rt + 
        "}";
        
        // 生成代理类的java源文件
        String srcFilePath = System.getProperty("user.dir") + "/bin/proxy/my/$Proxy0.java";
        File srcFile = new File(srcFilePath);
        // 使用commons-io-2.2.jar中的FileUtils, 向一个指定的文件中写入指定的字符串
        FileUtils.writeStringToFile(srcFile, srcStr);
        
        // 编译这个代理类的java源文件
        // 获取编译器
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        // 获取文件管理者
        StandardJavaFileManager fileMgr= compiler.getStandardFileManager(null, null, null);
        // 获取文件
        Iterable<? extends JavaFileObject> compilationUnits = fileMgr.getJavaFileObjects(srcFilePath);
        // 获取编译任务
        CompilationTask task = compiler.getTask(null, fileMgr, null, null, null, compilationUnits);
        // 编译
        task.call();
        
        // 获取代理类的类加载器
        ClassLoader classLoader = Proxy.class.getClassLoader();
        // 加载代理类
        Class<?> clazz = classLoader.loadClass("proxy.my.$Proxy0");
        // 获取代理类的构造器
        Constructor<?> constructor = clazz.getConstructor(InvocationHandler.class);
        // 通过代理类的构造器, 创建一个代理类的实例, 也就是代理对象, 返回代理对象
        T proxy = (T) constructor.newInstance(h);
        return proxy;
    }
}

3. 测试自己实现的 Proxy.newProxyInstance() 方法

先定义一个 ProxyInvocationHandler 类, 该类实现了java.lang.reflect.InvocationHandler接口, 实现invoke()方法

public class ProxyInvocationHandler implements InvocationHandler {
    private Object target;    // 被代理的目标对象

    public ProxyInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("来不及解释了, 快上车");
        method.invoke(target, args);    // 调用目标对象的方法
        System.out.println("下车了, 快记住车牌号");
        return null;    // 暂时只支持无参方法
    }
}

测试动态代理

public static void main(String[] args) throws Throwable {
        Moveable car = new Car();
        InvocationHandler h = new ProxyInvocationHandler(car);
        Moveable carProxy = Proxy.newProxyInstance(Moveable.class, h);
        carProxy.move();
}

Console输出:    // 其实我想当个老司机, 天天飙车

来不及解释了, 快上车

飙车中...

下车了, 快记住车牌号

Moveable 接口

public interface Moveable {
    public void move();
}

Moveable 接口的实现类 Car

public class Car implements Moveable {
    @Override
    public void move() {
        System.out.println("飙车中...");
    }
}

生成的代理类 $Proxy0, 其路径: bin/proxy/$Proxy0.java

package proxy;
import java.lang.reflect.Method;
import java.lang.reflect.InvocationHandler;
public class $Proxy0 implements proxy.Moveable{
    private InvocationHandler h;
    public $Proxy0(InvocationHandler h) {
        this.h = h;
    }
      @Override
    public void move() {
        try {
            Method method = proxy.Moveable.class.getMethod("move");
            h.invoke(this, method, null);    // 暂时只支持无参方法
        } catch (Throwable e) { e.printStackTrace(); }
    }
}

总结:

    调用 Proxy 实例的方法时, 都会被 InvocationHandler 的实例对象 invoke() 方法所捕获  

why?

生成的动态类 $Proxy0, 重写了被代理类 Car 的 move() 方法, 在 move() 方法里, 都是在调用InvocationHandler 实例对象的 invoke() 方法

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Wesley13 Wesley13
2年前
JDK动态代理和Cglib的动态代理
最简单的是静态代理方法,即代理模式,这里就不多啰嗦了。。重点说一下JDK的动态代理和Cglib的动态代理吧先说JDK的,需要被代理的类需要有接口,否则无法实现package proxy.dynamic;public interface IBook {void add();}实现接口
Easter79 Easter79
2年前
Spring的两种代理JDK和CGLIB的区别浅谈
一、原理区别:java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。而cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。1、如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP 2、如果目标对象实现了接口,可以
Stella981 Stella981
2年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Easter79 Easter79
2年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Wesley13 Wesley13
2年前
Java 动态代理实践AOP
大家都知道Spring中AOP是通过Java动态代理实现的,今天就来简单学习下demo。Java动态代理主要有两个核心类,InvocationHandler和Proxy。/{@codeInvocationHandler}istheinterfaceimplementedbythe<iinvo
Wesley13 Wesley13
2年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Easter79 Easter79
2年前
Spring的两种动态代理:Jdk和Cglib 的区别和实现
一、原理区别:java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。而cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。1、如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP 2、如果目标对象实现了接口,可以
Wesley13 Wesley13
2年前
Java动态代理机制解析
动态代理是指在运行时动态生成代理类。不需要我们像静态代理那个去手动写一个个的代理类。生成动态代理类有很多方式:Java动态代理,CGLIB,Javassist,ASM库等。这里主要说一下Java动态代理的实现。Java动态代理InvocationHandler接口Java动态代理中,每一个