• 代理设计模式


    代理设计模式

    1. 静态代理模式(基于接口的代理)

    1. 前提条件

    • 代理对象
      • 对被代理对象中的方法再不修改其源代码的情况小进行增强,要实现被代理接口
    • 被代理对象
      • 该对象中包含了被代理方法的具体实现,要实现被代理接口
    • 被代理接口
      • 该接口中包含了要被代理的抽象方法

    2. 案例需求说明

    用户要进行登录操作,在登录操作过程中进行日志的记录

    3. 创建的三个前提条件

    1. 代理对象:ClientRequestProxy
    2. 被代理对象:ClientRequest
    3. 被代理接口:Client

    1. 被代理接口:Client

    /**
     * 处理的客户端接口,定义一些公共方法标准
     */
    public interface Client {
        // 登录方法
        abstract public void login();
    }
    

    2. 被代理对象:ClientRequest

    /**
     * 处理请求的客户端,实现Client
     * 使用代理类对该客户端的请求进行代理,给处理请求的方法进行一定的增强
     * 在不修改原方法的前提下进行日志的记录
     */
    public class ClientRequest implements Client {
    
        /**
         * 模拟用户进行登录操作
         */
        @Override
        public void login(){
            System.out.println("用户登录中...");
        }
    
        /**
         * 测试静态代理模式
         */
        public static void main(String[] args) {
            // 被代理对象
            ClientRequest clientRequest = new ClientRequest();
            // 代理对象,并且将被代理对象作为参数传递到代理对象中
            ClientRequestProxy clientRequestProxy = new ClientRequestProxy(clientRequest);
            // 调用登录方法
            clientRequestProxy.login();
        }
    }
    

    3. 代理对象:ClientRequestProxy

    /**
     * 代理处理客户端请求的代理类,实现Client
     */
    public class ClientRequestProxy implements Client {
    
        // 创建一个客户端对象,用于接收通过构造函数传递进来的对象
        private Client client;
    
        /**
         * 创建代理类的有参构造,传入客户端对象,实现了客户端接口的所有类都可以传入
         * @param client 客户端对象
         */
        public ClientRequestProxy(Client client) {
            this.client = client;
        }
    	/**
         * 无参构造
         */
        public ClientRequestProxy() {}
    
        /**
         * 对用户登录操作进行代理
         */
        @Override
        public void login() {
            System.out.println("静态代理代理开始...");
            System.out.println("登录开始,开始记录日志...");
            this.client.login();
            System.out.println("登录结束,记录日志结束...");
        }
    }
    

    4. 测试结果

    2. JDK动态代理设计模式(基于接口的代理)

    基于JDK实现的动态代理模式(基于JDK的动态代理模式格式相对是固定的,要修改的地方几个)
    基于JDK的动态代理是调用 java.lang.reflect.Proxy 中的 newProxyInstance方法实现的,
    在调用该方法传入三个参数
    1.目标对象使用的类加载器:目标对象.getClass().getClassLoader()
    2.目标对象实现的接口类型:目标对象.getClass().getInterfaces()
    3.事情处理:执行目标对象的方法时,会触发事情处理器方法,会把当前执行的目标对象方法作为参数传入,里面就是具体增强业务
    

    1. 前提条件

    1. 被代理接口
      1. 包含需要代理的抽象方法
    2. 被代理对象(目标对象)
      1. 包含需要代理的方法的具体业务逻辑,需要实现被代理接口
    3. 代理对象(基于JDK创建)
      1. 基于JDK的代理创建,不需要实现代理接口,通过反射的方式动态获取到目标对象进行生成代理对象

    2. 案例需求说明

    ​ 用户要进行登录操作,在登录操作过程中进行日志的记录

    3. 创建三个前提条件

    1. 代理对象:ProxyFactory(基于JDK创建)
    2. 被代理对象:ClientRequest
    3. 被代理接口:Client

    1. 被代理接口Client

    /**
     * 处理的客户端接口,定义一些公共方法标准
     */
    public interface Client {
        // 登录方法
        abstract public void login();
    }
    

    2. 被代理对象:ClientRequest

    /**
     * 处理请求的客户端,实现Client
     * 使用代理类对该客户端的请求进行代理,给处理请求的方法进行一定的增强
     * 在不修改原方法的前提下进行日志的记录
     *
     * @author : 可乐
     * @version : 1.0
     * @since : 2021/7/18 16:58
     */
    @SuppressWarnings("all")
    public class ClientRequest implements Client {
    
        /**
         * 模拟用户进行登录操作
         */
        @Override
        public void login() {
            System.out.println("用户登录中...");
        }
    
        /**
         * 测试静态代理模式
         */
        public static void main(String[] args) {
            // 被代理接口对象
            Client client = new ClientRequest();
            // 给目标对象,创建代理对象,内存中动态生成了代理对象(proxyInstance.getClass()可以查看到)
            Client proxyInstance = (Client) new ProxyFactory(client).getProxyInstance();
            // 通过生成的代理对调用需要增强的方法
            proxyInstance.login();
        }
    }
    

    3. 代理对象:ProxyFactory(基于JDK创建)

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    /**
     * 基于JDK实现的动态代理模式(基于JDK的动态代理模式格式相对是固定的,要修改的地方几个)
     * 基于JDK的动态代理是调用 java.lang.reflect.Proxy 中的 newProxyInstance方法实现的,
     * 在调用该方法传入三个参数
     * 1.目标对象使用的类加载器:目标对象.getClass().getClassLoader()
     * 2.目标对象实现的接口类型:目标对象.getClass().getInterfaces()
     * 3.事情处理:执行目标对象的方法时,会触发事情处理器方法,
     *   会把当前执行的目标对象方法作为参数传入,里面就是具体增强业务
     */
    public class ProxyFactory {
    
        // 被代理对象,因为是动态代理,被代理的对象类型不确定,所以使用Object
        private Object target;
    
        // 创建代理类的有参构造,将被代理进行初始化
        public ProxyFactory(Object target) {
            this.target = target;
        }
    
        /**
         * 为目标对象生成代理对象
         * @return 生成的代理对象
         */
        public Object getProxyInstance() {
    
            /**
             * 使用JDK的代理方法生成代理对象
             *
             * 方法原型:
             *  public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
             *
             * 参数一:ClassLoader loader :  指定当前目标对象使用的类加载器, 获取加载器的方法固定
             * 参数二:Class<?>[] interfaces: 目标对象实现的接口类型,使用泛型方法确认类型
             * 参数三:InvocationHandler h :  事情处理,执行目标对象的方法时,会触发事情处理器方法, 会把当前执行的目标对象方法作为参数传入
             */
            Object object = Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),
                    new InvocationHandler() {
                        /**
                         * 对被代理对象实现业务增强
                         * @param proxy
                         * @param method
                         * @param args
                         * @return
                         * @throws Throwable
                         */
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            System.out.println("JDK动态代理开始...");
                            System.out.println("开始记录日志...");
                            // 反射机制调用目标对象的方法
                            Object result = method.invoke(target, args);
                            System.out.println("记录日志结束...");
                            return result;
                        }
                    });
            return object;
        }
    }
    

    4. 测试结果

    3. Cglib代理模式(可以不基于接口进行动态代理)

    1. 特点

    cgilb代理的目标对象不需要实现接口,cglib代理会在内存中动态生成一个目标类的一个子类,在这个子类的继承上对方法进行增强操作。

    2. 前提条件

    1. 被代理类(目标对象)
    2. cglib的jar包,创建一个类实现cglib中的MethodInterceptor接口中的intercept方法

    3. 案例说明

    用户要进行登录操作,在登录操作过程中进行日志的记录

    4. 创建两个前提条件

    1. 被代理对象

    /**
     * 用户登录类
     */
    public class UserLogin {
        // 用户登录方法
        public void login(){
            System.out.println("用户登录中...");
        }
    
        /**
         * 测试cglib代理
         */
        public static void main(String[] args) {
            // 创建被代理对象
            UserLogin userLogin = new UserLogin();
            // 创建代理对象,并且将被代理对象当做参数传入到代理对象的构造函数中
            UserLogin proxyFactory = (UserLogin)new ProxyFactory(userLogin).getProxyInstance();
            proxyFactory.login();
        }
    }
    

    2. 实现了cglib中MethodInterceptor接口

    import net.sf.cglib.proxy.Enhancer;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    
    import java.lang.reflect.Method;
    
    /**
     * 实现cglib中的MethodInterceptor接口中的intercept方法
     */
    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();
        }
    
        /**
         * 重写  intercept 方法,会调用目标对象的方法
         */
        @Override
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            System.out.println("Cglib代理模式开始...");
            System.out.println("开始记录日志...");
            Object returnVal = method.invoke(target, objects);
            System.out.println("记录日志结束...");
            return returnVal;
        }
    }
    
  • 相关阅读:
    ubuntu 无法在终端切换输入法的解决办法
    c代码连接mysql数据库内存泄露的问题
    栈和堆的地址哪个高
    笔试题之union与struct
    笔试题之interface和abstract class之间的区别
    笔试题之C#struct
    c++单例模式的写法
    c++返回引用是否是真的引用
    operator = 为什么要返回*this的应用
    c++ new和delete操作符的重载
  • 原文地址:https://www.cnblogs.com/wufuqin/p/15032346.html
Copyright © 2020-2023  润新知