-
透过代理模式探究JAVA的静态代理和动态代理
一、代理代理是英文Proxy翻译过来的。我们在生活中见到过的最常见的代理大概就是顾客和商家的关系了按理说,顾客可以直接从厂家购买产品,但是现实生活中,很少有这样的销售模式。一般都是厂家委托给代理商进行销售,顾客跟代理商打交道,而不直接与产品实际生产者进行关联。所以,代理就有一种中间人的味道。接下来,我们说说软件中的代理模式。二、代理模式代理模式是面向对象中比较常见的一种设计模式了。这是代理模式常见的UML图,代理模式可以为其他对象提供一种代理以控制对这个对象的访问,比如像对一个类的访问做一些控制或加强的时候都可以用到代理模式(AOP就是基于此发展而来的)。对于代理模式的理解需要注意的有下面几点:(1)用户只关心接口功能,而不在乎谁提供了功能。上图中接口是Subject。(2)接口真正实现者是上图的RealSubject,但是它不与用户直接接触,而是通过代理。(3)代理就是上图中的Proxy,由于它实现了Subject接口,所以它能够直接与用户接触。(4)用户调用Proxy的时候,Proxy内部调用了RealSubject。所以,Proxy是中介者,它可以增强\控制RealSubject操作。光说概念比较难以理解,下面我用示例来说明。值得注意的是,代理可以分为静态代理和动态代理两种。先从静态代理讲起。三、静态代理我们平常去电影院看电影的时候,在电影开始的阶段是不是经常会放广告呢?电影是电影公司委托给影院进行播放的,但是影院可以在播放电影的时候,产生一些自己的经济收益,比如卖爆米花、可乐等,然后在影片开始结束时播放一些广告。现在用代码来进行模拟。首先得有一个接口,通用的接口是代理模式实现的基础。这个接口我们命名为Movie,代表电影播放的能力。然后,我们要有一个真正的实现这个Movie接口的类,和一个只是实现接口的代理类。这个类实现了Movie接口的play方法,只要调用就可以播放,接下来还需要一个代理类来代理这个”电影“这里的MovieProxy就是一个代理类它有一个play()方法。不过调用play()方法时,它进行了一些相关利益的处理,那就是广告。现在,测试一下执行结果:从执行的结果可以看到,代理模式可以在不修改被代理对象的基础上,通过扩展代理类,进行一些功能的附加与增强。值得注意的是,代理类和被代理类应该共同实现一个接口,或者是共同继承某个类。上面这种代理方式因为它的类型是我们程序员或接助工具事先预定好的,比如上面代码中的MovieProxy这个类。所以他被称为静态代理模式。下面要介绍的内容就是动态代理。四、动态代理在Java开发中,很多技术和框架都用到了动态代理,它与静态代理的区别就是动态与静态的区别:上一节代码中MovieProxy类是代理,需要程序员手动编写代码让MovieProxy实现Movie接口,而在动态代理中,我们可以让程序在运行的时候自动在内存中创建一个实现Movie接口的代理,而不需要去定义MovieProxy这个类。这就是它被称为动态的原因。下面用一个例子来说明:假设有一个大商场,商场有很多的柜台,有一个柜台卖茅台酒。我们进行代码的模拟。SellWine是一个买酒的就扣,实现它就可以买酒了,类似于许可证。之后比如我们要买茅台酒,那就实现SellWine获取许可之后按照静态代理就要写一个代理对象了,可是动态代理不需要了,在Java中我们可以使用Proxy.newProxyInstance()方法来动态的创建代理对象。之后客户端通过代理对象买酒就可以了执行结果:可以看到,我并没有像静态代理那样为SellWine接口实现一个代理类,但最终它仍然实现了相同的功能,这其中的差别,就是之前讨论的动态代理所谓“动态”的原因。五、Java对动态代理的支持1、newProxyInstance方法Java中动态代理涉及到一个非常重要的类Proxy。正是通过Proxy的静态方法newProxyInstance才会动态创建代理。说一下它三个参数的含义:loader:类加载器interfaces:需要代理的接口h:代理类的具体实现,InvocationHandler是一个接口,需要有一个具体的实现类,在这个实现类中可以定义动态生成的代理类可以有哪些行为2、InvocationHandler接口InvocationHandler内部只是一个invoke()方法,正是这个方法决定了怎么样处理代理传递过来的方法调用。proxy:代理对象method:代理对象调用的方法args:调用的方法中的参数在了解了基本的语法之后,我们加大难度,现在我扩大经营了,我还要买五粮液。Wuliangye这个类也实现了SellWine这个接口,说明它也拥有卖酒的许可证,同样把它放到柜台(Counter)上售卖。执行结果:可以看到,结果符合预期。使用动态代理扩展业务十分的快捷,这一切还得归功于Java的反射机制。到这里,脑子灵的同学就会产生好奇了,为什么一个newProxyInstance方法就可以实现动态生成代理对象?它在底层是不是主动帮我们new了一个对象?那么事实是不是这样子呢?直接查看它们的源码好了。(需要说明的是,我当前查看的源码是jdk1.8版本。)查找或生成指定的代理类:有上面的源码可知,在newProxyInstance方法中它确实为我们创建了一个代理类的对象,但是不是简单的new,而是通过反射创建了一个实例。另外,需要我们注意的是:它不会一上来就通过反射创建代理对象的实例,而是先在给定类加载器中检查,如果发现有实现了给定接口的代理类存在时,它就会简单地返回这个代理对象,否者通过ProxyClassFactory类创建代理类的实例。ProxyClassFactory是Proxy类的内部类这个类的注释可以了解到,它通过指定的ClassLoader和接口数组用工厂方法生成proxyclass。然后这个proxyclass的名字是:所以,动态生成的代理类名称是包名+$Proxy+id序号。生成的过程,核心代码如下:到这里,我们J只需要知道Java动态动态代理就是通过反射实现的以及动态代理生成的类名就是包名+$Proxy+id序号即可。下面我们来验证一下动态生成的代理类的名字是不是包名+$Proxy+id序号。执行结果:六、动态代理的应用动态代理的一个显著作用就是可以在不修改被代理对象的源码上,进行功能的增强。熟悉Spring的同学对这个观念应该不陌生,这就是AOP的作用,AOP就可以使用JDK的动态代理来实现(但是Spring采用了另一种方法,详情参考我的另一篇博客Spring从入门到精通—AOP与AspectJ的关系?原生JDK和CGLib手动实现AOP?)。因此,关于动态代理应用,一个比较重要的方向就是实现AOP编程,还有一个就是MyBatis通过动态代理生成Mapper接口的实现类......还有很多,总之凡是想在访问一个类时做一些控制都可以使用代理。总结代理分为静态代理和动态代理两种。静态代理,代理类需要自己编写代码写成。动态代理,代理类通过Proxy.newInstance()方法生成。不管是静态代理还是动态代理,代理与被代理者都要实现两样接口,它们的实质是面向接口编程。静态代理和动态代理的区别是在于要不要开发者自己定义Proxy类。动态代理通过Proxy动态生成proxyclass,但是它也指定了一个InvocationHandler的实现类。代理模式本质上的目的是为了增强现有代码的功能。转载来自:https://blog.csdn.net/briblue/article/details/73928350
LoveIT 2020-06-04设计模式