zoukankan      html  css  js  c++  java
  • 设计模式学习总结(十二)--代理模式

    定义

    代理模式就是给一个对象提供一个代理,并由代理对象控制对原对象的引用。在代理模式中,“第三者”代理主要是起到一个中介的作用,它连接客户端和目标对象。

    角色

    • Subject: 抽象角色。声明真实对象和代理对象的共同接口。

    • Proxy: 代理角色。代理对象与真实对象实现相同的接口,所以它能够在任何时刻都能够代理真实对象。代理角色内部包含有对真实对象的引用,所以她可以操作真实对象,同时也可以附加其他的操作,相当于对真实对象进行封装。

    • RealSubject: 真实角色。它代表着真实对象,是我们最终要引用的对象

    优缺点

    优点

    • 代理模式能够协调调用者和被调用者,在一定程度上降低了系统的耦合度。

    • 代理对象可以在客户端和目标对象之间起到中介的作用,这样起到了的作用和保护了目标对象的

    缺点

    • 、由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。
    • 实现代理模式需要额外的工作,有些代理模式的实现非常复杂。

    实例

    静态代理

    UserDao 接口:

    public interface IUserDao {
        void save();
    }
    

    UserDao 实现:

    public class UserDao implements IUserDao {
        @Override
        public void save() {
            System.out.println("保存用户信息");
        }
    }
    

    UserDao 代理:

    public class UserDaoProxy implements IUserDao {
    
        private IUserDao iUserDao;
    
        public UserDaoProxy(IUserDao iUserDao){
            this.iUserDao = iUserDao;
        }
        @Override
        public void save() {
            System.out.println("调用方法前处理");
            iUserDao.save();
            System.out.println("调用方法后处理");
        }
    }
    

    调用:

    public class Test {
        public static void main(String[] args) {
            UserDaoProxy userDaoProxy = new UserDaoProxy(new UserDao());
            userDaoProxy.save();
        }
    }
    

    控制台输出:

    调用方法前处理
    保存用户信息
    调用方法后处理
    

    优点:

    可以做到在不修改目标对象的功能前提下,对目标功能扩展.
    

    缺点:

    因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类,类太多.同时,一旦接口增加方法,目标对象与代理对象都要维护.
    

    动态代理

    动态代理特点

    • 代理对象,不需要实现接口
    • 代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象(需要我们指定创建代理对象/目标对象实现的接口的类型)
    • 动态代理也叫做:JDK代理,接口代理

    动态代理工厂:

    public class ProxyFactory {
    
    
        private Object target;
    
        public ProxyFactory(Object target) {
            this.target = target;
        }
    
        /**
         * 生成代理对象
         * 注意 Proxy.newProxyInstance() 方法接受三个参数:
         *      ClassLoader loader:指定当前目标对象使用的类加载器,获取加载器的方法是固定的
         *      Class<?>[] interfaces:指定目标对象实现的接口的类型,使用泛型方式确认类型
         *      InvocationHandler:指定动态处理器,执行目标对象的方法时,会触发事件处理器的方法
         * @return
         */
        public Object getProxyInstance() {
            return Proxy.newProxyInstance(
                    target.getClass().getClassLoader(),
                    target.getClass().getInterfaces(),
                    new InvocationHandler() {
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            // 执行目标对象方法
                            Object returnValue = method.invoke(target, args);
                            return returnValue;
                        }
                    }
            );
        }
    }
    

    调用:

    public static void main(String[] args) {
        // 目标对象
        IUserDao target = new UserDao();
        // 【原始的类型 class cn.itcast.b_dynamic.UserDao】
        System.out.println(target.getClass());
    
        // 给目标对象,创建代理对象
        IUserDao proxy = (IUserDao) new ProxyFactory(target).getProxyInstance();
        // class $Proxy0   内存中动态生成的代理对象
        System.out.println(proxy.getClass());
        // 执行方法
        proxy.save();
    }
    

    控制台输出:

    class com.marklogzhu.designpatterns.structure.proxy.dynamic.UserDao
    class com.sun.proxy.$Proxy0
    保存用户信息
    

    Cglib 代理

    Cglib代理,也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能的扩展.

    • JDK的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口,如果想代理没有实现接口的类,就可以使用Cglib实现.

    • Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展java类与实现java接口.它广泛的被许多AOP的框架使用,例如Spring AOP和synaop,为他们提供方法的interception(拦截)

    • Cglib包的底层是通过使用一个小而块的字节码处理框架ASM来转换字节码并生成新的类.不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉.

    Cglib子类代理实现方法:

    • 1.需要引入 cglib 的 jar 文件,但是 Spring 的核心包中已经包括了Cglib功能,所以直接引入pring-core-3.2.5.jar即可.

    • 2.引入功能包后,就可以在内存中动态构建子类

    • 3.代理的类不能为final,否则报错

    • 4.目标对象的方法如果为 final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法.

    修改代理工厂类:

    public class ProxyFactory implements MethodInterceptor {
    
        private Object target;
    
        public ProxyFactory(Object target) {
            this.target = target;
        }
    
        // 创建代理对象
        public Object getProxyInstance() {
            //1.工具类
            Enhancer en = new Enhancer();
            //2.设置父类
            en.setSuperclass(target.getClass());
            //3.设置回调函数
            en.setCallback(this);
            //4.创建子类(代理对象)
            return en.create();
    
        }
    
        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            //执行目标对象的方法
            Object returnValue = method.invoke(target, args);
            return returnValue;
        }
    }
    

    调用:

    public static void main(String[] args) {
        //目标对象
        UserDao target = new UserDao();
        //代理对象
        UserDao proxy = (UserDao)new ProxyFactory(target).getProxyInstance();
        //执行代理对象的方法
        proxy.save();
    }
    

    控制台输出:

    保存用户信息
    

    CGLIB 创建的动态代理对象比 JD K创建的动态代理对象的性能更高,但是 CGLIB 创建代理对象时所花费的时间却比 JDK 多得多。所以对于单例的对象,因为无需频繁创建对象,用 CGLIB 合适,反之使用 JDK 方式要更为合适一些。同时由于 CGLib 由于是采用动态创建子类的方法,对于 final 修饰的方法无法进行代理。

  • 相关阅读:
    随机森林算法参数调优
    BAYES和朴素BAYES
    阿里云 金融接口 token PHP
    PHP mysql 按时间分组 表格table 跨度 rowspan
    MySql按周,按月,按日分组统计数据
    PHP 获取今日、昨日、本周、上周、本月的等等常用的起始时间戳和结束时间戳的时间处理类
    thinkphp5 tp5 会话控制 session 登录 退出 检查检验登录 判断是否应该跳转到上次url
    微信 模板消息
    php 腾讯 地图 api 计算 坐标 两点 距离 微信 网页 WebService API
    php添加http头禁止浏览器缓存
  • 原文地址:https://www.cnblogs.com/markLogZhu/p/11582601.html
Copyright © 2011-2022 走看看