博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
利用反射生成JDK动态代理
阅读量:4563 次
发布时间:2019-06-08

本文共 5584 字,大约阅读时间需要 18 分钟。

利用反射生成JDK动态代理

在Java的java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过使用这个类和接口可以生成JDK动态代理类和动态代理对象

1.使用Proxy和InvocationHandler创建动态代理

Proxy提供了用于创建动态代理类和代理对象的静态方法,也是所有动态代理类的父亲。如果在程序中为一个或多个接口动态的生成实现类,就可以用proxy来创建动态代理类;如果需要为一或多个接口动态的创建实例,也可以使用Proxy来创建动态代理实例

Proxy提供了如下两个方法用于创建动态代理类和代理对象。

  • static Class<?> getProxyClass(ClassLoader loader,Class<?>... interfaces):创建一个动态代理类的所对应的Class对象,该代理类将实现interface所制定的多个接口。loader参数指定生成动态代理类的类加载器。
  • static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h):直接创建一个动态代理对象。该对象的实现类实现了interfaces指定的系列接口,执行代理对象的每个方法时都会被替换执行InvocationHandler对象的invoke方法。

实际上,第一种方法生成动态代理之后,如果程序需要通过该代理类来创建对象,依然需要传入一个InvocationHandler对象。也就是说,系统生成的每个代理对象都有一个与之关联的InvocationHandler对象。

创建动态代理对象时需要实现一个或多个接口定义的方法,而InvocationHandler对象的作用是—当执行动态代理对象里的方法时,实际上会替换成调用InvocationHandler对象的invoke方法。

程序中可以采用先生成一个动态代理类,然后通过动态代理类来创建代理对象的方式生成一个动态代理对象,代码如下

package com.gdut.test0516;import java.lang.reflect.Constructor;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;class MyInvocationHandler implements InvocationHandler{    @Override    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        return null;    }}interface  Foo{}public class ProxyTest1 {    public static void main(String[] args) throws Exception{       InvocationHandler handler = new MyInvocationHandler();       Class proxyClass = Proxy.getProxyClass(Foo.class.getClassLoader(),new Class[]{Foo.class});       //获取ProxyClass中带一个InvocationHandler参数的实例        Constructor ctor = proxyClass.getConstructor(new Class[]{InvocationHandler.class});        //调用ctor的newInstance方法创建实例        Foo f = (Foo)ctor.newInstance(new Object[]{handler});    }}

也可以简化成

package com.gdut.test0516;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;class MyInvocationHandler implements InvocationHandler{    @Override    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        return null;    }}interface  Foo{}public class ProxyTest1 {    public static void main(String[] args) throws Exception{      Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),new Class[]{Foo.class},new MyInvocationHandler());    }}

下面程序示范了使用Proxy和InvocationHandler来生成动态代理对象

package com.gdut.test0516;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;interface Person{    void walk();    void sayHello(String name);}class MyInvocationHandler2 implements InvocationHandler{    /**     * 执行动态代理对象的所有方法时,都会被替换成执行如下的invoke方法     * @param proxy 代表动态代理对象     * @param method 代表正在执行的方法     * @param args 代表调用目标方法时传入的实参     * @return     * @throws Throwable     */    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        System.out.println("-----正在执行的方法:"+method);        if(args != null){            System.out.println("下面执行该方法时传入的实参为:");            for (Object val:args) {                System.out.println(val);            }        }else{            System.out.println("调用该方法没有实参!");        }        return null;    }}public class ProxyTest2 {    public static void main(String[] args) throws Exception {        InvocationHandler handler = new MyInvocationHandler();        Person p = (Person)Proxy.newProxyInstance(Person.class.getClassLoader(),new Class[]{Person.class},new MyInvocationHandler2());        p.walk();        p.sayHello("孙悟空");    }}

2. 动态代理和AOP

开发实际应用的软件系统时,通常会存在相同代码段重复出现的情况

我们大多数会将深色代码定义成一个方法,然后让另外三段代码段直接调用该方法即可。

但采用这种方式来实现代码复用仍存在一个重要问题,虽然代码段1,代码段2,代码段三和深色代码分开了,但是又和一个特定的方法耦合了!最理想的状态是代码块1,代码块2,代码块3能执行深色代码块部分,又无需以硬代码的方式直接调用深色代码的方法,这时可以通过动态代理达到这种效果。

由于JDK动态代理只能为接口创建代理,下面先提供一个Dog接口

interface Dog{    void info();    void run();        }

实际情况通常时,软件系统会为该接口提供一个或多个实现类,例如:GunDog

public class GunDog implements Dog{    @Override    public void info() {        System.out.println("我是一只猎狗");    }    @Override    public void run() {        System.out.println("我迅速奔跑");    }}

此处假设info(),run()两个方法分别代表代码段1,代码段2,那么要求:程序执行info()、run()方法时能调用某个通用方法,但又不想以硬代码方式调用该方法。下面提供一个DogUtil类,该类里包含两个通用方法。

public class DogUtil {    public void method1(){        System.out.println("=====模拟第一个通用方法=====");    }    public void method2(){        System.out.println("=====模拟第一个通用方法=====");    }}

借助动态代理,将method1(),method2()两个通用方法分别插入info(),run()方法中执行

这个程序的关键在于下面的MyInvocationHandler3类,该类是InvocationHandler实现类,该实现类的invoke()方法将会作为代理对象的方法实现

package com.gdut.test0516;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;public class MyInvocationHandler3 implements InvocationHandler {    private Object target;    public void setTarget(Object target){        this.target = target;    }    @Override    public Object invoke(Object proxy, Method method, Object[] args) throws Exception  {      DogUtil du = new DogUtil();      du.method1();      Object result = method.invoke(target,args);      du.method2();      return result;    }}

下面再提供MyProxyFactory类,该对象专为指定的target生成动态代理实例

public class MyProxyFactory {    public static Object getProxy(Object target)throws Exception{        MyInvocationHandler3 handler3 = new MyInvocationHandler3();        handler3.setTarget(target);        return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),handler3);    }    }

上面的动态代理工厂类提供了一个getProxy()方法,该方法为target对象生成一个动态代理对象,这个动态代理对象与target实现了相同接口,所以具有相同的public方法——从这个意义上看,动态代理对象可以当成target对象使用。当程序调用动态代理对象的指定方法时,实际上将变为执行MyInvocationHandler3对象的invoke方法。

下面是测试效果

转载于:https://www.cnblogs.com/yumiaoxia/p/9047279.html

你可能感兴趣的文章
qq登陆错误提示
查看>>
bzoj 1192: [HNOI2006]鬼谷子的钱袋 思维 + 二进制
查看>>
没写完,没调完,咕咕咕的代码
查看>>
Android Studio使用技巧:导出jar包
查看>>
Problem E. TeaTree - HDU - 6430 (树的启发式合并)
查看>>
Kafka序列化和反序列化与示例
查看>>
win10下VS2010中文输入法切换为英文卡死
查看>>
retinex相关代码汇总
查看>>
Cortex-M3 异常返回值EXC_RETURN
查看>>
kettle 转换字段遇到问题(couldn't get row from result set)——摘
查看>>
nginx首页根据IP跳转
查看>>
【2019-08-20】有点目标,有点计划,有点目的
查看>>
【2019-09-10】美,真的跟年龄无关
查看>>
【2019-09-28】少,但更好
查看>>
【2019-09-13】耐心观察是一种技能
查看>>
mysql数据库2-常用命令
查看>>
安卓开发环境搭建(转)
查看>>
Harris角点检测
查看>>
Struts2的处理流程及为Action的属性注入值
查看>>
设计中最常用的CSS选择器
查看>>