• 大话--代理模式


    代理模式

    定义

    代理模式就是给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。通俗讲代理模式就是我们生活中常见的中介。

    使用场景

    远程(Remote)代理:为一个位于不同的地址空间的对象提供一个本地的代理对象,这个不同的地址空间可以是在同一台主机中,也可是在另一台主机中。
    虚拟(Virtual)代理:如果需要创建一个资源消耗较大的对象,先创建一个消耗相对较小的对象来表示,真实对象只在需要时才会被真正创建。
    Copy-on-Write代理:它是虚拟代理的一种,把复制(克隆)操作延迟到只有在客户端真正需要时才执行。一般来说,对象的深克隆是一个开销较大的操作,Copy-on-Write代理可以让这个操作延迟,只有对象被用到的时候才被克隆。
    保护(Protect or Access)代理:控制对一个对象的访问,可以给不同的用户提供不同级别的使用权限。
    缓冲(Cache)代理:为某一个目标操作的结果提供临时的存储空间,以便多个客户端可以共享这些结果。
    防火墙(Firewall)代理:保护目标不让恶意用户接近。
    同步化(Synchronization)代理:使几个用户能够同时使用一个对象而没有冲突。
    智能引用(Smart Reference)代理:当一个对象被引用时,提供一些额外的操作,如将此对象被调用的次数记录下来等。

    优点

    在目标对象实现的基础上,可以增强额外的功能操作,即扩展目标对象的功能。
    代理模式主要起增强方法和权限拦截的作用。

    缺点

    1.增加了代理对象可能导致请求的处理速度变慢。
    2.实现代理模式需要额外工作,有些代理模式的实现非常复杂。

    组成

    代理模式通常分成静态代理和动态代理。动态代理又分为JDK代理和Cglib代理

    tips

    1、和适配器模式的区别:适配器模式主要改变所考虑对象的接口,而代理模式不能改变所代理类的接口。
    2、和装饰器模式的区别:装饰器模式为了增强功能,而代理模式是为了加以控制。
    静态代理需要自己手动编写代理类和目标方法。
    动态代理就不需要自己手动实现代理类和目标方法,但动态代理的目标类要必须实现接口!
    Cglib代理的目标类可以实现接口也可以不实现,因为可以使用继承子类的方式代理。

    关键代码

    实现代理类与被代理类的组合

    类图

    普通代理模式

    静态代理

    动态代理

    Cglib代理

    具体实现

    静态代理

    静态代理在使用时,需要定义接口或者父类,目标对象与代理对象一起实现相同的接口或者是继承相同的父类。

    静态代理的优缺点:
    优点:在不修改目标对象功能的前提下,能通过代理对象对目标功能扩展。
    确定:需要很多代理类;一旦接口增加,目标对象和代理对象都要维护。

    1.定义一个接口ITeacherDao
    2.目标对象TeacherDao实现接口ITeacherDAO
    3.使用静态代理方式,需要代理对象TeacherDaoProxy也实现ITeacherDao
    4.调用时,通过调用代理对象的方法来调用目标对象
    5.代理对象和目标对象要实现相同的接口,并通过调用相同的方法来调用目标对象的方法
    
    package Proxy.staticProxy;
    
    public interface ITeacherDao {
        void teach();
    }
    
    package Proxy.staticProxy;
    
    /**
     * @title: TeacherDao
     * @Author yzhengy
     * @Date: 2020/7/27 16:23
     * @Question:  目标对象
     */
    public class TeacherDao implements ITeacherDao {
    
        @Override
        public void teach() {
            System.out.println("老师正在授课......");
        }
    }
    
    package Proxy.staticProxy;
    
    /**
     * @title: TeacherDaoProxy
     * @Author yzhengy
     * @Date: 2020/7/27 16:24
     * @Question: 代理对象
     */
    public class TeacherDaoProxy implements ITeacherDao {
    
        private ITeacherDao target;//目标对象,通过接口来聚合
    
        //构造器
        public TeacherDaoProxy(ITeacherDao target) {
            this.target = target;
        }
    
        @Override
        public void teach() {
            System.out.println("开始代理......操作过程进行中");
            target.teach();
            System.out.println("提交操作");
        }
    }
    
    package Proxy.staticProxy;
    
    /**
     * @title: Client
     * @Author yzhengy
     * @Date: 2020/7/27 16:27
     * @Question: 测试类
     */
    public class Client {
    
        public static void main(String[] args) {
            //创建目标对象
            TeacherDao teacherDao = new TeacherDao();
    
            //创建代理对象,同时将目标对象传递给代理对象
            TeacherDaoProxy teacherDaoProxy = new TeacherDaoProxy(teacherDao);
    
            //通过代理对象,调用目标对象的方法(执行的是代理对象的方法,代理对象再去调用目标对象的方法)
            teacherDaoProxy.teach();
    
        }
    
    }
    
    
    动态代理

    1.代理对象不需要实现接口,但目标对象需要实现接口。
    2.代理对象的生成是利用JDK的API,动态地在内存中构建代理对象。
    3.代理类所在的包:java.lang.reflect.Proxy
    4.JDK实现代理只需要使用newProxyInstance方法。static Object newProxyInstance(Classloader loader,Class<?>[]interfaces,InvocationHandler h)

    代理类是由Proxy这个类通过newProxyInstance方法动态生成的,生成对象后使用“实例调用方法”的方式进行方法调用,那么代理类的被代理类的关系只有在执行这行代码的时候才会生成,因此成为动态代理。

    package Proxy.dynamicProxy;
    
    public interface ITeacherDao {
    
        void teacher();
        void sayHello(String name);
    
    }
    
    package Proxy.dynamicProxy;
    
    /**
     * @title: TeacherDao
     * @Author yzhengy
     * @Date: 2020/7/27 17:04
     * @Question: 目标对象
     */
    public class TeacherDao implements ITeacherDao {
    
        @Override
        public void teacher() {
            System.out.println("老师授课中......");
        }
    
        @Override
        public void sayHello(String name) {
            System.out.println("hello" + name);
        }
    }
    
    package Proxy.dynamicProxy;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    /**
     * @title: ProxyFactory
     * @Author yzhengy
     * @Date: 2020/7/27 17:05
     * @Question: 代理对象工厂
     */
    public class ProxyFactory {
    
        private Object target;//维护一个目标对象
    
        public ProxyFactory(Object target){//构造器,对target进行初始化
            this.target = target;
        }
    
        //给目标对象生成一个相应的代理对象
        public Object getProxyInstance(){
    		/*说明
    		 *  public static Object newProxyInstance(ClassLoader loader,
                                              Class<?>[] interfaces,
                                              InvocationHandler h)
                1. ClassLoader loader : 指定当前目标对象使用的类加载器, 获取加载器的方法固定
                2. Class<?>[] interfaces: 目标对象实现的接口类型,使用泛型方法确认类型
                3. InvocationHandler h : 事情处理,执行目标对象的方法时,会触发事情处理器方法, 会把当前执行的目标对象方法作为参数传入
    		 */
    		return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    System.out.println("JDK动态代理开始......");
                    Object returnVal = method.invoke(target,args);//利用反射机制调用目标对象的方法
                    System.out.println("JDK动态代理提交......");
                    return returnVal;
                }
            });
    
        }
    }
    
    package Proxy.dynamicProxy;
    
    /**
     * @title: Client
     * @Author yzhengy
     * @Date: 2020/7/27 17:13
     * @Question: 测试类
     */
    public class Client {
    
        public static void main(String[] args) {
            //创建目标对象
            ITeacherDao target = new TeacherDao();
    
            //给了目标对象,创建代理对象,需要转成ITeacherDao
            ITeacherDao proxyInstance = (ITeacherDao)new ProxyFactory(target).getProxyInstance();
    
            //proxyInstance=class com.sun.proxy.$Proxy0 内存中动态生成了代理对象
            System.out.println("proxyInstance=" + proxyInstance.getClass());
    
            //通过代理对象,调用目标对象的方法
            proxyInstance.teacher();
            proxyInstance.sayHello("tina");
        }
    }
    
    
    Cglib代理

    CGLib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。但因为采用的是继承,所以不能对final修饰的类进行代理。
    1.引用Cglib的jar包
    2.在内存中动态地创建子类,注意代理的类不能为final,否则会出现java.lang.IllegalArgumentException错误。
    3.目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法。

    package Proxy.cglibProxy;
    
    /**
     * @title: TeacherDao
     * @Author yzhengy
     * @Date: 2020/7/27 18:10
     * @Question: 目标对象
     */
    public class TeacherDao {
    
        public String teach(){
            System.out.println("老师授课中  , 我是cglib代理,不需要实现接口");
            return "hello";
        }
    }
    
    package Proxy.cglibProxy;
    
    import net.sf.cglib.proxy.Enhancer;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    
    import java.lang.reflect.Method;
    
    /**
     * @title: ProxyFactory
     * @Author yzhengy
     * @Date: 2020/7/27 17:38
     * @Question: 代理对象
     */
    public class ProxyFactory implements MethodInterceptor {
    
        //维护一个目标对象
        private Object target;
    
        public ProxyFactory(Object target) {
            this.target = target;
        }
    
        //返回一个代理对象,是target目标对象的代理对象
        public Object getProxyInstance(){
            //1.创建一个工具类
            Enhancer enhancer = new Enhancer();
            //2.设置父类
            enhancer.setSuperclass(target.getClass());
            //3.设置回调函数
            enhancer.setCallback(this);
            //4.创建子类对象,即代理对象
            return enhancer.create();
        }
    
        @Override
        public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
            System.out.println("cglib代理模式......开始");
            Object returnVal = method.invoke(target,args);
            System.out.println("cglib代理模式......结束");
            return returnVal;
        }
    
    }
    
    package Proxy.cglibProxy;
    
    /**
     * @title: Client
     * @Author yzhengy
     * @Date: 2020/7/27 18:21
     * @Question: 测试类
     */
    public class Client {
    
        public static void main(String[] args) {
            //.创建目标对象
            TeacherDao target = new TeacherDao();
    
            //.获取代理对象,并将目标对象传递给代理对象
            TeacherDao proxyInstance = (TeacherDao)new ProxyFactory(target).getProxyInstance();
    
            //.执行代理对象的方法,触发intercept方法,实现对目标对象方法的调用
            String res = proxyInstance.teach();
            System.out.println("res=" + res);
        }
    
    }
    
    

    使用代理模式的源码

    Spring AOP 编程的实现原理就是动态代理。使用的是JDK代理和cglib代理,比如Spring的事务使用的是AOP技术,当目标类没有实现接口时候,会使用cglib代理,实现了接口默认使用JDK代理。

    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.");
         }
           // 判断目标类是否是接口或者目标类是否Proxy类型,若是则使用JDK动态代理
         if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
           return new JdkDynamicAopProxy(config);
         }
           // 配置了使用CGLIB进行动态代理或者目标类没有接口,那么使用CGLIB的方式创建代理对象
         return new ObjenesisCglibAopProxy(config);
       }
       else {
           // 上面的三个方法没有一个为true,那使用JDK的提供的代理方式生成代理对象
         return new JdkDynamicAopProxy(config);
       }
     }
       //其他方法略……
    }
    
    
  • 相关阅读:
    数据挖掘实践(20):算法基础(三)SVM(支持向量机)算法
    数据挖掘实践(19):算法基础(二)Logistic回归(逻辑斯蒂)算法
    数据挖掘实践(18):算法基础(一)线性回归
    Flink 源码(三): Flink Client 实现原理与源码解析(二)
    Flink 源码(二): Flink Client 实现原理与源码解析(一)
    GIT基础(九):Git 远程仓库(Github)
    GIT基础(八):查看提交历史
    GIT基础(七):分支管理
    GIT基础(六):Git 基本操作(一)
    rust thread
  • 原文地址:https://www.cnblogs.com/yzhengy/p/13387274.html
Copyright © 2020-2023  润新知