• 代理模式


    1 概念

    1.1 代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

    1.2 组成

      抽象角色:通过接口或抽象类声明真实角色实现的业务方法。

      代理角色:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。

      真实角色:实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。

    1.3 优点
    1. 职责清晰:真实的角色就是实现实际的业务逻辑,不用关心其他非本职责的事务,通过后期的代理完成一件完成事务,附带的结果就是编程简洁清晰。
    2. 代理对象可以在客户端和目标对象之间起到中介的作用,这样起到了中介的作用和保护了目标对象的作用。
    3. 高扩展性
    1.4 UML类图

    1.5 模式结构

      一个是真正的你要访问的对象(目标类),一个是代理对象,真正对象与代理

      对象实现同一个接口,先访问代理类再访问真正要访问的对象。

      代理模式分为:静态代理、动态代理。

      静态代理是由程序员创建或工具生成代理类的源码,再编译代理类。所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。

      动态代理是在实现阶段不用关心代理类,而在运行阶段才指定哪一个对象。

    2 静态代理和动态代理
    2.1 静态代理

    静态代理是基于接口的实现:

     1)创建一个抽象角色,指定真实角色具有哪些业务行为

    1 /**
    2  * 明星唱歌接口类
    3  */
    4 public interface SingService {
    5     void sing();
    6 }

     2)创建真实角色,实现其基本单一业务职责功能,如明星角色的主要任务就是唱歌

    1 /**
    2  * 明星类
    3  */
    4 public class Star implements SingService {
    5     @Override
    6     public void sing() {
    7         System.out.println("明星唱歌......");
    8     }
    9 }

     3)创建代理角色,其职责是负责真实角色的打理工作

     1 /**
     2  * 经纪人代理类
     3  */
     4 public class Agent implements SingService {
     5 
     6     private Star star;
     7 
     8     public Agent(Star star) {
     9         this.star = star;
    10     }
    11 
    12 
    13     @Override
    14     public void sing() {
    15         System.out.println("同意明星唱歌。。。。");
    16     }
    17 
    18     /**
    19      * 经纪人代理明星处理谈判事宜,决定明星是否唱歌
    20      *
    21      * @param cash
    22      */
    23     public void contract(Long cash) {
    24         if (cash <= 10000L) {
    25             System.out.println("费用少了,谈判失败。。。");
    26         } else {
    27             sing();
    28             star.sing();
    29         }
    30     }
    31 }

     4)客户端测试

    /**
     * 客户端测试类
     */
    public class SingTest {
    
        public static void main(String[] args) {
            Star star = new Star();
            Agent service = new Agent(star);
            service.contract(200000L);
        }
    }

     5)测试结果

    同意明星唱歌。。。。
    明星唱歌......
    2.2 动态代理

    动态代理分为JDK动态代理和cglib动态代理,JDK动态代理实现方式又分两种。

    2.2.1 基于JDK的代理模式:
     1 jdk中为实现代理提供了支持,主要用到2个类:
     2 1、java.lang.reflect.Proxy
     3 2、java.lang.reflect.InvocationHandler
     4 java.lang.reflect.Proxy常用到的静态方法:
     5 (1)getProxyClass方法:为指定的接口创建代理类,返回代理类的Class对象
     6 public static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces)
     7 参数说明:
     8 loader:定义代理类的类加载器
     9 interfaces:指定需要实现的接口列表,创建的代理默认会按顺序实现interfaces指定的接口
    10 (2)newProxyInstance方法:创建代理类的实例对象
    11 public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
    12 这个方法先为指定的接口创建代理类,然后会生成代理类的一个实例,最后一个参数比较特殊,是InvocationHandler类型的
    13 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
    14 这个方法会返回一个代理对象,当调用代理对象的任何方法的时候,会就被InvocationHandler接口的invoke方法处理
    15 (3)isProxy方法:判断指定的类是否是一个代理类
    16 public static boolean isProxyClass(Class<?> cl)
    17 (4)getInvocationHandler方法:获取代理对象的InvocationHandler对象
    18 public static InvocationHandler getInvocationHandler(Object proxy) throws IllegalArgumentException

    1) 创建抽象角色,即声明接口方法

    public interface IService {
    
        void m1();
        void m2();
        void m3();
    }

    2)创建代理并实现

     1 public class JdkProxy {
     2 
     3     /**
     4      * 方式1:
     5      * 1.调用Proxy.getProxyClass方法获取代理类的Class对象
     6      * 2.使用InvocationHandler接口创建代理类的处理器
     7      * 3.通过代理类和InvocationHandler创建代理对象
     8      * 4.上面已经创建好代理对象了,接着我们就可以使用代理对象了
     9      */
    10     @Test
    11     public void proxyTest1() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
    12         //1、获取接口对应代理类的Class对象
    13         Class<IService> proxyClass = (Class<IService>) Proxy.getProxyClass(IService.class.getClassLoader(), IService.class);
    14         //2、创建代理类的处理器
    15         InvocationHandler invocationHandler = (proxy, method, args) -> {
    16             System.out.println("我是InvocationHandler,被调用的处理方法是: " + method.getName());
    17             return null;
    18         };
    19         //3、创建代理实例
    20         IService proxyService = proxyClass.getConstructor(InvocationHandler.class).newInstance(invocationHandler);
    21         //4、调用代理的方法
    22         proxyService.m1();
    23         proxyService.m2();
    24         proxyService.m3();
    25     }
    26 
    27     /**
    28      * 方式2:
    29      * 1.使用InvocationHandler接口创建代理类的处理器
    30      * 2.使用Proxy类的静态方法newProxyInstance直接创建代理对象
    31      * 3.使用代理对象
    32      */
    33     @Test
    34     public void proxyTest2() {
    35         //1、创建代理类的处理器
    36         InvocationHandler invocationHandler = (proxy, method, args) -> {
    37             System.out.println("我是InvocationHandler,被调用的处理方法是: " + method.getName());
    38             return null;
    39         };
    40         //2、创建代理实例
    41         IService proxyService = (IService) Proxy.newProxyInstance(IService.class.getClassLoader(), new Class[]{IService.class}, invocationHandler);
    42         //3、调用代理的方法
    43         proxyService.m1();
    44         proxyService.m2();
    45         proxyService.m3();
    46     }
    47 }

    JDK动态代理总结:相对于静态代理,JDK 动态代理大大减少了我们的开发任务,同时减少了对业务接口的依赖,降低了耦合度。JDK 动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用 InvokeHandler 来处理。但是 JDK 动态代理有个缺憾:JDK 实现动态代理需要实现类通过接口定义业务方法。也就是说它始终无法摆脱仅支持 interface 代理的桎梏,因为它的设计就注定了这个遗憾。

    2.2.2 基于cglib的代理模式

    1 cglib是一个强大、高性能的字节码生成库,它用于在运行时扩展Java类和实现接口,被许多AOP框架使用,如我们常用的Spring AOP; 
    2 本质上它是通过动态的生成一个子类去覆盖所要代理的类(非final修饰的类和方法)。
    3 Enhancer可能是CGLIB中最常用的一个类,和jdk中的Proxy不同的是,Enhancer既能够代理普通的class,也能够代理接口。Enhancer创建一个被代理对象的子类并且拦截所有的方法调用(包括从Object中继承的toString和hashCode方法)。
    4 Enhancer不能够拦截final方法,例如Object.getClass()方法,这是由于Java final方法语义决定的。 5 基于同样的道理,Enhancer也不能对final类进行代理操作。

    1) 创建抽象角色,声明一个普通的class

    public class MService {
        public void m1(){
            System.out.println("我是方法m1()");
        }
    
        public String m2(){
            System.out.println("我是方法m2()");
            return "test-m2";
        }
    }

    2)

     1 public class CglibProxy {
     2 
     3     /**
     4      * 基于Enhancer实现:
     5      * (1).创建Enhancer对象
     6      * (2).创建代理类
     7      * (3).设置回调
     8      * (4).获取代理对象
     9      * (5).调用代理对象的方法
    10      *
    11      */
    12     @Test
    13     public void cglibTest1(){
    14         //1 创建Enhancer对象
    15         Enhancer enhancer = new Enhancer();
    16         //2 通过setSuperclass创建代理对象
    17         enhancer.setSuperclass(MService.class);
    18         //3.设置拦截器回调,需实现org.springframework.cglib.proxy.Callback接口,
    19         // 此处我们使用的是org.springframework.cglib.proxy.MethodInterceptor,也是一个接口,实现了Callback接口,
    20         // 当调用代理对象的任何方法的时候,都会被MethodInterceptor接口的invoke方法处理
    21         enhancer.setCallback(new MethodInterceptor() {
    22             /**
    23              * 代理对象拦截器
    24              * @param o 代理对象
    25              * @param method 被代理类的方法
    26              * @param objects 调用方法传递的参数
    27              * @param methodProxy 方法代理对象
    28              * @return
    29              */
    30             @Override
    31             public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
    32                 System.out.println("代理对象的方法:"+ method);
    33                 //通过调用methodProxy的方法invokeSuper获取
    34                 Object result = methodProxy.invokeSuper(o, objects);
    35                 System.out.println(result);
    36                 return result;
    37             }
    38         });
    39 
    40         //设置过滤器回调
    41         enhancer.setCallbackFilter(method -> {
    42             System.out.println("过滤方法:" + method);
    43             return 0;
    44         });
    45         MService mService = (MService) enhancer.create();
    46         mService.m1();
    47         mService.m2();
    48     }
    49 
    50 }

    cglib代理总结:CGLIB 创建的动态代理对象比 JDK 创建的动态代理对象的性能更高,但是 CGLIB 创建代理对象时所花费的时间却比 JDK 多得多。所以对于单例的对象,因为无需频繁创建对象,用 CGLIB 合适,反之使用JDK方式要更为合适一些。同时由于 CGLIB 由于是采用动态创建子类的方法,对于final修饰的方法无法进行代理。同时,CGLIB也摆脱了interface代理的桎梏,也能支持普通的class。

    3 spring采用的代理

    spring5源码:

    public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
    
        @Override
        public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
            if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
                Class<?> targetClass = config.getTargetClass();
                if (targetClass == null) {
                    throw new AopConfigException("TargetSource cannot determine target class: " +
                            "Either an interface or a target is required for proxy creation.");
                }
                if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
                    return new JdkDynamicAopProxy(config);
                }
                return new ObjenesisCglibAopProxy(config);
            }
            else {
                return new JdkDynamicAopProxy(config);
            }
        }
    
        /**
         * Determine whether the supplied {@link AdvisedSupport} has only the
         * {@link org.springframework.aop.SpringProxy} interface specified
         * (or no proxy interfaces specified at all).
         */
        private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) {
            Class<?>[] ifcs = config.getProxiedInterfaces();
            return (ifcs.length == 0 || (ifcs.length == 1 && SpringProxy.class.isAssignableFrom(ifcs[0])));
        }
    
    }
    从if中的判断条件可以看到3个方面影响着Spring的判断:
    config.isOptimize()
    /**
         * Return whether proxies should perform aggressive optimizations.
         */
        public boolean isOptimize() {
            return this.optimize;
        }
    config.isProxyTargetClass()
    /**
         * Return whether to proxy the target class directly as well as any interfaces.
         */
        public boolean isProxyTargetClass() {
            return this.proxyTargetClass;
        }
    hasNoUserSuppliedProxyInterfaces(config))
    /**
         * Determine whether the supplied {@link AdvisedSupport} has only the
         * {@link org.springframework.aop.SpringProxy} interface specified
         * (or no proxy interfaces specified at all).
         */
        private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) {
            Class<?>[] ifcs = config.getProxiedInterfaces();
            return (ifcs.length == 0 || (ifcs.length == 1 && SpringProxy.class.isAssignableFrom(ifcs[0])));
        }

    (1)optimize:默认是false,用来控制通过CGLIB创建的代理是否使用激进的优化策略。除非完全了解AOP代理是如何优化,否则不推荐用户使用这个设置,目前这个属性仅用于CGLIB代理,对于JDK动态代理(缺省代理)无效。
    (2)proxyTargetClass:默认是false,这个属性为treu时,目标类本身被代理而不是目标类的接口。如果这个属性值被设为true,CGLIB代理将被创建,。
    (3)hasNoUserSuppliedProxyInterfaces:是否存在代理接口。
    使用总结:如果目标对象实现的是接口,那么spring默认采用JDK代理方式;如果目标对象实现的是类,spring默认采用CGLIB代理方式;如果目标对象实现的是接口,也可以通过设置optimize属性,进行强制使用CGLIB代理方式

     

  • 相关阅读:
    CSP2021&NOIP2021游记
    P3835[模板]可持久化平衡树【无旋Treap】
    P4688[Ynoi2016]掉进兔子洞【莫队,bitset】
    C# (CSharp) ADODB.Command示例
    求最大公约数 算法记录
    流逝时间+Windows下监测文件夹
    C# WinForm开发系列 文章索引
    09年搞笑签名
    高三班主任写给学生的一封信(在读大学的要看完)
    北京生存法则
  • 原文地址:https://www.cnblogs.com/light-sunset/p/13063341.html
Copyright © 2020-2023  润新知