zoukankan      html  css  js  c++  java
  • Java动态代理

    Java动态代理的实现方式主要有两种:

       1、基于JDK的动态代理:动态代理类和被代理类必须继承相同的接口(没有实现接口的类,无法通过这种方式实现动态代理。所以也称基于接口的动态代理)

       2、基于CGLIB的动态代理:没有上面的限制

    在业务中使用动态代理,一般是为了给需要实现的方法添加预处理或者后续操作,同时又不干预原先的业务实现。    把一些基本业务和主要的业务逻辑分离,Spring的AOP就是基于动态代理实现的

    关于动态代理或者说AOP的好处:在业务处理代码中,通常都有日志记录、性能统计、安全控制、事务处理、异常处理等操作,尽管使用OOP(面向对象),可以通过封装或继承达到代码的重用,但仍然存在同样的代码分散到各个方法中。为了解决此类问题,AOP(面向切面)诞生了,它采取横向抽取机制,将分散在各个方法中的重复代码抽取出来,然后再程序编译或运行阶段,再将这些抽取出来的代码应用到需要执行的地方。这种横向抽取机制,是OOP无法做到的,因为OOP实现的是父子关系的纵向继承,而AOP则对OOP的缺陷做了补充,两者相辅相成。

    下面进入正题


    1、基于JDK的动态代理

    基于JDK的动态代理是通过两个类      1、InvocationHandler(调用处理 接口)   2、Proxy(代理 类)         来实现的

     实现基于JDK的动态代理               首先要创建具有一定功能的接口,然后创建接口的实现类(也就是被代理类)

    譬如,我们假设周杰伦要开演唱会了             为了实现这一逻辑,我需要创建一个歌手接口Singer,接口里有演唱的方法sing。之后我们为这个接口创建一个实现类(也就是我们的被代理类)ZhouJielun

    代码如下:

    interface Singer{
        void sing();
    }
    //接口实现类(也就是被代理类)
    class ZhouJielun implements Singer{
        @Override
        public void sing() {
            System.out.println("周杰伦在演唱会进行演唱");
        }
    }

    在开演唱会之前,造型师势必会对他的造型进行修理,这个过程是必须的,而这个过程它不由歌手本人负责,因为每个人的精力都是有限的。这个逻辑换成工作开发就是什么呢?每个人都负责一定的功能模块,如果一个人负责过多的任务,那么就很有可能出现各种各样的问题,同时也会造成各种功能代码耦合在一起,不便于维护。         希望这个解释帮助大家更清晰的认识到AOP的好处。

    接着说如何实现动态代理,现在,接口创建好了,被代理的类也创建好了 ,之后我们创建一个代理工厂。

    这就需要用到上面说的 InvocationHandler接口 和 Proxy 类了(先贴代码,再说流程)

    首先,我们创建一个MyInvocationHandler类并实现InvocationHandler接口,代码如下:

    class MyInvocationHandler implements InvocationHandler {
        //这是 被代理类的对象
        private Object obj;
    
        //捆绑方法:将被代理的对象捆绑到 MyInvocationHandler(调用处理器) 类
        //当然,该操作也可以在构造函数中完成
        public void bind(Object obj){
            this.obj = obj;
        }
    
        //当我们通过”代理对象“调用方法时,就会自动调用如下方法:invoke()
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            //method:即为要调用的被代理类的方法
            //args:即为要调用方法的参数
    
            doBefore();
            //调用被代理类中的方法
            Object returnValue = method.invoke(obj,args);
            doAfter();
    
            return returnValue;
        }
        //需要插入的前置代码
        private void doBefore(){
            System.out.println("造型师帮助化妆");
        }
        //需要插入的后置代码
        private void doAfter(){
            System.out.println("造型师帮助卸妆");
        }
    }

    接着创建一个代理工厂类,代码如下:

    class ProxyFactory{
        public static Object getProxyInstance(Object obj){
            MyInvocationHandler handler = new MyInvocationHandler();
            handler.bind(obj);
            return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),handler);
        }
    }

    在main方法中测试一下:

    public class ProxyTest {
        public static void main(String[] args) {
            ZhouJielun zhouJielun = new ZhouJielun();
            Singer proxyInstance = (Singer) ProxyFactory.getProxyInstance(zhouJielun);
            proxyInstance.sing();
        }
    }

    简单说一下整个过程:在Proxy类中有一个静态方法newProxyInstance(ClassLoader loader,Class[] interfaces,InvocationHandler h),它需要传入一个类加载器,一个代表对象实现接口的Class数组,一个调用处理器,然后返回一个实现了所有传入接口代理对象。           在上面的代码中,我们选择创建了一个代理工厂,它的静态方法可以根据接受的对象,返回需要的代理类。       

    InvocationHandler顾名思义,就是调用处理器。   就是它完成了额外功能的插入:它里面内置了一个被代理类的对象 和 一个invoke()方法,当我们通过代理类调用任意方法时,都会调用invoke()方法。       它的代码逻辑很简单:通过反射调用被代理对象的同名方法,而我们在原方法执行前后,可以插入一些其他代码。        比如在执行这段方法前,进行日志记录。     这样一来,成功将不同的功能分离,使得代码更加易于阅读。


    2、基于CGLIB的动态代理

    还没学

  • 相关阅读:
    JSON1
    program的发展史及两个方法
    统计字符出现的次数
    美国十大web2.0公司背后的故事
    web history-----JavaScript 的起源故事
    Baidu_Map
    My json(Demo)
    program发展史及 forecast
    js事件类型
    字符统计与正则表达式
  • 原文地址:https://www.cnblogs.com/simplelong/p/13639035.html
Copyright © 2011-2022 走看看