代理设计模式的优点 (设计模式之代理模式)

点击上方 "程序员小乐"关注, 星标或置顶一起成长

第一时间与你相约

每日英文

There are plenty of things in life that you don't want to do but you have to,this is responsibility.For the things that you want to do but you can't,this is fate.

生命中有许多你不想做却不能不做的事,这就是责任;生命中有许多你想做却不能做的事,这就是命运。

每日掏心话

人生,顺其自然就好,心安自然快乐。当你看淡得失、无谓成败的时候,反倒顺风顺水、遇难成祥。

来自: 二营长的笔记 | 责编:乐乐

链接:cnblogs.com/happyone/p/11932874.html

动态代理设计模式,设计代理模式

程序员小乐(ID:study_tech)第 703 次推文 图片来自网络

往日回顾:我很神秘的采访了一位 Pornhub 工程师,聊了这些纯纯的话题

正文

什么是代理模式

代理模式就是为一个对象提供一个代理对象,由这个代理对象控制对该对象的访问。

理解代理模式,可以对照生活中的一些具体例子,比如房产中介、二手车交易市场、经纪人等。

为什么要用代理模式

通过使用代理模式,我们避免了直接访问目标对象时可能带来的一些问题,比如:远程调用,需要使用远程代理来帮我们处理一些网络传输相关的细节逻辑;可能需要基于某种权限控制对目标资源的访问,可以使用保护代理等。

总的来说,通过是用代理模式,我们可以控制对目标对象的访问,可以在真实方法被调用前或调用后,通过代理对象加入额外的处理逻辑。

代理模式分类

代理模式分为静态代理和动态代理。动态代理根据实现不同又可细分为JDK动态代理和cglib动态代理。

静态代理是由程序员创建或工具生成代理类的源码,再编译代理类。所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。

动态代理是在实现阶段不用关心代理类,而在运行时动态生成代理类的。

静态代理

以房哥买房子为例,用代码实现静态代理。

1、首先建立一个Seller接口

  • public interface Seller { void sell();}

2、创建实现类,房哥,有一个方法,就是买房子

  • public class FangGe implements Seller{ @Override public void sell() { System.out.println("房哥要出手一套四合院"); }}

3、买房子需要找到买家,达成交易后还要办理过户等其他手续,房哥只想卖房收钱就完了。因此,需要找一个代理来帮房哥处理这些杂事。

我们创建一个代理类FangGeProxy,代理类也需要实现Seller接口,行为上要保持和FangGe一样,都是要卖房子。同时该代理类还需要持有房哥的引用。

  • public class FangGeProxy implements Seller{ private FangGe fangGe;

  • public FangGeProxy(FangGe fangGe){ this.fangGe = fangGe; } @Override public void sell() { findBuyer(); fangGe.sell(); afterSell(); }

  • public void findBuyer(){ System.out.println("代理帮助寻找买主"); }

  • public void afterSell(){ System.out.println("达成交易后,办理相关手续"); }}

可以看到,房哥的代理类通过findBuyer()和afterSell()两个方法帮助房哥完成了其他一些杂事。

4、测试类

  • public class StaticProxyTest { public static void main(String[] args) { Seller seller = new FangGeProxy(new FangGe()); seller.sell(); }}

输出:

代理帮助寻找买主房哥要出手一套四合院达成交易后,办理相关手续

最后,看下类图

动态代理设计模式,设计代理模式

静态代理的问题:

1、由于静态代理类在编译前已经确定了代理的对象,因此静态代理只能代理一种类型的类,如果要给大量的类做代理,就需要编写大量的代理类;

2、如果我们要给Seller,也就是目标对象要增加一些方法,则需要同步修改代理类,不符合开闭原则。

JDK动态代理

JDK的动态代理依赖于jdk给我们提供的类库实现,是一种基于接口实现的动态代理,在编译时并不知道要代理哪个类,而是在运行时动态生成代理类。同时也解决了静态代理中存在的问题。

我们接上上面静态代理的例子,继续实现JDK的动态代理。

1、我们建一个方法转发的处理器类,该类需要实现InvocationHandler接口。

  • public class SellerInvocationHandler implements InvocationHandler {

  • // 要代理的真实对象 private Object target;

  • /** * 使用Proxy类静态方法获取代理类实例 */ public Object getProxyInstance(Object target){ this.target = target; Class<?> clazz = target.getClass(); return Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this); }

  • @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { before(); Object obj = method.invoke(this.target, args); after(); return obj; }

  • private void before() { System.out.println("执行方法前"); }

  • private void after() { System.out.println("执行方法后"); }}

2、新建JDK动态代理测试类,首先代理房哥卖房子

  • public class JDKDynamicProxyTest { public static void main(String[] args) {

  • // new一个房哥,下面帮房哥找个代理 FangGe fangGe = new FangGe(); SellerInvocationHandler sellerInvocationHandler = new SellerInvocationHandler();

  • // 房哥的代理对象 Seller seller = (Seller) sellerInvocationHandler.getProxyInstance(fangGe); seller.sell();

  • }}

输出:

执行方法前房哥要出手一套四合院执行方法后

可以看到,完成了代理。

3、接下来我们新建另外一个类,User类,并使用JDK动态代理完成代理User类

  • public interface IUser { void sayHello();

  • void work();}

  • public class UserImpl implements IUser{ @Override public void sayHello() { System.out.println("hello,我是小明"); }

  • @Override public void work() { System.out.println("我正在写代码"); }}

修改测试类,

  • public class JDKDynamicProxyTest { public static void main(String[] args) {

  • /* // new一个房哥,下面帮房哥找个代理 FangGe fangGe = new FangGe(); SellerInvocationHandler sellerInvocationHandler = new SellerInvocationHandler();

  • // 房哥的代理对象 Seller seller = (Seller) sellerInvocationHandler.getProxyInstance(fangGe); seller.sell();*/

  • // 代理user类 IUser user = new UserImpl(); SellerInvocationHandler sellerInvocationHandler = new SellerInvocationHandler(); IUser userProxy = (IUser) sellerInvocationHandler.getProxyInstance(user); userProxy.sayHello(); userProxy.work();

  • }}

输出:

执行方法前hello,我是小明执行方法后执行方法前我正在写代码执行方法后

可以看到,我们SellerInvocationHandler 并未做任何改动,它便能为UserImpl类生成代理,并在执行方法的前后增加额外的执行逻辑。

cglib动态代理

JDK动态代理有一个局限就是,被代理的类必须要实现接口。如果被代理的类没有实现接口,则JDK动态代理就无能为力了。这个时候该cglib动态代理上场了。

CGLIB是一个功能强大,高性能的代码生成包。它为没有实现接口的类提供代理,为JDK的动态代理提供了很好的补充。通常可以使用Java的动态代理创建代理,但当要代理的类没有实现接口或者为了更好的性能,CGLIB是一个好的选择。

1、新建一个MyCglibInterceptor,实现MethodInterceptor接口。该类类似于JDK动态代理中的InvocationHandler实例,是实现cglib动态代理的主要类。

  • public class MyCglibInterceptor implements MethodInterceptor {

  • public Object getCglibProxyInstance(Object object){ // 相当于Proxy,创建代理的工具类 Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(object.getClass()); enhancer.setCallback(this); return enhancer.create(); }

  • public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { before(); Object obj = methodProxy.invokeSuper(o, objects); after(); return obj; }

  • private void before() { System.out.println("执行方法之前"); }

  • private void after() { System.out.println("执行方法之后"); }}

2、新建cglib动态代理的测试类,先代理上面例子中的User类。

  • public class CglibDynamicProxyTest { public static void main(String[] args) { MyCglibInterceptor myCglibInterceptor = new MyCglibInterceptor(); IUser userCglibProxy = (IUser) myCglibInterceptor.getCglibProxyInstance(new UserImpl()); userCglibProxy.sayHello(); userCglibProxy.work(); }}

输出:

执行方法之前hello,我是小明执行方法之后执行方法之前我正在写代码执行方法之后

3、新建一个类HelloWorld,不实现任何接口,为该类实现动态代理。

  • public class HelloWorld { public void hello(){ System.out.println("世界这么大,我想去看看"); }}

测试代理类

  • public class CglibDynamicProxyTest { public static void main(String[] args) {/* MyCglibInterceptor myCglibInterceptor = new MyCglibInterceptor(); IUser userCglibProxy = (IUser) myCglibInterceptor.getCglibProxyInstance(new UserImpl()); userCglibProxy.sayHello(); userCglibProxy.work();*/

  • // 代理未实现任何接口的类 MyCglibInterceptor myCglibInterceptor = new MyCglibInterceptor(); HelloWorld helloWorldProxy = (HelloWorld) myCglibInterceptor.getCglibProxyInstance(new HelloWorld()); helloWorldProxy.hello(); }}

输出:

执行方法之前世界这么大,我想去看看执行方法之后

使用cglib动态代理,我们实现了对普通类的代理

欢迎在留言区留下你的观点,一起讨论提高。如果今天的文章让你有新的启发,学习能力的提升上有新的认识,欢迎转发分享给更多人。

猜你还想看

阿里、腾讯、百度、华为、京东最新面试题汇集

假如有人把支付宝存储服务器炸了(物理炸),大众在支付宝里的钱是不是就都没有了呢?

7 个显著提升编码效率的 IntelliJ IDEA 必备插件

Spring Boot 集成 Ehcache 缓存,三步搞定!

关注「程序员小乐」订阅号,收看更多精彩内容嘿,你在看吗?