代理模式修炼曲

1.什么是代理模式?

为其他对象提供一种代理以控制对这个对象的访问,在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。简单点说即是给目标对象找个代理,其他对象通过代理对象来访问目标类(可以类比卖房、中介、买房三者之间的关系)。

2.常见代理模式

  • 静态代理
  • JDK代理
  • CGLIB代理

3.静态代理

  • 类分析图解

在这里插入图片描述

静态代理中代理对象(中介)与目标类(出租)双方都是通过实现相同的接口或父类的形式进行实现的。

  • 实例

    (1)代理对象与目标类共同接口

    /**
     * 出租者与租户需要实现的接口
     */
    public interface IRent {
        void rentOut();
    }
    

    (2)出租类

    /**
     * 出租类
     * @author JHS
     */
    public class Renter implements IRent {
        @Override
        public void rentOut() {
            System.out.println("有空闲的房屋出租《==");
        }
    }
    
    

    (3)代理对象

    /**
     * 代理对象
     * @author JHS
     */
    public class RentProxy  implements IRent{
        //需要代理的对象
        private final IRent renter;
    
        public RentProxy(IRent renter) {
            this.renter = renter;
        }
    
    	//重写了父类方法,并再次去调用rentOut时回去找它的实现类(多态的体现)
        @Override
        public void rentOut() {
            System.out.println("开始代理");
            renter.rentOut();
            System.out.println("代理结束");
        }
    }
    

    (4)租户类(测试类)

    /**
     *
     * 租户测试类
     * @author JHS
     */
    public class Rent {
        public static void main(String[] args) {
            //被代理类
            Renter renter = new Renter();
            //代理
            RentProxy rentProxy = new RentProxy(renter);
            rentProxy.rentOut();
        }
    }
    

    (5)结果

    开始代理
    有空闲的房屋出租《==
    代理结束
    

这种代理模式实现简单,但是有个缺点就是一旦增加接口功能,目标类与代理类都要同时跟着维护,造成维护量大。

4.JDK代理

  • 类分析图解

    在这里插入图片描述

    JDK代理相比于静态代理主要取消了代理对象必须实现接口或父类的需求,而是采用了JDK核心包java.lang.reflect下Proxy类下的newProxyInstance静态方法来动态生成代理对象,从而访问目标对象。

  • 实例

    (1)接口类与出租类(目标类)与静态代理一样,这里只修改代理对象类

    (2)代理对象类

    package proxy.jdkproxy;
    
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    /**
     * 代理对象
     * @author JHS
     */
    public class RentProxy {
        //需要代理的对象
        private final Object target;
    
    
        public RentProxy(Object target) {
            this.target = target;
        }
    
        /**
        * 动态生成代理类
        *  ClassLoader:        目标类加载器,目标类这里同常设为Object,使用时才传入具体类
         * interfaces:         目标类实现接口类型
         * InvocationHandler:  事件处理,执行目标对象方法时,会触发事情处理器方法,将目标对象方法作为参数传入
        * @return java.lang.Object
        */
        
        public Object getProxyInstance(){
            //通过JDK的类 Proxy调用静态方法newProxyInstance,需要填入三个参数ClassLoader、                 interfaces、InvocationHandler,用到反射机制,匿名内部类实现
            Object o = Proxy.newProxyInstance(target.getClass().getClassLoader(),
                       target.getClass().getInterfaces(),
                       new InvocationHandler() {
             //proxy: 代理对象  method: 目标对象的方法  args:目标方法参数
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
                            System.out.println("开始代理");
                            //调用目标方法
                            Object invoke = method.invoke(target, args);
                            System.out.println("代理结束");
                            return invoke;
                        }
                    });
            return o;
    
        }
    }
    
    

    (3)租户类

    **
     * 租户
     * 
     * @author JHS
     */
    public class Rent {
        public static void main(String[] args) {
            //被代理类
            Renter renter = new Renter();
            //代理
            RentProxy rentProxy = new RentProxy(renter);
            //生成代理对象
            IRent proxyInstance = (IRent)rentProxy.getProxyInstance();
            proxyInstance.rentOut();
    
        }
    }
    

    (4)结果

    开始代理
    有空闲的房屋出租《==
    代理结束
    

JDK代理优点:若有多个代理对象,接口增加几个方法,这时只需修改目标类无需修改代理对象,减少了代理类的维护。

5.CGLIB代理

静态代理和 JDK 代理模式都要求目标对象是实现一个接口,但是有时候目标对象只是一个单独的对象,并没有实

现任何的接口,这个时候可使用目标对象子类来实现代理,这就是 cglib代理(子类代理),它是在内存中构建一个

子类对象从而实现对目标对象功能扩展。

  • 类分析图

    在这里插入图片描述

    cglib代理取消了接口,采用动态生成子类去实现目标类,需要改动的地方是代理对象需要实现MethodInterceptor 类,重写intercept方法,取消JDK与静态代理的接口。

  • 实例

    (1)由于cglib代理需要用到第三方包,导入包

    • asm-8.0.1.jar
    • asm-commons-8.0.1.jar
    • asm-tree-8.0.1.jar
    • cglib-3.3.0.jar

    jar包下载地址:https://mvnrepository.com/

    (2)出租类

    /**
     * 出租类
     * @author JHS
     */
    public class Renter  {
    
        public void rentOut() {
            System.out.println("有空闲的房屋出租《==");
        }
    }
    

    (3)代理对象

    package proxy.cglib;
    
    
    import net.sf.cglib.proxy.Enhancer;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    /**
     * 代理对象
     * 使用cglib的动态代理
     * @author JHS
     */
    public class RentProxy implements MethodInterceptor {
        //需要代理的对象(被代理对象)
        private final Object target;
    
    
        public RentProxy(Object target) {
            this.target = target;
        }
    
       /**
       * 返回代理对象
       * @return java.lang.Object
       */
        public Object getProxyInstance(){
            //1. 创建增强器,通过CGLIB动态代理获取代理对象的过程
            Enhancer enhancer = new Enhancer();
            //2. 为目标类(被代理对象)生成父类或者接口
            enhancer.setSuperclass(target.getClass());
            //3. 设置会回调函数
            enhancer.setCallback(this);
            //4. 创建子类对象
            return enhancer.create();
    
        }
    	/**
    	 * 实现方法
         * o:           cglib生成的代理对象
         * method:      被代理对象方法
         * objects:     方法入参
         * methodProxy:  代理方法
    	 */
        @Override
        public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
            System.out.println("开始代理");
            Object invoke = method.invoke(target,args);
            System.out.println("代理结束");
            return invoke;
        }
    }
    
    

    (4)租户类(测试类)

    package proxy.cglib;
    
    
    /**
     * 租户
     * @author JHS
     */
    public class Rent {
        public static void main(String[] args) {
            //被代理类
            Renter renter = new Renter();
            //代理
            RentProxy rentProxy = new RentProxy(renter);
            //生成代理对象
            Renter proxyInstance = (Renter)rentProxy.getProxyInstance();
            //代理对象调用方法
            proxyInstance.rentOut();
    
        }
    }
    
    

    (5)结果

    开始代理
    有空闲的房屋出租《==
    代理结束
    

优点:减少了维护,提高了性能,也是一些框架中的常用代理。

posted @ 2020-08-17 18:34  猿大佛  阅读(17)  评论(0编辑  收藏  举报