透过源码学习设计模式7-适配器模式与HandlerApapter
- 2019 年 10 月 10 日
- 筆記
定义
适配器模式把一个类的接口,变换成客户端所期待的另一种接口,使原本因接口不匹配的两个类能够在一起工作。
结构

角色:
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,而且目标抽象类只能为接口,不能为类,其使用有一定的局限性。且该模式不能将一个适配者类和他的子类同时适配到目标接口。