SpringMVC之源码分析--HandlerAdapter(三)
概述
回顾上两章,我们主要分析了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进行构建,最终的项目结构如下:
-- 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,共同学习、共同进步,谢谢!