wxuande 2019-06-27
回顾上两章,我们主要分析了HandlerAdapter的概念、作业以及Spring MVC如何使用的HandlerAdapter组件,本节以及后续几章,将介绍Spring为我们提供的HandlerAdapter的具体实现类,基于源码和设计层面进行介绍,欢迎大家关注。
本系列文章是基于Spring5.0.5RELEASE。
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进行构建,最终的项目结构如下:

-- 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方法上打断点,然后启动,具体如下:

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


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

以上我们验证了正确的情况,作为验证肯定不完善,我们还要验证错误的用例,比如,我们的Handler不继承HttpServlet,即不实现Servlet接口,按照SimpleServletHandlerAdatper适配策略,应该找不到handler进行处理,我们来验证这个结果。
首先修改我们的Handler,去掉extends HttpServlet,然后重启应用再次访问,结果首先界面会显示500错误,并且后台也跑异常,如下图:


至此,我们完成了测试验证。
本章主要分析了SimpleServletHandlerAdapter类源码以及开发了demo用于验证测试,验证测试覆盖了正常测试和异常测试,在实际的工作中,测试非常重要,不仅要测试正常情况也要对异常情况下进行测试,这样我们最大程度的确保我们开发的程序健壮。本章分析就这些,谢谢大家!
最后创建了qq群方便大家交流,可扫描加入,同时也可加我qq:276420284,共同学习、共同进步,谢谢!
