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

    一、JDK动态代理

       基于接口实现,要求委托类要是接口的实现。

      Java的 java.lang.reflect 包下提供了 Proxy 类和一个InvocationHandler接口。

      Proxy定义了生成JDK动态代理类的方法 getProxyClass(ClassLoader loader,Class<?>... interfaces)生成动态代理类

      InvocationHandler 是被动态代理类回调的接口,接口中的invoke()方法调用委托类(被代理的类)的实际方法,我们所有需要的针对委托类的统一增强逻辑都放在invoke()方法中,在调用委托类接口方法之前或之后。

      生成JDK动态代理步骤:

        1、定义需要被代理的接口

    public interface Sleep {
        void goToSleep();
    }

        2、定义委托类,并实现1中的接口

    public class SleepImpl implements Sleep {
        @Override
        public void goToSleep() {
            System.out.println("睡觉");
        }
    }

        3、定义 InvocationHandler 接口的实现类,并重写 invoke 方法,在invoke方法中调用委托类的方法,并可在调用前后添加增强逻辑

    public class MyInvocationHandler<T> implements InvocationHandler {
    
        /**
         * 被代理的目标对象
         */
        private T target;
    
        public MyInvocationHandler(T target) {
            this.target = target;
        }
    
        /**
         * 调用委托类的实际方法及执行增强逻辑
         *
         * @param proxy
         * @param method
         * @param args
         * @return
         * @throws Throwable
         */
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
            // 增强逻辑
            System.out.println("前增强");
    
            // 调用委托类的实际方法
            Object result = method.invoke(target, args);
    
            // 增强逻辑
            System.out.println("后增强");
    
            // 结果返回
            return result;
        }
    
    }

        4、实例化委托类,并通过Proxy.newProxyInstance 生成代理对象

         // 实例化委托类
            Sleep sleep = new SleepImpl();
    
            // 生成代理对象
            Sleep sleepProxy= (Sleep) Proxy.newProxyInstance(sleep.getClass().getClassLoader(),sleep.getClass().getInterfaces(),new MyInvocationHandler<Sleep>(sleep));
    
            // 执行代理类增强后的方法
            sleepProxy.goToSleep();
      建议整合步骤3和4,使用匿名内部类的方式,如:
         // 实例化委托类
            Sleep sleep = new SleepImpl();
    
            // 生成代理对象
            Sleep sleepProxy = (Sleep) Proxy.newProxyInstance(sleep.getClass().getClassLoader(), sleep.getClass().getInterfaces(), new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    // 增强逻辑
                    System.out.println("前增强");
                    // 调用委托类的实际方法
                    Object result = method.invoke(sleep, args);
                    // 增强逻辑
                    System.out.println("后增强");
                    // 结果返回
                    return result;
                }
            });
    
            // 执行代理类的方法
            sleepProxy.goToSleep();
    
    
    
     
    
    

      JDK动态代理特点

        1、生成的代理类:$Proxy0 extends Proxy implements Subject,我们看到代理类继承了Proxy类,Java的继承机制决定了JDK动态代理类们无法实现对 类 的动态代理。所以也就决定了JDK动态代理只能对接口进行代理。

        2、每个生成的动态代理实例都会关联一个调用处理器对象,可以通过 Proxy 提供的静态方法 getInvocationHandler去获得代理类实例的调用处理器对象。在代理类实例上调用其代理的接口中所声明的方法时,这些方法最终都会由调用处理器的 invoke 方法执行。

        3、代理类的根类 java.lang.Object 中有三个方法也同样会被分派到调用处理器的 invoke 方法执行,它们是 hashCode,equals 和 toString,可能的原因有:一是因为这些方法为 public 且非 final 类型,能够被代理类覆盖;二是因为这些方法往往呈现出一个类的某种特征属性,具有一定的区分度,所以为了保证代理类与委托类对外的一致性,这三个方法也应该被调用处理器分派到委托类执行。

      JDK动态代理的不足

      JDK动态代理的代理类字节码在创建时,需要实现业务实现类所实现的接口作为参数。如果业务实现类是没有实现接口而是直接定义业务方法的话,就无法使用JDK动态代理了。(JDK动态代理重要特点是代理接口)并且,如果业务实现类中新增了接口中没有的方法,这些方法是无法被代理的(因为无法被调用)。动态代理只能对接口产生代理,不能对类产生代理。

     

    二、CGLib动态代理

      基于继承,CGlib是针对类来实现代理的,他的原理是对委托类(被代理的目标类)生成一个子类,并覆盖其中方法实现增强。

      因为底层是基于创建被代理类的一个子类,所以它避免了JDK动态代理的缺陷。

      但因为采用的继承,所以不能对final修饰的类进行代理。

      生成CGLib动态代理的步骤:

      1、定义委托类(需要被代理的类)

    public class Sleep {
        public void goToSleep() {
            System.out.println("sleeping");
        }
    }

      2、定义 MethodInterceptor 接口的实现类,并重写 intercept方法,在intercept方法中调用委托类的方法,并可在调用前后添加增强逻辑

    public class CGLibDynamicProxy implements MethodInterceptor {
    
        /**
         * 调用委托类的实际方法及执行增强逻辑
         * @param obj
         * @param method
         * @param objects
         * @param methodProxy
         * @return
         * @throws Throwable
         */
        @Override
        public Object intercept(Object obj, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            // 增强逻辑
            System.out.println("前增强");
    
            //调用委托类(父类中)的方法
            Object result = methodProxy.invokeSuper(obj, objects);
    
            // 增强逻辑
            System.out.println("后增强");
    
            // 返回执行结果
            return result;
        }
    }

      3、通过 Enhancer.create 生成代理对象

         // 生成Sleep类的代理对象
            Sleep sleepProxy= (Sleep) Enhancer.create(Sleep.class,new CGLibDynamicProxy());
            
            // 执行代理对象增强后的方法
            sleepProxy.goToSleep();

      注意:Enhancer 、 MethodInterceptor  MethodProxy 均来自 org.springframework.cglib.proxy 包。

      建议整合步骤2和3,使用匿名内部类的方式,如:

            // 生成Sleep类的代理对象
            Sleep sleepProxy = (Sleep) Enhancer.create(Sleep.class, new MethodInterceptor() {
                @Override
                public Object intercept(Object obj, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                    // 增强逻辑
                    System.out.println("前增强");
                    //调用委托类(父类中)的方法
                    Object result = methodProxy.invokeSuper(obj, objects);
                    // 增强逻辑
                    System.out.println("后增强");
                    return result;
                }
            });
    
            // 执行代理类增强后的方法
            sleepProxy.goToSleep();

      

      CGLib动态代理特点:

        1、CGlib可以传入接口也可以传入普通的类,接口使用实现的方式,普通类使用会使用继承的方式生成代理类;

        2、由于是继承方式,如果是static方法,private方法,final方法等描述的方法是不能被代理的;

        3、做了方法访问优化,使用建立方法索引的方式避免了传统JDK动态代理需要通过Method方法反射调用;

        4、提供callback 和filter设计,可以灵活地给不同的方法绑定不同的callback。编码更方便灵活;

        5、CGLIB会默认代理Object中equals,toString,hashCode,clone等方法。比JDK代理多了clone

    END.

  • 相关阅读:
    Cartographer源码阅读(1):程序入口
    ROS开发与常用命令
    实时Cartographer测试(1)
    Cartographer安装
    ROS安装(2)
    Linux学习和ROS安装(1)
    无法启动程序
    c# 获取端口的连接数,网站的连接数
    SignarL服务器端发送消息给客户端的几种情况
    c#操作IIS之IISHelper
  • 原文地址:https://www.cnblogs.com/yangyongjie/p/14603633.html
Copyright © 2011-2022 走看看