zoukankan      html  css  js  c++  java
  • 从法外狂徒张三卖房引起的代理模式

    微信公众号:大黄奔跑
    关注我,可了解更多有趣的面试相关问题。

    写在之前

    谈到代理模式,最常用的使用场景莫过于 AOP 中的利用了,在讨论 AOP 的实现之前,先来聊聊什么代理模式。

    动态代理有两种形式,静态代理和动态代理,大家先不用在意两者的概念,等了解本篇你将会发现其实两者差别不大。

    静态代理

    用一个简单的例子来分析什么是静态代理,用买房张三卖房这件事儿为例,聊聊代理模式有何作用,为何如此使用如此频繁。

    Subject 接口:用于对被访问者的抽象化(比如卖房这件事儿)

    SubjectImpl:被访问者的具体实现(张三想要卖房)

    SubjectProxy:被访问者的代理实现类,该类需要有一个 Subject 接口具体的实例。(比如房产中介,需要拿着张三授权才可以代理)

    Client:访问者的抽象。(比如李四想买房,本身是一个顾客)

    代理模式

    代理类本身就是替被访问者做事的,李四想买房子,提了很多要求,比如朝南、学区房;房产中介(SubjectProxy)看张三家的房子刚好符合李四预期,将张三的房子介绍给李四;相当于当一个中间人的意思。有人可能会说,就类似于一个介绍的活儿吗?非得让中介来吗?如果只是简单的介绍,还真不需要中介,但是中介可以帮忙跑贷款、帮忙把握合同等。

    这样,张三只需要授权给中介,就可以一边做别的事儿,一边更加省心地完成交易。

    上面的图顺理成章变成了如下的模式

    卖房理解代理模式

    被代理的抽象接口——房子

    public interface House {
        /**
         * 卖房子
         */
        void sell();
    }
    

    被访问的类——张三的房子

    public class HouseForZhangsan implements House {
    
    
        @Override
        public void sell() {
            System.out.println("zhangsan sell house…………");
        }
    }
    

    代理类——房产中介

    public class HouseProxy implements House {
    
        private House house;
    
        // 通过构造方法做到每次替不同的人代理
        public HouseProxy (House house) {
            this.house = house;
        }
        @Override
        public void sell() {
            house = new HouseForZhangsan();
            house.sell();
        }
    }
    

    测试类——交易场所

    public class Test {
    
        public static void main(String[] args) {
            // 构造具体的卖房者
            House house = new HouseForZhangsan();
            // 将张三交给中介代理
            House houseForPerson = new HouseProxy(house);
            houseForPerson.sell();
        }
    }
    

    还是回到 Spring AOP 模式中,其中 SubjectProxy 就像是 SubjectImpl 的中介,而 SubjectImpl 本身是系统中的 JoinPoint 所在的对象(目标对象),顺理成章地为目标对象创建一个代理对象,完成切面的逻辑。

    AOP 代理对象

    但是各位想想,市面上并不是只有房子卖呢,张三家里有一辆空闲的车,也想卖,还能去找房产中介吗?

    肯定不能了。

    于是催生出了专门用于车交易的中介,比如瓜子二手车(号称没有中间商赚差价,哈哈哈)、二手车之家等等。

    二手车平台

    再比如万一张三突然发现手机用久了,想卖掉二手手机,新的 iPhone,于是又出现了各种各样的二手手机平台(其实本质也是一种中介)

    二手手机

    ……

    ……

    等等,那有人可能会想,能不能搞一个中介,能够啥都卖呢?于是更加全面的二手平台应运而生了。

    中介平台

    在这上面可以灵活的代理各种商品 (被代理对象),这就达到了一种动态中介的效果

    对,没错,动态代理已经介绍完了。

    image-20210417210040941

    开玩笑,继续聊聊 Spring AOP 是如何利用动态代理的。

    动态代理

    可以指定接口在运行期间动态的生成代理对象。(换句话说:无论你要卖什么,你来的时候都可以给你找一个对应的中介)

    那么如何动态生成代理类呢?

    需要借助两个工具,一个是 java.lang.reflect.Proxy 类 和 java.lang.reflect.InvocationHandler,问题的关键在于如何实时的给客户产生一个满足要去的中介。

    这个就是借助 InvocationHandler来动态生成代理类,还是以上面中介为例,我们姑且讲要生成的代理类叫做 target.

    如何动态产生不同类型的中介?

    第一步肯定需要知道此时替什么类型客户代理,但是又不能写得太死,我们姑且在生成代理类中先声明一个 被代理的对象。

    第二步:通过某种方式将 被代理对象通过传入的方式传进来

    第三步:将被代理对象与中介进行绑定。

    /**
     * 被代理的目标
     */
    public Object target;
    
    /**
     * 绑定委托对象,并且生成代理类
     * @param target
     * @return
     */
    public Object bind(Object target) {
        this.target = target;
        //绑定该类实现的所有接口,取得代理类
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), 
                                      target.getClass().getInterfaces(), this);
    }   
    

    上述几步部署完成之后,会明白中介要替什么人做事儿,中介做什么事儿,并且将中介与客户关联起来。

    客户与中介绑定

    最后才是真正的替客户做事儿。

    public class SellInvocationHandler implements InvocationHandler {
    
        /**
         * 被代理的目标
         */
        public Object target;
    
        /**
         * 绑定委托对象,并且生成代理类
         * @param target
         * @return
         */
        public Object bind(Object target) {
            this.target = target;
            //绑定该类实现的所有接口,取得代理类
            return Proxy.newProxyInstance(target.getClass().getClassLoader(), 
                                          target.getClass().getInterfaces(), this);
        }
    
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            log.info("额外逻辑……");
            return method.invoke(target, args);
        }
    }
    

    还记得我们之前说过的吗?

    动态代理解决的只是灵活产生不同代理类(换句话说灵活搭配不同类型中介)

    至于做什么类型事儿,和替什么人做什么事儿这两件事儿还是得存在。

    因此仍然需要申明两个类

    做什么类型事儿

    public interface House {
        /**
         * 卖房子
         */
        void sell();
    }
    

    替什么人做什么事儿

    public class HouseForZhangsan implements House {
        @Override
        public void sell() {
            System.out.println("zhangsan sell house…………");
        }
    }
    

    然后就可以愉快地进行交易了,每次有新的顾客来,就可以叫不同类型的中介来服务。

    public class DynamicProxyTest {
    
        public static void main(String[] args) {
    
            SellInvocationHandler invocationHandler = new SellInvocationHandler();
    
            // 将被访问类和代理类相互绑定( 将房产中介 与 房子卖者相互绑定 )
            House house = (House) invocationHandler.bind(new HouseForZhangsan());
            
            // 真正执行
            house.sell();
        }
    }
    

    至此,我们已经完成了真正的灵活代理工作。

    动态代理虽好,却不能解决所有的事情。比如,动态代理只能对实现了相应接口 (Interface) 的类使用,如果某个类没有实现任何的 Interface,就无法使用动态代理机制为其生成相应的动态代理对象。

  • 相关阅读:
    针对大数据量 高并发量网站的解决方案
    session cookie 在用户登录中的使用
    彻底弄懂HTTP缓存机制及原理
    cookie 和session 的区别详解
    常见的浏览器兼容性问题
    内容分发网络(CDN)
    表现与数据分离、Web语义化
    关于bootstrap样式重写,无法覆盖的问题
    判断一个字符串中出现次数最多的字符
    [译]SQL Passion Week 10: 计划缓存
  • 原文地址:https://www.cnblogs.com/xiaxj/p/14753847.html
Copyright © 2011-2022 走看看