博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java 动态代理
阅读量:5111 次
发布时间:2019-06-13

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

1、动态代理的目的                                                                      

  动态代理用于代码的分离;将通用代码从各个业务模块中分离出来,不必在每个业务模块中都去实现,这样既减少了开发工作量,也便于维护;

  eg: “吃饭”、“说话”这两个业务都需要用到“张嘴”  “闭嘴”这两行代码,但是张嘴之后和闭嘴之前所做的事情是不一样的,这样将业务模块和通用模块(即 张嘴、闭嘴)分离出来,每次在说话 或者 吃饭的时候,使用通用模块+业务模块 实现吃饭和说话这两件事;

2、动态代理的应用                          

   最典型的应用是基于动态代理技术实现spring框架中的AOP(面向切面编程);  面向切面编程,通过切面代码层,实现业务代码和通用代码的分离,这样各个业务模块可以共用一个通用代码。大概原理如下:切面层代码提供了一个调用其他方法的接口(姑且命名为 invoke),通过反射机制将类 和 方法等参数传入后,便在invoke 里面执行此方法,传入的方法仅仅是invoke方法的一部分,另一部分便是通用代码,这样就将通用代码和业务代码编制在了一起。(实际上sping中的增强就是这么回事)

3、动态代理的原理及实现                                                              

   java提供了两种动态代理方式,一种是JDK动态代理,另一种是cglib动态代理;JDK动态代理仅仅实现了对接口的代理,cglib实现的是对对象的代理;二者相比,chlib综合性能更好一点;

  (1) JDK动态代理

  JDK动态代理是基于 “InvocationHandler接口”  和  “Proxy类” 实现的,其技术基础是 JAVA中的反射机制 及 面向对象中的重写;其中 代理类就是继承于InvocationHandler这个接口的,主要是继承他的invoke 方法;Proxy类用于获取反射对象,调用方法。虽然在本来的业务类中没有invoke方法,但是会在Java虚拟机中生成一个$Proxy0文件,这个文件是继承于本来的业务类,在这个文件中会调用invoke方法。

                     

 下面就根据上图中的代理模型,来写一个简单的JDK动态代理

    ① 首先构建业务层接口 java代码如下:

package Proxy.JDK;import java.lang.*;public interface SayHelloWorld {    public void say(String language);}
业务层接口

 

    ② 基于构建的接口层,创建业务类

package Proxy.JDK;public class menSay implements SayHelloWorld {    @Override    public void say(String language) {        // TODO Auto-generated method stub        System.out.println("HelloWorld!");    }}
实际的业务类

 

    ③ 构建代理类

package Proxy.JDK;import java.lang.reflect.*;;public class proxyClass implements InvocationHandler {    private Object VR_class;        /**     * 返回代理类     * @method: proxyClass() -by fjt     * @TODO:  利用反射机制,被代理业务类产生代理对象     * @param obj 被代理的业务类     * @return      * @return Object     */    public Object getProxy(Object obj)    {        this.VR_class = obj;        return Proxy.newProxyInstance(VR_class.getClass().getClassLoader(), VR_class.getClass().getInterfaces(), this);    }        @Override    /**     * 当调用业务层方法的时候  就会进入这个接口,所以可以认为是覆盖了原先的方法     * 至于是怎么实现覆盖的,为什么会跳到这儿,还不是很清楚     */    public Object invoke(Object proxy, Method method, Object[] args)            throws Throwable {        // TODO Auto-generated method stub        System.out.println("张嘴");        method.invoke(this.VR_class, args);        System.out.println("闭嘴");        return null;    }}
代理类

    ④ 代理的应用

package Proxy.JDK;public class jdk_proxyTest {    public static void main(String[] arges)    {        SayHelloWorld manSay = new menSay();        proxyClass proxyMan = new proxyClass();        //JDK实现的动态代理 是基于接口实现的,因此下面的语句  如果将SayHelloWorld 改为 menSay就会出错        //$Proxy0 cannot be cast to Proxy.JDK.menSay (因为menSay是一个类 Proxy0)        SayHelloWorld proxyMenSay = (SayHelloWorld)proxyMan.getProxy(manSay);          proxyMenSay.say("hi!");            }}
代理的应用

   通过上述例子不难看出,JDK动态代理,实际上就是接口注入,也仅仅是接口注入,通过代理返回的对象也是接口,所以代理的也只能是接口。

  (2) CGLIB动态代理

   JDK动态代理使用起来不太方便,需要写接口、服务类、代理类等,而且只能代理接口,繁琐许多。相比之下,CGLIB就简单许多了。然而CGLIB创建代理对象的时间要花费的比JDK多。所以如果需要频繁的创建对象,并且是接口代理,使用JDK会更好一点。CGLIB是动态创建子类,所以不能代理final变量和方法。

  CGLIB的使用,需要引入如下几个包:

        

  下面先看一下CGLIB的动态代理的实现吧!

package Proxy.CGLIB;public class sayHello {    public int a = 0;    public int b = 0;    public void add()    {        System.out.println("计算结果: " + Integer.toString(a+b));    }}
被代理类

 

package Proxy.CGLIB;import java.lang.reflect.Method;import net.sf.cglib.proxy.Enhancer;import net.sf.cglib.proxy.MethodInterceptor;import net.sf.cglib.proxy.MethodProxy;public class CglibProxy implements MethodInterceptor {    private Object obj;   //被代理的原始对象    public Object creatProxy(Object target)    {        this.obj = target;        Enhancer enhancer = new Enhancer();        enhancer.setSuperclass(this.obj.getClass());  //设置代理目标        enhancer.setCallback(this);        enhancer.setClassLoader(target.getClass().getClassLoader());        return enhancer.create();    }    //阻塞被代理的类的方法调用    @Override    public Object intercept(Object arg0, Method arg1, Object[] arg2,            MethodProxy arg3) throws Throwable {        // TODO Auto-generated method stub        System.out.println("调用之前");        arg3.invokeSuper(arg0, arg2);        System.out.println("调用之后");        return null;    }}
代理类

 

package Proxy.CGLIB;public class cglib_proxyTest {     public static void main(String[] args) {         sayHello calcu = new sayHello();         CglibProxy cglib_proxy = new CglibProxy();         sayHello cal_proxy = (sayHello)cglib_proxy.creatProxy(calcu);         cal_proxy.a = 1;         cal_proxy.b = 1;         cal_proxy.add();     }}
测试代码

  看起来很简单吧。

  CGLIB实现的是代理类,JDK实现的是代理接口;CGLIB是通过intercept这个函数来阻塞代理类函数的调用,也就是说调用被代理类的方法的时候,执行的实际上是intercept这个方法。

 

转载于:https://www.cnblogs.com/tengpan-cn/p/4841033.html

你可能感兴趣的文章
1076 Wifi密码 (15 分)
查看>>
rsync
查看>>
java中的IO操作总结
查看>>
noip模拟赛 党
查看>>
bzoj2038 [2009国家集训队]小Z的袜子(hose)
查看>>
Java反射机制及其Class类浅析
查看>>
Postman-----如何导入和导出
查看>>
面试题17:合并两个排序的链表
查看>>
Jmeter HTTPS接口测试的证书导入
查看>>
移动设备显示尺寸大全 CSS3媒体查询
查看>>
hihoCoder #1831 : 80 Days-RMQ (ACM/ICPC 2018亚洲区预选赛北京赛站网络赛)
查看>>
图片等比例缩放及图片上下剧中
查看>>
jQuery方法大全
查看>>
WebView加载网页详情
查看>>
【转载】Linux screen 命令详解
查看>>
dd命令 建立两颗一模一样的磁盘
查看>>
常用的jquery触屏手机页面特效代码下载
查看>>
background-clip,background-origin
查看>>
C# 如何创建一个Windows服务
查看>>
集群和分布式区别
查看>>