zoukankan      html  css  js  c++  java
  • java设计模式:代理模式(二)动态代理

    java的代理模式有两种,一种是jdk自带的动态代理,一种是cglib动态代理。

    1、jdk动态代理:

    /**
     * 1.创建接口
     */
    public interface Subject {
        int sellBook();
    
        String speak();
    }
    /**
     * 2.创建真实对象
     */
    public class RealSubject implements Subject {
        @Override
        public int sellBook() {
            System.out.println("卖书");
            return 1;
        }
    
        @Override
        public String speak() {
            System.out.println("说话");
            return "张三";
        }
    }
    /**
     * 3.创建真实对象的代理类,代理类要实现InvocationHandler接口,表示使用jdk动态代理
     * 代理模式的重要作用是用于在被代理类的方法执行前后切入代码
     */
    public class JdkProty implements InvocationHandler {
        /**
         * 因为需要处理真实对象,所以需要将真实对象传过来,通过构造方法赋值
         */
        private Subject subject;
    
        public JdkProty(Subject subject) {
            this.subject = subject;
        }
    
        /**
         * @param proxy     被代理的对象
         * @param method    正在调用的方法
         * @param args      方法的参数
         * @return
         * @throws Throwable
         */
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("调用代理类");
            if (method.getName().equals("sellBook")) {
                int invoke = (int) method.invoke(subject, args);
                System.out.println("调用了卖书的方法");
                return invoke;
            } else if (method.getName().equals("speak")) {
                String string = (String) method.invoke(subject, args);
                System.out.println("调用的是说话的方法");
                return string;
            }
            return null;
        }
    }
    /**
    *测试方法
    */
    public class TestDemo { public static void main(String[] args) { Subject subject = new RealSubject(); JdkProty jdkProty = new JdkProty(subject); /** * 代理对象(本方法可以封装入代理类中,被代理类对象作为obj参数传递) *subject.getClass().getClassLoader():被代理类对象的加载器 * subject.getClass().getInterfaces():得到被代理类对象的所有接口 * jdkProty:与被代理类对象相关的代理类对象 */ Subject proxyInstance = (Subject) Proxy.newProxyInstance(subject.getClass().getClassLoader(), subject.getClass().getInterfaces(), jdkProty); proxyInstance.sellBook(); proxyInstance.speak(); } }

     使用jdk动态代理的前提条件:被代理的类一定要有实现的接口

    /**
    * java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口用来完成动态代理。
    * InvocationHandler接口:
    * 当我们通过代理对象调用一个方法时,这个方法的调用就会被转发为由InvocationHandler这个接口的invoke方法进行调用
    * Proxy类
    * 该类提供了一个创建动态代理对象的方法
    */

     2、cglib动态代理

    Cglib 动态代理是针对代理的类, 动态生成一个子类, 然后子类覆盖被代理类中的方法, 如果是private或是final类修饰的方法,则不会被重写。

    CGLIB是一个功能强大,高性能的代码生成包。它为没有实现接口的类提供代理,为JDK的动态代理提供了很好的补充。通常可以使用Java的动态代理创建代理,但当要代理的类没有实现接口或者为了更好的性能,CGLIB是一个好的选择。

    使用cglib动态代理需要导包:cglib-nodep-2.2.2.jar

    /**
     * 1.创建真实类对象
     */
    public class Ennigeer {
    
        //可以被代理的方法
        public int eat() {
            System.out.println("工程师在吃饭");
            return 1;
        }
    
        //final修饰的方法不会被cglib生成的子类覆盖,可以通过代理类调用,但是不能执行代理类的方法
        public final String work() {
            System.out.println("工程师在工作");
            return "hello";
        }
    
        //private修饰的方法不会被生成的子类覆盖,不能通过代理类调用
        private void play() {
            System.out.println("工程师在玩");
        }
    }
    public class CglibProxy implements MethodInterceptor {
    
        /**
         * 该intercept方法类似于jdk的invoke方法发
         * @param o         被代理类对象
         * @param method    被代理类的方法对象
         * @param objects   传进去的参数
         * @param methodProxy 代理方法的代理对象
         * @return
         * @throws Throwable
         */
        @Override
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            System.out.println("方法调用之前");
            Object invoke = methodProxy.invokeSuper(o, objects);
            System.out.println("方法调用之后");
            return invoke;
        }
    
        /**
         * 得到代理类
         * @param obj 被代理类对象
         * @return     代理类对象
         */
        public Object getProxy(Object obj) {
            //创建加强器,用来创建被代理类的对象
            Enhancer enhancer = new Enhancer();
            //为加强器指定要代理的业务类(即:为下面生成的代理类指定父类)
            enhancer.setSuperclass(obj.getClass());
            //设置回调,对代理类上所有方法的调用,都会调用CallBack(),而CallBack()方法则需要intercept方法进行拦截
            enhancer.setCallback(this);
            //创建被代理类对象的特殊格式的对象
            return enhancer.create();
        }
    }
    public class TestDemo {
        public static void main(String[] args) {
            Ennigeer ennigeer = new Ennigeer();
            CglibProxy cglibProxy = new CglibProxy();
            Ennigeer proxy = (Ennigeer) cglibProxy.getProxy(ennigeer);
            int eat = proxy.eat();//可以被代理的方法
            String work = proxy.work();//不能被代理的方法,可以调用该方法,但是不能代理
            System.out.println(eat);
            System.out.println(work);
        }
    }
    /**
    * net.sf.cglib.proxy.MethodInterceptor接口:是最通用的回调(callback)类型,
    * 它经常被AOP用来实现拦截(intercept)方法的调用。这个接口只定义了一个方法。
    * public Object intercept(Object proxy, Method method, Object[] params, MethodProxy methodProxy) 
    * proxy:代理的对象;
    * method:委托类执行的方法;
    * params:方法中的参数;
    * methodProxy:代理的方法
    * 由于性能的原因,对原始方法的调用我们使用CGLIB的net.sf.cglib.proxy.MethodProxy对象,而不是反射中一般使用java.lang.reflect.Method对象
    */


    两种代理方式的比较:

      CGLib动态代理创建代理实例速度慢,但是运行速度快;JDK动态代理创建实例速度快,但是运行速度慢。如果实例是单例的,推荐使用CGLib方式动态代理,反之则使用JDK方式进行动态代理。Spring的实例默认是单例,所以这时候使用CGLib性能高。 

      JDK动态代理是通过接口中的方法名,在动态生成的代理类中调用业务实现类的同名方法;

      CGlib动态代理是通过继承业务类,生成的动态代理类是业务类的子类,通过重写业务方法进行代理;

       静态代理是通过在代码中显式定义一个业务实现类一个代理,在代理类中对同名的业务方法进行包装,用户通过代理类调用被包装过的业务方法;

  • 相关阅读:
    018_STM32程序移植之_串口接收中文
    003_软件安装之_Visual Studio 2012
    001_C#我的第一个串口上位机软件
    017_STM32程序移植之_AS608指纹模块
    016_STM32程序移植之_舵机
    015_STM32程序移植之_NRF24L01模块
    014_STM32程序移植之_L298N电机驱动模块
    002_89C52_Proteus_DAC0832_输出50HZ,正弦波,三角波,矩形波,锯齿波
    001_89C52之_Proteus_ADC0809采集电压
    001_电子工程师招聘笔试题及详细解析
  • 原文地址:https://www.cnblogs.com/Zs-book1/p/11142894.html
Copyright © 2011-2022 走看看