一个简单的例子模拟JDK动态代理

来源:互联网 发布:java集合框架中抽象类 编辑:程序博客网 时间:2024/05/22 00:20

今天学习了代理模式,并且对JDK动态代理的底层实现有了一定的了解,下面把我学到的和我对动态代理的底层实现写个小例子展示一下。

说到JDK动态代理,大家可能都熟悉。JDK动态代理用起来比较简单。

首先写一个自己的代理类实现InvocationHandler接口

public class MyProxy implements InvocationHandler {    //被代理对象    private Object target;    //通过构造方法注入被代理对象    public MyProxy(Object target) {        this.target = target;    }    @Override    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        //这里是一些业务逻辑...        method.invoke(target, args);        //这里是一些业务逻辑...        return null;    }}

然后在客户端创建一个代理实例,调用代理的方法来实现业务逻辑的增强

public class MyProxyTest {    public static void main(String[] args) {        //创建代理对象实例并且注入被代理对象        MyProxy myProxy = new MyProxy(new Car());        //返回一个代理对象        Moveable car = (Moveable) Proxy.newProxyInstance(Car.class.getClassLoader(), Car.class.getInterfaces(), myProxy);        //调用代理对象的方法是已经加上业务逻辑的被代理对象的方法        car.move();    }}

到这里,一个简单的JDK动态代理的例子就写完了。

可能有人会问了,为什么JDK的动态代理为什么必须要求被代理对象实现接口,调用Proxy.newProxyInstance方法返回的为什么是个接口而不是个类,下面我将用个小例子模拟下JDK动态代理。

主要由以下三个类来完成这次模拟操作。

1. 模拟JDK的Proxy类,提供newProxyInstance()方法返回代理对象

2. 模拟InvocationHandler接口,并且实现这个接口写上自己的业务逻辑,完成对原本方法的“加料”。

3. 客户端调用模拟的Proxy类的newProxyInstance方法返回代理对象,并且调用接口的方法完成代理过程。


1.Proxy类

package design.myjdkproxy;import org.apache.commons.io.FileUtils;import javax.tools.JavaCompiler;import javax.tools.StandardJavaFileManager;import javax.tools.ToolProvider;import java.io.File;import java.lang.reflect.Constructor;import java.lang.reflect.Method;/** * Created by Administrator on 2017/7/18/018. */public class Proxy {    /**     *     * @param infce 被代理类的接口(因为是模拟,所以只是传入一个接口,     *                              真实的JDK动态代理是传入被代理类的多个接口)     * @param h 实现InvocationHandler的实例     */    public static Object newProxyInstance(Class infce, InvocationHandler h) throws Exception{        String methodStr = "";        /**         * 这里就是为什么JDK动态代理为什么要求被代理类必须实现接口         * 生成的代理类会重写原代理类接口的所有方法         * 然后调用用户自己实现的InvocationHandler接口的invoke方法         * 这样用户自己的业务逻辑就得到了实现         */        for(Method method: infce.getMethods()){            methodStr +=  "    @Override\n" +                    "    public void " + method.getName() + "() {\n" +                    "       try{\n" +                    "           Method m = " +infce.getName() + ".class.getMethod(\"" +                                           method.getName() + "\");\n" +                    "           h.invoke(this, m);\n" +                    "       } catch(Throwable e){\n  e.printStackTrace(); \n}\n" +                    "    }\n" ;        }        /**         * 通过字符串的方式动态生成类         */        String str = "package design.myjdkproxy;\n" +                "import java.lang.reflect.Method;\n" +                "public class $Proxy0 implements " + infce.getName() + " {\n" +                "\n" +                //通过构造器传入动态生成的代理类                "    public $Proxy0(InvocationHandler h) {\n" +                "        this.h = h;\n" +                "    }\n" +                "\n" +                "    private InvocationHandler h;\n" +                "\n" + methodStr +"\n"+                "}";        /**         * 生成.java文件         */        String fileName =  "F:\\untitled\\src\\design\\myjdkproxy\\$Proxy0.java";        File file = new File(fileName);        //需要导入commons-io包 将写的字符串的java源文件写入文件中        FileUtils.writeStringToFile(file, str);        /**         * 编译 生成.class字节码文件         */        //拿到编译器,类似于javac的东西        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();        //通过编译器获得文件管理器  参数是语法分析器和一些编码的东西,想了解更多自行百度,这里就设为Null        StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);        //获取文件        Iterable units = fileManager.getJavaFileObjects(fileName);        //获得编译任务        JavaCompiler.CompilationTask task =compiler.getTask(null, fileManager, null, null, null, units);        //进行编译,会生成.class字节码文件        task.call();        //关闭文件管理器        fileManager.close();        /**         * 1. 获得动态生成的代理类的Class,         * 2. 通过Class找到构造器         * 3. 通过构造器返回代理实例         */        ClassLoader classLoader = ClassLoader.getSystemClassLoader();        Class c = classLoader.loadClass( "design.myjdkproxy.$Proxy0");        //LoadClass和Class.forName加载类获得Class的区别是前者不会加载类的静态块和静态变量,后者会        //Class c = Class.forName("design.myjdkproxy.$Proxy0");        //找到参数是InvocationHandler的$Proxy0的构造器        Constructor ctr = c.getConstructor(InvocationHandler.class);        //返回生成的动态代理的实例        return ctr.newInstance(h);    }}

因为是字符串写的动态类,所以不太好写注释,把生成的结果也就是$Proxy0这个实际的代理拿出来看

这是被代理对象实现的接口,里面有两个方法

public interface Movable {    void move();    void test();}
生成的代理对象$Proxy0根据上面的代码应该是也实现了这个接口并且把这两个方法全部实现

package design.myjdkproxy;import java.lang.reflect.Method;public class $Proxy0 implements design.myjdkproxy.Movable {    //通过构造方法传入自定义InvocationHandler    public $Proxy0(InvocationHandler h) {        this.h = h;    }    private InvocationHandler h;    @Override    public void test() {       try{           //获取当前方法的Method对象           Method m = design.myjdkproxy.Movable.class.getMethod("test");           /**            * 调用InvocationHandler的invoke方法            * this:当前代理对象            * m:当前方法的Method对象,用来使用反射执行被代理对象的原始方法            */           h.invoke(this, m);       } catch(Throwable e){            e.printStackTrace();       }    }    @Override    public void move() {       try{           Method m = design.myjdkproxy.Movable.class.getMethod("move");           h.invoke(this, m);       } catch(Throwable e){            e.printStackTrace();       }    }}

果然,生成的代理类实现了原被代理类的接口并且实现了方法,通过调用InvocationHandler的invoke方法,然后再根据用户自定义的业务逻辑对应执行,当然,被代理对象的原本的方法肯定是也要执行的。

下面是被代理对象、自定义的InvocationHandler接口和其实现类


被代理对象Car:

package design.myjdkproxy;/** * Created by Administrator on 2017/7/15/015. */public class Car implements Movable {    @Override    public void move() {             System.out.println("汽车行驶中...");    }    @Override    public void test() {        System.out.println("JDK动态代理测试");    }}

自定义InvocationHandler接口:


public interface InvocationHandler {    /**     * 模拟JDK动态代理的InvocationHandler的invoke方法     * @param obj 代理对象     * @param method 被代理对象原本要执行的方法     * @throws Throwable     */    void invoke(Object obj, Method method) throws Throwable;}

InvocationHandler接口的实现类:


package design.myjdkproxy;import java.lang.reflect.Method;/** * Created by Administrator on 2017/7/18/018. */public class MyHandler implements InvocationHandler {    //被代理对象    private Object target;    //通过构造器注入被代理对象用来执行被代理对象原本的方法    public MyHandler(Object target) {        this.target = target;    }    @Override    public void invoke(Object obj, Method method) throws Throwable{        //对应的业务逻辑...        System.out.println("我在汽车行使前...");        //执行实际的方法也就是被代理对象原本的方法        method.invoke(target);        //对应的业务逻辑...        System.out.println("我在汽车行使后...");    }}

到现在为止,一个模拟的JDK动态代理就剩最后一部了,就是如何调用生成代理对象


package design.myjdkproxy;/** * Created by Administrator on 2017/7/18/018. */public class Client {    public static void main(String[] args) throws Exception {        //将被代理对象绑定到自定义事务处理器上        MyHandler handler = new MyHandler(new Car());        /**         * 返回生成的动态代理的实例         * 这个代理对象也实现了被代理对象的接口         * 被代理对象实现的接口的方法,代理对象也全部实现了         * 所以调用被代理对象的方法:m.move,实际上是调用了代理对象.move方法         * 然后代理对象的move方法又会进入到InvocationHandler的实现类的invoke方法         * 然后就会执行自定义的业务逻辑并且也执行了被代理对象的原本方法( 这里是move()方法)         */        Movable m = (Movable) Proxy.newProxyInstance(Movable.class, handler);        //调用代理对象的方法        m.move();    }}

最后执行结果:

我在汽车行使前...
汽车行驶中...
我在汽车行使后...


好了,一个简单的JDK动态代理的实现就写完了,本人也是刚开始学习代理模式,这篇文章如果有什么不对或者不恰当的地方欢迎各位大神指正,大家共同学习进步。

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 黏痰在嗓子眼很干出不来怎么办 宝宝生病好了不久突然又咳嗽怎么办 7个月的宝宝经常生病怎么办 擤鼻涕鼻子周围红肿爆皮怎么办 洗衣机有鼻涕虫洗过的衣服怎么办 手机丢了里边有穿内衣照片怎么办 脸上不知是过敏还是湿疹流水怎么办 一个月的宝宝鼻子不通气怎么办 六个月的宝宝有清水鼻涕怎么办 一岁三个月宝宝流清鼻涕怎么办 宝宝流清水鼻涕怎么办最简单方法 8个月宝宝流清鼻涕怎么办 7个月宝宝流清鼻涕怎么办 9个月宝宝流清鼻涕怎么办 十一个月宝宝流清鼻涕怎么办 18个月宝宝咳嗽有痰怎么办 2个月宝宝鼻子不通气怎么办 两个月的宝宝鼻塞不通气怎么办 两个月大的宝宝鼻子不通气怎么办 宝宝6个月咳嗽有痰怎么办 6个月的宝宝有痰怎么办 小孩流有点咳嗽和脓鼻涕怎么办 6个月的宝宝流鼻涕该怎么办? 小孩鼻塞不流鼻涕总吸鼻子怎么办 四个多月宝宝感冒鼻塞严重怎么办 怀孕6个月严重感冒鼻塞怎么办 16个月的宝宝上火流鼻血怎么办 小狗咳嗽打了针还不好怎么办 孕妇感冒后鼻涕带血口腔发炎怎么办 宝宝出生17天睡眠不安稳怎么办 月子里宝宝睡觉老是睡不安稳怎么办 孕妇晚期咳嗽鼻涕黄咽喉痛怎么办 狗狗流脓鼻涕拉稀没食欲怎么办 孕妇眼睛充血很快就有眼屎怎么办 婴儿的眼睛流泪生眼屎怎么办呀 刚出生的婴儿眼睛有眼屎怎么办 25天的婴儿鼻子有鼻屎不通怎么办 一个月的宝宝好多鼻屎怎么办 隆鼻取线的时候好多鼻屎怎么办 小孩流浓鼻涕怎么办最简单方法 小孩流黄鼻涕怎么办最简单方法