专栏首页java达人透过源码学习设计模式7-适配器模式与HandlerApapter

透过源码学习设计模式7-适配器模式与HandlerApapter

定义

适配器模式把一个类的接口,变换成客户端所期待的另一种接口,使原本因接口不匹配的两个类能够在一起工作。

结构

角色:

Client:用户类,使用新接口Target来完成某些特定的需求。

Target:新的接口类,开放特定接口request来完成某些特定操作,与Client协作。

Adaptee:原有的类,即需要适配的类或被适配者类。

Adapter:适配器类,将Adaptee中的接口封装成Target中的新接口,来满足新的需求。

过程:

客户通过目标接口调用适配器的方法对适配器发出请求,适配器使用被适配者接口把请求转换成被适配者的一个或多个调用接口。客户接收到调用的结果,但未察觉到一切都是适配器在起转换作用。

源码示例: HandlerAdapter

HandlerMapping将方法映射到URL,因此DispatcherServlet知道特定请求应该调用哪个方法。然后DispatcherServlet使用HandlerAdapter来调用该方法。

为什么DispatcherServlet不直接调用方法?

因为有很多方式可以调用方法,比如注解、xml等等。HandlerAdapter将DispatcherServlet和被调用的操作解耦。

HandlerAdapter就是其中的适配器类,它类似于handler对象和dispatcher servlet之间的桥。

你可以从下面HandlerAdapter源代码中看到的,有一个返回类型是ModelAndView的handle方法。每个HandlerAdapter都会实现这个方法,将HttpServletRequest和HttpServletResponse委托给handler对象,然后handler对象将使用这些HttpServletRequest/Response执行应用程序逻辑。

Object handler) throws Exception;

此应用程序逻辑执行将生成模型和视图。视图可以是视图名称字符串或视图对象的形式。模型包含将用于呈现视图的数据。HandlerAdapter将在ModelAndView对象中包装模型和视图。处理ModelAndView对象是dispatcher servlet的工作。

Dispatcher servlet不知道Handler对象,并且不直接处理应用程序逻辑。Handler对象也不用将模型和视图转换为ModelAndView对象,因为HandlerAdapter会完成转换工作。

HandlerAdapter的一些实现类:

1. SimpleServletHandlerAdapter: 适配实现 Servlet 接口的 Handler, 默认调用其 service 方法

2. SimpleControllerHandlerAdapter: 适配实现 Controller 接口的 Handler, 默认调用其 handleRequest 方法

3. HttpRequestHandlerAdapter: 适配实现 HttpRequestHandler 接口的 Handler, 默认调用其 handleRequest 方法

4. RequestMappingHandlerAdapter: 适配被@RequestMapping注释的方式, 一般都是解析一个一个参数, 并且通过反射进行激活

相关实现片段如下:

SimpleServletHandlerAdapter:

   @Override 

    public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) 

            throws Exception {

        ((Servlet) handler).service(request, response);

        return null;

    }

RequestMappingHandlerAdapter>AbstractHandlerMethodAdapter:

  @Override 

    public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) 

            throws Exception {

        return handleInternal(request, response, (HandlerMethod) handler);

    }



    @Override 

    protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

            ModelAndView mav;

            checkRequest(request);

           // Execute invokeHandlerMethod in synchronized block if required.

            if (this.synchronizeOnSession) {

                HttpSession session = request.getSession(false);

                if (session != null) {

                   Object mutex = WebUtils.getSessionMutex(session);

                    synchronized (mutex) {

                        mav = invokeHandlerMethod(request, response, handlerMethod);

                    }

                }

                else {

                    // No HttpSession available -> no mutex necessary

                    mav = invokeHandlerMethod(request, response, handlerMethod);

                }

            }

            else {

                // No synchronization on session demanded at all...

                mav = invokeHandlerMethod(request, response, handlerMethod);

            }

            if (!response.containsHeader(HEADER_CACHE_CONTROL)) {

                if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {

                    applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);

                }

                else {

                    prepareResponse(response);

                }

            }

            return mav;

      }

HttpRequestHandlerAdapter:

   @Override 

    public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) 

            throws Exception {

        ((HttpRequestHandler) handler).handleRequest(request, response);

        return null;

    }

SimpleControllerHandlerAdapter:

@Override 

    public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) 

            throws Exception {

        return ((Controller) handler).handleRequest(request, response);

    }

适用场景

1、 以前开发的系统存在满足新系统功能需求的类,但其接口同新系统的接口不一致, 需要重复使用现有的类,。

2、 想要建立一个可以重用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作。

3、 使用第三方提供的组件,但组件接口定义和自己要求的接口定义不同, 不希望修改自己的接口,但是要使用第三方组件接口的功能,避免重复造轮子。

优缺点

优点:

1、将目标类和适配者类解耦

2、增加了类的透明性和复用性,将具体的实现封装在适配者类中,对于客户端类来说是透明的,而且提高了适配者的复用性

3、灵活性和扩展性都非常好,符合开闭原则

结构图中展示的是对象适配器模式,即适配器实现我们的目标接口,但是并不继承需要被适配的类,而是通过在适配器的构造函数中将需要被适配的类传递进来从而进行适配。

对象适配器还有的优点:

  把多个不同的适配者适配到同一个目标,也就是说,同一个适配器可以把被适配者类和他的子类都适配到目标接口。

对象适配器的缺点:

 使得override(重定义)Adaptee的行为比较困难。如果一定要override Adaptee的方法,就只好先做一个Adaptee的子类以override Adaptee的方法,然后再把这个子类当作真正的Adaptee源进行适配。

类适配器即适配器Adapter继承被适配者Adaptee,并实现目标接口Target。

如图:

类适配器还有的优点:

 由于Adapter是Adaptee的子类,Adapter可以override(重定义) Adaptee的方法。

类适配器的缺点:

 对于Java、C#等不支持多重继承的语言,一次最多只能适配一个Adaptee,而且目标抽象类只能为接口,不能为类,其使用有一定的局限性。且该模式不能将一个适配者类和他的子类同时适配到目标接口。

本文分享自微信公众号 - java达人(drjava)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-09-28

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Java 中的 T,E,K,V, 别傻傻分不清楚!

    https://juejin.im/post/5d5789d26fb9a06ad0056bd9

    Java技术栈
  • 这个中秋,我用 Java 画了一个月饼!

    今年中秋节,我又想写点不一样的来给大家祝福,用 Java 来画一个月饼,听起来是不是很好玩?

    Java技术栈
  • 从头到尾说一次 Java 垃圾回收,写得非常好!

    之前上学的时候有这个一个梗,说在食堂里吃饭,吃完把餐盘端走清理的,是 C++ 程序员,吃完直接就走的,是 Java 程序员。?

    Java技术栈
  • Java虚拟机最多支持多少个线程?

    McGovernTheory在StackOverflow提了这样一个问题:Java虚拟机最多支持多少个线程?跟虚拟机开发商有关么?跟操作系统呢?还有其他的因素吗...

    Java技术栈
  • 请你解释一下什么是线程池(thread pool)?

    在面向对象编程中,创建和销毁对象是很费时间的,因为创建一个对象要获取内存资源或者其它更多资源。在Java中更是如此,虚拟机将试图跟踪每一个对象,以便能够在对象销...

    剑走天涯
  • 什么是 Java 对象深拷贝?面试必问!

    浅拷贝只是拷贝了源对象的地址,所以源对象的值发生变化时,拷贝对象的值也会发生变化。

    Java技术栈
  • AsyncContext异步请求的用法

    在Servlet 3.0中,在ServletRequest上提供了startAsync()方法

    全菜工程师小辉
  • 「Java Grammar」:运算符

    计算机自打诞生以来,用作最多的就是进行计算,而计算离不开运算符,所以运算符在我们的Java语言中的地位举足轻重,我们现在就来了解一下Java给我们提供的运算符。

    Vi的技术博客
  • 一起学Rust-实战leetcode(三)

    之后,你的输出需要从左往右逐行读取,产生出一个新的字符串,比如:"LCIRETOESIIGEDHN"。

    江湖安得便相忘
  • 一种非大小排序(先后关系排序)—拓扑排序

    在以前很多人可能听过拓扑排序,但可能认为它太难而不愿接触学习,也不清楚是排啥序的,然而拓扑排序实际很简单,生活中也很常用,面试笔试也会遇到,所以掌握拓扑排序已是...

    全菜工程师小辉

扫码关注云+社区

领取腾讯云代金券