SpringMVC之源码分析--HandlerAdapter(三)

wxuande 2019-06-27

概述

回顾上两章,我们主要分析了HandlerAdapter的概念、作业以及Spring MVC如何使用的HandlerAdapter组件,本节以及后续几章,将介绍Spring为我们提供的HandlerAdapter的具体实现类,基于源码和设计层面进行介绍,欢迎大家关注。

本系列文章是基于Spring5.0.5RELEASE。

SimpleServletHandlerAdapter

SimpleServletHandlerAdapter适配器是Spring MVC提供的实现HandlerAdapter接口的最简单的一个,其功能就是对javax.servlet.Servlet进行适配,从其源码可知,在support方法中,判断我们的类是否是Servlet,也即是否继承Servlet类为条件来进行适配的,并且在handle方法中调用的service方法,源码如下:

public class SimpleServletHandlerAdapter implements HandlerAdapter {

    /**
     * 实现handler的匹配策略,此处以handler是否是Servlet子类进行的判断
     * 也就是说,判断我们的handler是否实现javax.servlet.Servlet接口或者继承其子类
     */
    @Override
    public boolean supports(Object handler) {
        return (handler instanceof Servlet);
    }
    
    /**
     * 请求处理方法,此处调用的是handler的service方法
     * 如果我们定义的handler直接实现了Servlet,那么需要实现其service方法
     * 但一般我们在进行Servlet编程时,并不直接实现此接口,而是继承HttpServlet抽象类,
     * 该抽象类实现了Servlet接口的service方法,我们的handler只需要根据需求,重写doGet、doPost等方法即可
     */
    @Override
    @Nullable
    public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        // 调用handler的service方法
        ((Servlet) handler).service(request, response);
        return null;
    }

    
    @Override
    public long getLastModified(HttpServletRequest request, Object handler) {
        return -1;
    }

}

以上就是SimpleSerlvetHandlerAdapter的源码,这是Spring使用HandlerAdapter最简单的方式,此方式是为了在Spring中支持Servlet方式开发,即把Servlet适配为处理器handler。

实战

好几篇文章没有写这部分了,其实每一篇文章我都有写过测试demo,但是由于篇幅问题,没有发布出去,本章内容较少,所以跟大家分享下如何验证以及验证的方法。

  • 项目结构

在开发IDE中创建webapp项目,我使用的IDEA,用maven进行构建,最终的项目结构如下:

SpringMVC之源码分析--HandlerAdapter(三)

-- java目录是我们存放源代码的目录

-- resources是存放配置文件的目录,如spring-servlet.xml是Spring MVC的配置文件

-- webapp是存放静态资源以及view视图目录,其中/WEB-INF/web.xml是部署描述文件,在其内部配置Spring MVC的DispatcherServlet

  • 项目源码

    • pom文件

      由于我们使用maven进行构建,在其pom文件中加入项目依赖,就本项目我们需要添加Spring MVC的依赖,最终源码如下:

      <?xml version="1.0" encoding="UTF-8"?>
      
      <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        
        <!-- 项目的maven坐标 -->
        <groupId>com.github.dalianghe</groupId>
        <artifactId>spring-mvc-handleradapter</artifactId>
        <version>1.0-SNAPSHOT</version>
        <packaging>war</packaging>
      
        <name>spring-mvc-handleradapter Maven Webapp</name>
        <!-- FIXME change it to the project's website -->
        <url>http://www.example.com</url>
      
        <properties>
          <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
          <maven.compiler.source>1.8</maven.compiler.source>
          <maven.compiler.target>1.8</maven.compiler.target>
        </properties>
      
        <dependencies>
          <!-- 引入Spring MVC依赖 -->
          <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.0.5.RELEASE</version>
          </dependency>
          <!-- 引入Servlet依赖,不是必须的,如果本地需要使用Servlet相关API需要引入 -->
          <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
          </dependency>
        </dependencies>
      
        <build>
          <finalName>spring-mvc-handlermapping</finalName>
          <pluginManagement>
            <plugins>
              <!-- tomcat插件 -->
              <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat7-maven-plugin</artifactId>
                <version>2.2</version>
                <configuration>
                  <path>/</path>
                  <port>8086</port>
                </configuration>
              </plugin>
            </plugins>
          </pluginManagement>
        </build>
      </project>
    • Spring MVC配置文件

      目前我们是用的传统的基于xml配置的方式进行编程的,故需要创建一个Spring MVC的配置文件,用于初始化Spring MVC上下文以及配置策略,源码如下:

      <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://www.springframework.org/schema/beans"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns:mvc="http://www.springframework.org/schema/mvc"
             xmlns:context="http://www.springframework.org/schema/context"
             xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
          http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
          http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd"
             default-autowire="byName">
      
          <!-- 使用SimpleUrlHandlerMapping注册我们的handler -->
          <bean id="handlerMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
              <property name="urlMap">
                  <props>
                      <prop key="/servletController">servletController</prop>
                  </props>
              </property>
          </bean>
          <!-- 配置SimpleServletHandlerAdapter适配器 -->
          <bean id="handlerAdapter" class="org.springframework.web.servlet.handler.SimpleServletHandlerAdapter"/>
          <!-- 定义我们的handler -->
          <bean id="servletController" class="com.github.dalianghe.controller.ServletController"/>
      
      </beans>

      本例我们使用的SimpleUrlHandlerMapping注册我们的handler的,也可以使用其他HandlerMapping进行注册。

    • 部署描述文件

      在web.xml中配置Spring MVC的DispatcherServlet,代码如下:

      <?xml version="1.0" encoding="UTF-8"?>
      <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
               version="3.1">
        <display-name>Archetype Created Web Application</display-name>
      
        <servlet>
          <!-- Servlet名称,可任意定义,但必须与servlet-mapping中对应 -->
          <servlet-name>dispatcher</servlet-name>
          <!-- 指定Spring MVC核心控制类,即J2EE规范中的前端控制器(Front Controller) -->
          <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
          <!-- 指定Spring MVC配置文件,默认在WEB-INF目录下,切名字为[servlet-name]-servlet.xml,此文件中配置web相关内容,比如:指定扫描Controller路径、配置逻辑视图前缀后缀、上传文件等等 -->
          <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring-servlet.xml</param-value>
          </init-param>
          <!-- 此配置的值为正整数时,表示容器启动时初始化,即调用Servlet的init方法 -->
          <load-on-startup>1</load-on-startup>
          <async-supported>true</async-supported>
        </servlet>
        <!-- 定义servlet映射 -->
        <servlet-mapping>
          <!-- 与servlet中servlet-name对应 -->
          <servlet-name>dispatcher</servlet-name>
          <!-- 映射所有的url -->
          <url-pattern>/</url-pattern>
        </servlet-mapping>
      
      </web-app>
    • 编写Handler

      最后我们编写自己的Handler,根据SimpleServletHandlerAdatper要求,我们的Handler必须实现javax.servlet.Servlet接口,代码如下:

      package com.github.dalianghe.controller;
      
      import org.springframework.lang.Nullable;
      import org.springframework.web.servlet.ModelAndView;
      import org.springframework.web.servlet.mvc.Controller;
      
      import javax.servlet.ServletException;
      import javax.servlet.http.HttpServlet;
      import javax.servlet.http.HttpServletRequest;
      import javax.servlet.http.HttpServletResponse;
      import java.io.IOException;
      import java.io.Writer;
      
      /**
       * 本例我们继承HttpServlet抽象类,其内部实现了Servlet接口的service方法
*/
        public class ServletController extends HttpServlet{
        
            @Override
            protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
                Writer writer = resp.getWriter();
                writer.write("hello SimpleServletHandlerAdapter!");
                writer.flush();
            }
            
        }

至此,我们就编写完成了我们的代码,下面进行测试验证。
  • 测试验证

我们配置使用了SimpleServletHandlerAdapter适配器,首先验证Spring MVC是不是使用我们配置的适配器,验证方法是在DispatcherServlet的initHandlerAdapters方法上打断点,然后启动,具体如下:

SpringMVC之源码分析--HandlerAdapter(三)

有图可知,Spring MVC确实使用的是我们配置的SimpleServletHandlerAdapter,验证通过。

接下来,我们验证用户请求是否使用的SimpleServletHandlerAdapter适配器进行的处理,在DispatcherServlet的doDispatch方法中打断点以及在我们的handler方法中打断点,并发起请求http://localhost:8086/servletController,结果如下图:

SpringMVC之源码分析--HandlerAdapter(三)

SpringMVC之源码分析--HandlerAdapter(三)

由以上可知,Spring MVC使用SimpleServletHandlerAdapter适配器把请求转发给我了我们的Handler。跳过断点,浏览器也会数据hello SimpleServletHandlerAdapter,如下图:

SpringMVC之源码分析--HandlerAdapter(三)

以上我们验证了正确的情况,作为验证肯定不完善,我们还要验证错误的用例,比如,我们的Handler不继承HttpServlet,即不实现Servlet接口,按照SimpleServletHandlerAdatper适配策略,应该找不到handler进行处理,我们来验证这个结果。

首先修改我们的Handler,去掉extends HttpServlet,然后重启应用再次访问,结果首先界面会显示500错误,并且后台也跑异常,如下图:

SpringMVC之源码分析--HandlerAdapter(三)

SpringMVC之源码分析--HandlerAdapter(三)

至此,我们完成了测试验证。

总结

本章主要分析了SimpleServletHandlerAdapter类源码以及开发了demo用于验证测试,验证测试覆盖了正常测试和异常测试,在实际的工作中,测试非常重要,不仅要测试正常情况也要对异常情况下进行测试,这样我们最大程度的确保我们开发的程序健壮。本章分析就这些,谢谢大家!

最后创建了qq群方便大家交流,可扫描加入,同时也可加我qq:276420284,共同学习、共同进步,谢谢!

SpringMVC之源码分析--HandlerAdapter(三)

相关推荐