Java 动态代理介绍及用法

来源:互联网 发布:淘宝开店交税吗 编辑:程序博客网 时间:2024/05/25 05:36

Java 动态代理介绍及用法

一,静态代理模式的特点

在之前的文章中 java代理模式 已经介绍里java里的(静态)代理模式

下面是上文静态代理类的例子:

public class ProxyBear implements Hitable<Dog>{      private Hitable<Dog> f = null;      public ProxyBear(){          if (null == f){              f = new Fox();          }      }      @Override      public void hit(Dog g){          if (null != f){              System.out.println("Bear hit InterDogChicken!");              f.hit(g);              System.out.println("Bear bite InterDogChicken!");          }      }  

从代码可以看出, 上面代理类可以增强被代理的对象的某个方法。
但是被代理对象的类型Hitable是指定1个接口(或抽象类)

也就是讲上静态代理类只适用于某一种指定的接口实现类, 如果某个对象没有实现揍狗接口, 而是其他另1个接口, 那么就必须新建1个代理类的。即使新代理类也是实现同样的功能

二. 动态代理的1个需求

就用会上文的例子

我们宜家有两个接口。殴打和调戏接口..

殴打接口 Hitable

public interface Hitable<T> {    public void hit(T o);}

调戏接口 Molestable

public interface Molestable<T> {    public void molest(T o);}

其中狐狸类实现了殴打狗接口

public class Fox implements Hitable<Dog> {    @Override    public void hit(Dog g){        this.sap(g);        this.uppercut(g);    }    //闷棍    private void sap(Dog g){        System.out.println("give " + g.getName() + " a Sap!");    }    //上勾拳    private void uppercut(Dog g){        System.out.println("give " + g.getName() + " a Uppercute!");    }}

狼类实现调戏狗接口

public class Wolf implements Molestable<Dog> {    @Override    public void molest(Dog o) {        System.out.println("wolf laugh at the dog!");        System.out.println("wolf ruffle the dog!");    }}

这个是万恶的狗类

public class Dog {    private String name;    public Dog(String name){        this.setName(name);    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    @Override    public String toString() {        return "Dog [name=" + name + "]";    }}

问题来了, 依家狗太恶, 狼和狐狸都要找头熊(代理)看着才敢去惹狗

如果用静态代理, 就如本文一开始的那个代理类, 则只能单独为狐狸or 狼代理, 这个需求需要两个代理类, 因为狗和狐狸实现的是两个不同的接口

如下图
Alt text

那么有没有一种代理类, 可以同时代理多个接口的实现类呢,java 里动态代理就利用了反射实现了这个功能。

三,动态代理的例子

我们利用动态代理类重新写了1个熊类出来:

public class dynamicProxyBear implements InvocationHandler {    //delegate means proxy    //the object which will be delegated    private Object delegate;    public Object bind(Object delegate){        this.delegate = delegate;        /**         * This method newProxyInstance return ***one of the interfaces*** of delegate object(properties object of this class)         *          * @param          *      1.ClassLoader loader -> usually use delegate object's class loader         *      2.Class<?>[] interfaces -> collection of the interface which delegate object has implemented         *      3.InvocationHandler h -> this         * @return         */        return Proxy.newProxyInstance(delegate.getClass().getClassLoader(),                                       delegate.getClass().getInterfaces(), this);    }    /**     * This method will replace all the method owned by delegate object.     * @param proxy -> the delegate object, never use it's method directly, otherwise will lead to Dead loop     * @param method -> the method (once execute will fall into this invoke method) object of delegate object      * @param args  -> parameters of the mothod.     * @return     * @throws Throwable     */    @Override    public Object invoke(Object proxy, Method method, Object[] args)            throws Throwable {        Object result;        if (args.length < 1){            return method.invoke(this.delegate,args);        }        //bear watching        System.out.println("bear is watching " + args[0].toString());        result = method.invoke(this.delegate,args);        System.out.println("bear leaved " + args[0].toString());        return result;    }}

下面对上面的代码进行分析。

首先这个代理类要实现接口InvacationHandler, 而且不会直接提供要代理的方法(行为), 而是生成1个被代理完成(强化)的新对象。

delegate 成员对象

    //delegate means proxy    //the object which will be delegated    private Object delegate;

这个成员变量就是为了存放被代理的对象(Fox or wolf 对象)

bind(Object delegate) 方法

public Object bind(Object delegate){        this.delegate = delegate;        /**         * This method newProxyInstance return ***one of the interfaces*** of delegate object(properties object of this class)         *          * @param          *      1.ClassLoader loader -> usually use delegate object's class loader         *      2.Class<?>[] interfaces -> collection of the interface which delegate object has implemented         *      3.InvocationHandler h -> this         * @return         */        return Proxy.newProxyInstance(delegate.getClass().getClassLoader(),                                       delegate.getClass().getInterfaces(), this);    }

这个bind方法就是为了绑定要代理的对象, 并return 1个被代理后的对象啦

注意 参数Object delegate就是本文例子中的狼or狐狸对象, 因为参数类型是Object, 也就是说它可以同时适用于各种类(前提是实现了某种接口)

Proxy.newProxyInstance 这个静态方法就是返回1个被代理后的对象。

第一个参数是类加载器, 通常使用被代理对象的类加载器, 通常我们就用delegate.getClass().getClassLoader()

第二个参数是被代理对象实现的接口列表, 通常我们就用delegate.getClass().getInterface()

第三个参数是这个代理类对象本身

值得注意的是, 这个方法返回的也是1个Object类型, 在client端中, 我们可以将返回对象强制转换成被代理类的1个接口类型对象, 但是不能强制转换成被代理类本身的1个对象。

至于这个静态方法里面的细节这里就不讲了。

重写方法invoke

    /**     * This method will replace all the method owned by delegate object.     * @param proxy -> the delegate object, never use it's method directly, otherwise will lead to Dead loop     * @param method -> the method (once execute will fall into this invoke method) object of delegate object      * @param args  -> parameters of the mothod.     * @return     * @throws Throwable     */    @Override    public Object invoke(Object proxy, Method method, Object[] args)            throws Throwable {        Object result;        if (args.length < 1){            return method.invoke(this.delegate,args);        }        //bear watching        System.out.println("bear is watching " + args[0].toString());        result = method.invoke(this.delegate,args);        System.out.println("bear leaved " + args[0].toString());        return result;    }}

这个方法就是真正实现代理功能的关键,
它有三个参数

Object proxy对象代表被代理的对象

Method method代表要被代理的方法(实际上是所有方法)

args 就是代表方法的参数列表了。

返回值Object result 自然就是代表被代理方法的返回值了。

我们在里面先加入 熊看着狗的代码…

然后执行被代理的方法本身

然后加入熊离开的代码。

最后要返回被代理方法的返回值。

而且, 千万不要直接调用proxy的方法, 因为proxy直接调用方法会再次进入这个代理对象invoke方法, 导致死循环。

Client端代码

public static void g(){        Dog g = new Dog("InterDogChicken");        dynamicProxyBear bear = new dynamicProxyBear();        Hitable<Dog> newFox = (Hitable<Dog>) bear.bind(new Fox());        newFox.hit(g);        Molestable<Dog> newWolf = (Molestable<Dog>) bear.bind(new Wolf());        newWolf.molest(g);    }

上面代码中 首先要定义1个动态代理(熊)类, 然后熊返回1个新的狐狸对象(必须强制转换为Fox or Wolf类的1个接口, 而不能是本身)
然后再执行这个新的狐狸对象的方法。

输出结果

bear is watching Dog [name=InterDogChicken]give InterDogChicken a Sap!give InterDogChicken a Uppercute!bear leaved Dog [name=InterDogChicken]bear is watching Dog [name=InterDogChicken]wolf laugh at the dog!wolf ruffle the dog!bear leaved Dog [name=InterDogChicken]

这样的话无论狐狸or狼都被代理了

四,动态代理的关键点和小结

值得注意是, 上面重写的invoke的方法, 我并没有提到Fox or 狼类的方法名字。

实际上, 动态代理会利用java反射机制拦截被代理对象的所有方法。 接管对方法里管理。

所以动态代理是非常适合增加日志监控功能的。

例如在每个方法执行前和执行后都加日志的话, 动态代理模式避免了去修改以前的业务类。

被代理对象必须至少实现1个接口才能被动态代理对象代理, 因为动态代理bind方法返回的对象必须强制转换成1个接口引用。


而且,动态代理只对被代理对象的第一层入口方法有效果。

例如下面这个对象有3个方法

void a(){}void b(){}void c(){  a();  b();}

那么无论动态代理对象执行 a b c那个方法, 都是被拦截一次, 而不是当执行c时, c a b分别被拦截3次

五, 另1个例子, 利用动态方法实现监控功能

动态代理有时也会被利用于接管被代理对象的方法, 实现监控功能

例如下面要实现1个学生类, 它一次执行study/exam/graduate 三个方法,

学习接口:

public interface Studyable {    public void study();    public int exam();    public void graduate();}

学生类

package proxyPattern.dynamicProxy;public class Student implements Studyable {    private String name;    private int examResult;    public Student(String name, int examResult) {        super();        this.name = name;        this.examResult = examResult;    }    @Override    public String toString() {        return "Student [name=" + name + ", examResult=" + examResult + "]";    }    @Override    public void study() {        System.out.println(this + " is studying");    }    @Override    public int exam() {        System.out.println(this + " is examming");        return this.examResult;    }    @Override    public void graduate() {        System.out.println(this + " graduated");    }}

但是我们加入一个要求, 就是当某个学生对象exam分数少于60时, graduate执行失败

有人说直接修改学生类最快, 但是项目中很多情况下你没有修改上层类的权限, 这个时候我们可以利用动态代理来实现:

动态代理类 StudentMonitor

package proxyPattern.dynamicProxy;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;public class StudentMonitor implements InvocationHandler{    private Object delegate;    private boolean flag = false;    public Object bind(Object delegate){        this.delegate = delegate;        return Proxy.newProxyInstance(delegate.getClass().getClassLoader(),                                       delegate.getClass().getInterfaces(), this);    }    @Override    public Object invoke(Object proxy, Method method, Object[] args)            throws Throwable {        System.out.println("start to execute " + method.getName());        Object result;        if(method.getName().equals("exam")){            result = method.invoke(this.delegate, args);            if ((Integer)result >= 60){                this.flag = true;            }else{                this.flag = false;            }            return result;        }        if(method.getName().equals("graduate")){            if(this.flag == true){                return method.invoke(this.delegate, args);            }            System.out.println(this.delegate + " exam result < 60,cannot graduate");            return null;        }        return method.invoke(this.delegate, args);    }}

在重写的invoke方法中, 我们可以加入方法名字判断实现我们的需求

client代码

public static void h(){        StudentMonitor sm = new StudentMonitor();        //must be translated to a interface object of the class, cannot be translate to a class object        Studyable st = (Studyable)sm.bind(new Student("Mike", 59));        st.study();        st.exam();        st.graduate();        Studyable st2 = (Studyable)sm.bind(new Student("bill", 61));        st2.study();        st2.exam();        st2.graduate();    }

输出

start to execute studyStudent [name=Mike, examResult=59] is studyingstart to execute examStudent [name=Mike, examResult=59] is exammingstart to execute graduateStudent [name=Mike, examResult=59] exam result < 60,cannot graduatestart to execute studyStudent [name=bill, examResult=61] is studyingstart to execute examStudent [name=bill, examResult=61] is exammingstart to execute graduateStudent [name=bill, examResult=61] graduated
0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 评书杨家将刘兰芳播讲全集 刘兰芳评书大全在线听 刘兰芳评书呼家将93回 呼家将评书刘兰芳全集 刘兰芳评书网赵匡胤 呼家将刘兰芳评书全集 刘兰芳评书赵匡胤演义 刘兰芳评书网在线收听 刘兰芳赵匡胤演义100回 刘兰芳评书在线收听 刘兰芳评书赵匡胤演义100 刘兰芳岳飞传全集mp3在线听 刘兰芳评书大全岳飞传 评书赵匡胤刘兰芳播讲 扬家将评书刘兰芳播讲 岳飞传刘兰芳全集117 刘兰芳评书杨家将93回在线听 评书岳飞传刘兰芳播讲117回 刘兰芳评书赵匡胤演义100回 刘兰芳评书杨家将全集 刘兰芳评书杨家将全传 刘兰芳评书岳飞全传 刘兰芳评书岳飞传大全117集 刘兰芳评书网赵匡胤演义 刘兰芳平书杨家将全集 刘兰芳评书呼家将全集 评书杨家将刘兰芳广播全集 评书网刘兰芳岳飞传 评书杨家将刘兰芳mp3下载 评书岳飞传刘兰芳全集 刘兰芳岳飞传全集mp3下载 评书网刘兰芳杨家将 刘兰芳的评书岳飞传 刘兰芳评书大全呼家将 刘兰芳评书打包下载 杨家将评书刘兰芳全集下载 评书呼家将刘兰芳 全集 刘兰芳评书下载mp3打包下载 刘兰芳评书呼家将征西 刘兰芳评书大全杨家将 刘兰芳评书岳飞传全集mp3