Talk is cheap , show me your code!
欢迎来到付振南Java博客,让我们一起学习Java吧!

设计模式中的动态代理(基于接口) 动态代理其实真的没有那么难!

今天给大家讲讲我最近学习的动态代理,在讲动态代理之前,我先给大家讲一个故事。

案例

早些时候,我们买电脑,都要直接去生产厂家购买,那时候还没有经销商,代理商出现,过段时间之后,有的人电脑出现了问题,要返厂维修,有的人遇到了使用问题,需要厂家提供售后服务, 但是随着购买的人数增多, 这样的问题越来越多,厂家既需要提供销售服务,又要提供售后服务,成本越来越高,工作量也大,就这样出现了现在的经销商,代理商的角色。

代理商的出现,使得人们不需要再直接通过厂家购买电脑,代理商去厂家批量拿货,消费者只需要到代理商那购买,厂家只负责生产,代理商负责去厂家购买并销售给消费者,各自分工明确,大家的效率明显都提高了。但是代理商也不是慈善机构,他也要赚钱,所以他去厂家拿货并不是给消费者出售的最终价格。比如一台电脑售价6999元,代理商上厂家拿货可能只需要4000元,2999元便是自己的利润。有些代理商甚至提供售后服务,实在没办法的才寄回厂家,所以效率大大的提升了。

代码演示

这便是我们的动态代理思想。接下来我们用代码来实现一下基于接口的动态代理。

首先创建一个生产者类(生产工厂),代码如下图。

/**
 * 生产者(生产厂家)
 */
public class Producer implements IProducer{

    /**
     * 销售产品
     * @param money
     */
    @Override
    public void saleProduct(float money){
        System.out.println("销售产品,厂家获得钱:"+money);
    }

    /**
     * 售后服务
     * @param money
     */
    @Override
    public void afterSaleService(float money){
        System.out.println("提供售后服务,厂家获得钱"+money);
    }
}

生产工厂有两个方法,一个是销售产品,一个是提供售后服务,并实现一个生产厂家接口。因为代理商可能代理很多家厂家的产品,所以我们需要一个接口来定制一个标准。接口代码如下。

/**
* 对生产厂家要求的接口
*/
public interface IProducer {
/**
* 销售产品
*
@param money
*/
public void saleProduct(float money);

/**
* 售后服务
*
@param money
*/
public void afterSaleService(float money);
}

厂家有了,厂家标准有了,接下来我们的代理该上场了。

/**
 * 消费者
 */
public class Consumer {
    public static void main(String[] args){
        final Producer producer = new Producer();

        /**
         * 动态代理
         */
        IProducer proxyProducer = (IProducer) Proxy.newProxyInstance(producer.getClass().getClassLoader(),
                producer.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 {
                        //提供增强代码
                        Object returnValue = null;

                        //1.获取方法执行的参数
                        Float money = (Float) args[0];
                        //2.判断当前方法是不是销售产品
                        if ("saleProduct".equals(method.getName())){
                            returnValue = method.invoke(producer,money*0.8f);
                        }
                        return returnValue;
                    }
                });
        proxyProducer.saleProduct(10000f);
    }
}

首先实例化一个厂家对象,然后让我们的代理上场。我们使用Proxy类中的newProxyInstance方法来创建一个代理对象。

通过源码我们看到,这个方法需要三个参数,第一个ClassLoader(类加载器),它是用于加载代理对象字节码的,和被代理对象使用相同的类加载器,这是固定写法;第二个Class[](字节码数组),它是用于让代理对象和被代理对象有相同方法,这也是固定写法;第三个便是我们的重点了,InvocationHandler(一个调用处理器,用于提供增强的代码),它是让我们写如何代理。我们一般都是写一个该接口的实现类,通常情况下都是匿名内部类,但不是必须的,此接口的实现类都是谁用谁写

可是解释了这么多,大家还是懵逼的状态,我们再来结合上面的案例来解释一下。第一个参数,用于加载代理对象的字节码文件,这里代理对象便是生产厂家了,所以这里填producer.getClass().getClassLoader()第二个参数,它是一个数组类型,用于让代理对象和被代理对象有相同方法,这就很好理解了,我们把接口中的方法全都放进来就行,所以就是producer.getClass().getInterfaces()

第三个参数, 这里就是动态代理的核心内容了,它用于提供增强的代码,它的匿名内部类有一个invoke方法,执行被代理对象的任何接口方法都会经过该方法,也就是我们通常说的拦截。它也有三个参数,Object proxy, Method method, Object[] args,这里就很好理解了,第一个是代理对象的引用,第二个就是当前正在执行的方法,第三个就是当前执行的方法所需要的参数,这个方法的返回值和被代理对象有相同的返回值。

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//提供增强代码
Object returnValue = null;

//1.获取方法执行的参数
Float money = (Float) args[0];
//2.判断当前方法是不是销售产品
if ("saleProduct".equals(method.getName())){
returnValue = method.invoke(producer,money*0.8f);
}
return returnValue;
}

因为我们代理商代理的是厂家的销售服务(这里我们就不讲售后了,原理一样),所以我们增强的是它的销售方法。 我们首先需要定义一个Object类型的返回值,第一步获取方法执行的参数,Float money = (Float) args[0];,第二步判断当前方法是不是销售方法,直接用销售方法名和当前方法名equals就行,这个方法的返回值是method.invoke(target,args),由于代理拿货的价格并不是最终给消费者的价格,所以我们这个在厂家销售基础上给代理打个八折,所以就是method.invoke(producer,money*0.8f),然后我们把这个值返回。

最后,我们假设代理卖一台10000元的电脑,proxyProducer.saleProduct(10000f);我们来看一下运行结果:

很完美,我们要的就是这个答案,代理帮助厂家销售产品,厂家获得8000块钱,至此为止,我们基于接口的动态代理全部完成了,我们并没有操作任何厂家的销售方法,而是通过代理商代理厂家销售,厂家和代理都拿到钱了。这看起来很难,其实搞懂那几个参数其实还是很简单的,主要就是需要增强的代码要会写。

  • 我们通过这个案例,总结一下动态代理的特点。
  • 1.我们不难发现,代理类是在程序运行时创建的,字节码随用随创建,随用随加载,然而,一旦被创建了,就变成了常规类,与虚拟机中的其他类没有任何区别。
  • 2.我们不用修改源码的基础上便对方法进行了增强,这是动态代理最大的好处。
赞(3) 打赏
未经允许不得转载:付振南Java博客 » 设计模式中的动态代理(基于接口)

相关推荐

  • 暂无文章

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏