Quantcast
Viewing all articles
Browse latest Browse all 5930

从狡猾的房产中介来看动态代理


探索Android软键盘的疑难杂症
深入探讨Android异步精髓Handler
详解Android主流框架不可或缺的基石
站在源码的肩膀上全解Scroller工作机制


Android多分辨率适配框架(1)— 核心基础
Android多分辨率适配框架(2)— 原理剖析
Android多分辨率适配框架(3)— 使用指南


自定义View系列教程00–推翻自己和过往,重学自定义View
自定义View系列教程01–常用工具介绍
自定义View系列教程02–onMeasure源码详尽分析
自定义View系列教程03–onLayout源码详尽分析
自定义View系列教程04–Draw源码分析及其实践
自定义View系列教程05–示例分析
自定义View系列教程06–详解View的Touch事件处理
自定义View系列教程07–详解ViewGroup分发Touch事件
自定义View系列教程08–滑动冲突的产生及其处理


版权声明


代理模式简介

代理模式(Proxy Pattern)是面向对象中一种非常常见的设计模式。其实,不单是在软件开发领域,在我们的日常生活中对于代理也时常可见。比如:房东要将自家的房租出售,于是到房地产中介公司找一个代理,由他来帮自己完成销售房屋,签订合同等等事宜。

在此,就以该生活场景为蓝本介绍Java的代理模式。一般而言,代理技术可分为:静态代理和动态代理;我们先来看静态代理。


静态代理示例

房东通过一纸协议将自己的房子挂靠在房屋中介公司,委托中介出售其房屋。嗯哼,我们来一起瞅瞅这个房东和中介公司共同达成的协议:

/**
 * 本文作者:谷哥的小弟
 * 博客地址:http://blog.csdn.net/lfdfhl
 */
package cn.com;

public interface TradeProtocol {
    public void sellHouse(int money);
}

嗯哼,这个协议很简单,房东委托中介公司售卖自己的房子。

既然是房东和房屋中介共同达成的协议,那么房东和中介都需要遵守该协议。

先来看房东:

/**
 * 本文作者:谷哥的小弟
 * 博客地址:http://blog.csdn.net/lfdfhl
 */
package cn.com;

public class Owner implements TradeProtocol{

    @Override
    public void sellHouse(int money) {
        System.out.println("我是房主,我的房子卖了"+money+"块钱");
    }

}

再来看中介:

/**
 * 本文作者:谷哥的小弟
 * 博客地址:http://blog.csdn.net/lfdfhl
 */
package cn.com;

public class HouseAgent implements TradeProtocol {
    private TradeProtocol mHouseProtocol;

    public HouseAgent(TradeProtocol houseProtocol){
        mHouseProtocol=houseProtocol;
    }
    @Override
    public void sellHouse(int money) {
        System.out.println("我是中介,业主的房子卖了"+money+"块钱");
        mHouseProtocol.sellHouse(money);
    }

}

请注意HouseAgent的实现

  • 在HouseAgent的构造方法中传入TradeProtocol的对象,比如Owner
  • 在HouseAgent的sellHouse()中调用TradeProtocol的对象(比如Owner)的sellHouse()
  • 所以,HouseAgent售卖(sellHouse())房子,实际上是房东自己售卖了房屋!

测试如下:

/**
 * 本文作者:谷哥的小弟
 * 博客地址:http://blog.csdn.net/lfdfhl
 */
package cn.com;

public class Test {

    public static void main(String[] args) {
        Owner owner=new Owner();
        HouseAgent houseAgent=new HouseAgent(owner);
        houseAgent.sellHouse(10000*100);
    }

}

测试结果:

我是中介,业主的房子卖了1000000块钱
我是房主, 我的房子卖了 1000000块钱

在该静态代理中,房东将房屋的售卖交给了中介代理,中介将房屋出售后又把钱一分不少地交给了房东。咦,这咋不对呢?现实中有这样大公无私,视金钱如粪土的房屋中介么?没有!绝对没有!万一,你发现有类似的情况,请举起自己的双手抽自己两耳光:醒醒,别睡了,别做白日梦了!

那么,中介是怎么赚钱的呢?我们继续往下看


动态代理示例

与之前一样,房东和房屋中介公司之间存有一纸协议:

/**
 * 本文作者:谷哥的小弟
 * 博客地址:http://blog.csdn.net/lfdfhl
 */
package cn.com;

public interface TradeProtocol {
    public void sellHouse(int money);
}

同样地,房东遵守该协议:

/**
 * 本文作者:谷哥的小弟
 * 博客地址:http://blog.csdn.net/lfdfhl
 */
package cn.com;

public class Owner implements TradeProtocol{

    @Override
    public void sellHouse(int money) {
        System.out.println("我是房主,中介告诉我:房子卖了"+money+"块钱");
    }

}

现在,房东又去房屋中介公司挂靠自己的房子,到了那一看:原来熟悉的那个中介出去办事了,刚好不在。正准备走呢,中介公司的经理挺着啤酒肚走过来,笑嘻嘻地说:请问您是来办理业务的吗?来,请坐,我给你们引荐以为我们这里服务最好的一个房屋中介人员办理您的相关事宜!

嗯哼,我们来看看这个这位被称为”服务最好的中介”是怎么卖掉房东的房子的:

package cn.com;

import java.lang.reflect.Proxy;

/**
 * 原创作者:谷哥的小弟
 * 博客地址:http://blog.csdn.net/lfdfhl
 */
public class TestProxy02 {

    public static void main(String[] args) {
        TradeProtocol owner=new Owner();
        ClassLoader classLoader=owner.getClass().getClassLoader();
        Class<?>[] interfaces = owner.getClass().getInterfaces();
        //创建InvocationHandler
        HouseAgentInvocationHandler invocationHandler=new HouseAgentInvocationHandler(owner);
        //生成动态代理
        TradeProtocol tradeProtocol=(TradeProtocol) Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
        tradeProtocol.sellHouse(1000000);
    }

}

HouseAgentInvocationHandler 的实现如下:

package cn.com;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * 原创作者:谷哥的小弟 
 * 博客地址:http://blog.csdn.net/lfdfhl
 */
public class HouseAgentInvocationHandler implements InvocationHandler {
    private Object object;

    public HouseAgentInvocationHandler(Object object) {
        this.object=object;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Integer money=(Integer) args[0];
        System.out.println("我是中介,业主的房子我实际卖了"+money+"块钱");
        Object result=method.invoke(object, new Object[]{money/2});
        System.out.println("我是中介,这次交易我赚了不少钱!!!");
        return result;
    }
}

好了,所涉及到的代码就这么多。或许有一点看不懂,这也无妨,我们先看看运行的结果再作详细的解释。控制台输出信息如下:

我是中介,业主的房子我实际卖了1000000块钱
我是房主,我的房子卖了500000块钱
我是中介,这次交易我赚了不少钱!!!

哇哈,狡猾的中介把房子卖了100W却告诉房东只买了50W,自己狠狠地赚了一笔!这是怎么回事呢?不急,我们通过这两个示例来回顾一下卖房子的经过。

  • 第一个示例中,房东将房屋委托给一个自己熟知的中介人员售卖房产;中介卖了多少钱就给房东多少钱
  • 第二个示例中,房屋中介公司的经理临场(动态)地指定了一个中介人员为房东办理售卖房屋的相关事宜
  • 第二个示例中,中介对售卖的过程做了手脚。由此可见:动态代理可方便地对被代理类的方法进行某些处理。比如,在方法实际调用前做一些过滤或者拦截操作或者修改,在方法调用后做一些善后处理等。

好了,生活中的例子看完了,我们再回到工作中的代码,结合示例分析Java动态代理技术。


动态代理技术分析

在Java的动态代理机制中,有两个非常重要的不可或缺的东西:

  • InvocationHandler接口

  • Proxy类

先来瞅瞅InvocationHandler接口

InvocationHandler is the interface implemented by the invocation handler of a proxy instance. Each proxy instance has an associated invocation handler. When a method is invoked on a proxy instance, the method invocation is encoded and dispatched to the invoke method of its invocation handler.

这段官方文档的主要含义是:
每个动态代理类都必须要实现InvocationHandler接口,并且每个动态代理类的实例都关联到了一个handler。当我们通过代理调用被代理对象的方法时,该方法的调用会被转发至InvocationHandler接口的 invoke( )中进行处理,方法如下:

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{}

该方法有三个输入参数,它们分别代表什么呢?

  • proxy:最终生成的代理
  • method:被代理对象正在执行的方法
  • args:被代理对象正在执行的方法的输入参数

这么说,或许有点摸不着头脑。没事,我们在HouseAgentInvocationHandler类的invoke( )方法开头加几句打印语句:

@Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("proxy.getClass()="+proxy.getClass());
        System.out.println("method.getName()="+method.getName());
        for(Object object:args) {
            System.out.println("object.getClass()="+object.getClass());
        }
        ..............
    }

输出结果:

proxy.getClass()=class com.sun.proxy.$Proxy0
method.getName()=sellHouse
object.getClass()=class java.lang.Integer

嗯哼,看了InvocationHandler接口,再来瞅瞅Proxy类。

Proxy provides static methods for creating dynamic proxy classes and instances, and it is also the superclass of all dynamic proxy classes created by those methods.

这段官方文档的主要含义是:
Proxy用于动态创建一个代理类及其实例。该类中我们常用 newProxyInstance( )方法:

 public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler handler)  throws IllegalArgumentException {}

该方法有三个输入参数,它们分别代表什么呢?

  • loader:加载动态代理类的ClassLoader,该输入参数通常为被代理类的类加载器
  • interfaces:动态代理类需要实现的接口,该输入参数通常为被代理类使用的接口
  • handler:动态代理对象在调用方法时与之关联的InvocationHandler

从这里我们可以看出: 动态代理与静态代理的原理是一样的,但是在动态代理中它没有具体的代理类而是通过Proxy在JVM运行时利用反射动态地生成了一个代理。在静态代理中,需要代理(中介)与被代理对象(房东)遵守同一份协议;其实在动态代理中也是非常类似的,只不过它换了一种方式罢了,这点从newProxyInstance( )方法的参数interfaces就能看出端倪:这些接口(interface)不就是静态代理中的协议么?也可以这么理解:动态代理对于协议的遵守发生在newProxyInstance( )时;并且动态代理可以对原来协议中的方法”做手脚”!

至此,我们再回过头来梳理一下动态代理示例及其相关技术:

  • 房东遵守房屋售卖协议
  • 动态地生成代理,在生成代理的过程中代理也遵守了房屋售卖协议
  • 代理在执行协议中的方法时(比如sellHouse( )),将该方法转发至与该代理密切相关的InvocationHandler中
  • 在InvocationHandler中可对协议中的方法(比如sellHouse( ))进行额外的附加的处理
  • 在InvocationHandler中利用反射调用协议中的方法(比如sellHouse( ))。其实,从这里也可以看出来:动态代理属于Java反射技术范畴;或许细心的人从导包语句中也有所发现:java.lang.reflect.InvocationHandler;嗯哼,InvocationHandler就在reflect包下

小感悟

很多刚开始做开发的童鞋喜欢拿着一本厚厚的设计模式在角落里默默地啃。学习的劲头很足,态度也很端正,配得上10086个赞。在此,我也想提醒一下小伙伴们:学习态度和努力程度固然非常重要,但是我们也要注意学习方法。抛开实际应用和业务逻辑单纯地看设计模式是很难理解其精髓的。我们不妨将设计模式和自己的实际工作结合起来学习,比如做Android开发的小伙伴可结合Android源码或者非常流行的第三方库来深入地研究设计模式,在此推荐一篇《Retrofit分析-漂亮的解耦套路》供大家学习参考


参考资料

作者:lfdfhl 发表于2017/9/5 10:38:38 原文链接
阅读:1478 评论:4 查看评论

Viewing all articles
Browse latest Browse all 5930

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>