第5 章?搭建Spring MVC
??本章学习内容
* Spring MVC简介;
* Spring MVC程序运行原理;
* Spring MVC的体系结构;
* 基于注解方式配置控制器;
* Spring MVC注解详解。
5.1
Spring MVC简介
前面的章节介绍了Spring IoC、Spring AOP和Spring对JDBC的支持。Spring框架可以说是企业开发中技术体系最全面的一个框架,围绕着Spring Framework已经形成了完整的技术体系。目前,基于Web的MVC框架非常多,发展也很快,如JSF、Struts 1、Struts?2和Spring MVC等。Struts 1.x框架在2001年出现后成为主流,之后陆续出现了Struts 2和JSF等框架。从架构上说,Struts 2是一款非常优秀的软件,几乎成为Java EE开发的MVC框架的事实标准。但是由于Struts 2团队基本不再对其进行更新,只发布补丁,随着时间的推移,特别是2010年之后,Struts 2陆续曝出了多个漏洞。很多网络安全和开发团队都给出了类似的建议:鉴于Struts 2至今为止已经多次曝出严重的高危漏洞,如果不是必要,建议开发者以后考虑采用其他类似的Java开发框架。所以广大的开发者将目光移向了Spring MVC。 Spring MVC是后起之秀,从应用上来说要复杂一些,但是它基于Spring进行开发,继承了Spring的优秀血脉,所以一跃成为采用率最高的Java EE Web MVC框架。
5.2
第一个Spring MVC案例
首先通过一个简单的案例来了解Spring MVC。
该案例的实现步骤如下。
(1)获取Spring框架的JAR库文件。
截至2020年5月,Spring Framework已发布的稳定版本(GA版)为Spring 5.2.6版,如图5-1所示。Spring官方网站改版后,建议通过?Maven和Gradle下载,若不使用Maven和Gradle开发项目,可以通过Spring Framework官网直接下载,下载路径为http://repo. springsource.org/libs-release-local/org/springframework/spring。
对于已下载的ZIP文件,可以直接解压缩在本地文件夹。由于Spring框架的JAR包的深度特别深,造成解压后的文件夹名字超过了WinRAR或WinZIP软件的最长允许范围,在Windows操作系统下可能会导致解压失败,如果解压过程中出现问题,可以使用7ZIP软件来解压。
解压完成后,在文件夹中找到libs文件夹,有63个后缀名为jar的文件。仔细观察 可以发现,每个主题有3个文件,例如,核心包文件为spring-core-5.2.26.RELEASE.jar、spring-core--5.2.26.RELEASE-javadoc.jar和spring-core--5.2.26.RELEASE-sources.jar。其中文件名结尾为javadoc的文件是Java根据注释自动生成的文档,而文件名结尾为sources的文件则是源代码文件。可以将所有的JAR文件导入到Web项目中,最简单的方法是直接复制到项目的WebRoot的lib文件夹下。
图5-1 Spring框架的下载
如果使用MyEclipse2014开发工具,可以使用MyEclipse内置的Spring包,只是版本稍低一些,不过并不影响学习和使用。Spring 5.x的主要升级在于集成了Spring Boot模块,在启动方式上有很大的差别。对于初学者来说Spring 3.x更容易上手,所以本书后面依然采用MyEclipse2014+Spring3.x的方式来入门。虽然工具和版本更新换代会很快,但是Spring框架的思想和原理是稳定的,熟练掌握之后可以很快地完成版本切换。
(2)在Web项目中加入Spring框架。
通过MyEclipse2014新建一个Web项目,项目名为ssmBook_ch6。在新建的Web项目中加入Spring框架,同时选中Spring Web模块以便支持Spring MVC,如图5-2所示。
图5-2?在MyEclipse中添加Spring MVC
(3)配置Dispatchservlet和对应的Servlet。
接下来在项目的web.xml配置文件中加入DispatchServlet的配置。
Servlet的名字在这里命名为springapp,也可以自由定义,但是后续的定义都要修改成对应自定义的名字,如图5-3所示。由于Spring MVC采用了约定优先于配置的方式,它会将此处的Servlet名字加上-servlet.xml的后缀,以此来查找一个名为springapp-servlet.xml的配置文件。
图5-3?web.xml配置文件
在与web.xml文件相同的位置上,新建一个名为springapp-servlet.xml的xml文件。该springapp-servlet.xml配置文件中定义了用户请求的路径和对应的控制器的映射关系,如图?5-4所示。
图5-4?springapp-servlet.xml配置文件
在这个配置文件中,核心的语句是
。
当用户请求的路径是hello.htm时,它告诉spring用对应包的HelloController类来处理用户的请求。该语句用来取代以前纯servlet开发时servlet和url之间的关系映射。
(4)创建控制器类。
接下来创建HelloController的类,它的作用类似于以前的Servlet。
代码如下所示:
//代码清单5-1 HelloController.java
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
public class HelloController implements Controller {
//返回ModelAndView对象
public ModelAndView handleRequest(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException{
//向Request域中放入一条信息,给前端jsp用
request.setAttribute("message", "hello,Spring MVC");
//返回jsp的路径
return new ModelAndView("hello.jsp");
}
}
这个控制器类比原始的Servlet更简洁,它继承了Controller接口,并实现了handleRequest方法。handleRequest方法的两个参数就是原始的HttpServletRequest和HttpServletResponse。为了演示效果,使用Request放入一个字符串信息,然后在hello.jsp页面中显示出这个字符串。
(5)建立jsp文件。
在WebRoot文件夹下新建hello.jsp文件,其内容如下。
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
hello jsp页面
显示服务器信息如下:${requestScope.message }
该步骤的目的是显示HelloController类中放入的信息,可以直接用EL表达式显示出来。
(6)部署运行。
将项目部署到Tomcat服务器上,启动Tomcat服务器,在浏览器中输入地址http:// localhost:8080/ssmBook_ch6/hello.htm,结果如图5-5所示。
图5-5?第一个Spring MVC的运行结果
5.3
Spring MVC的工作原理
与体系结构
5.3.1?Spring MVC程序运行原理
通过第一个Spring MVC程序来介绍Spring MVC的运行流程。
(1)用户通过浏览器发出请求。
(2)在web.xml中DispatchServlet拦截*.htm的请求。
(3)在与web.xml相同的路径下查找该Servlet对应的Spring配置文件,此案例中为springapp-servlet.xml。
(4)根据springapp-servlet.xml配置文件中的BeanName,找到对应的处理请求的类。在此案例中,用HelloController类来响应hello.htm请求。
(5)在HelloController类中的方法 handleRequest,它的作用类似于纯Servlet中的doGet或者doPost。注意它的返回值是一个Spring MVC中的对象ModelAndView,这个对象可以用来封装模型和视图。Hello.jsp是默认的jsp页面的名字。这里直接返回页面的名字是不可取的,本章后面将会介绍更合理的方式。
图5-6为Spring MVC的工作流程图。
图5-6?Spring MVC工作流程图
5.3.2?视图解析器
在代码清单5-1的HelloController类中,handleRequest方法返回的是一个jsp页面的名字。在实际案例中会改为如下的代码。
//代码清单5-2 HelloController.java
package com.ssmbook2020.web.ch5;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
public class HelloController implements Controller {
//返回ModelAndView对象
public ModelAndView handleRequest(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
//向request域中放入一条信息,给前端jsp用
request.setAttribute("message", "hello,Spring MVC");
//返回jsp的路径
return new ModelAndView("hello");//和代码清单5-1的唯一区别!
}
}
在代码 return new ModelAndView("hello")中返回的是“hello”字符串而不是“hello.jsp”的文件,为了让程序能正常工作,必须要在springapp-servlet.xml中加入hello的对应视图的解析方式,即hello的对应文件。Spring MVC通过配置文件给“hello”加上前缀和后缀来指定唯一的物理文件。以下是修改后的springapp-servlet.xml文件内容。
其中,prefix属性的值表示地址前缀,suffix属性的值表示地址后缀,如图5-7所示。
图5-7?增加视图解析器
这样hello就会对应到 /WebRoot/WEB-INF/jsp/hello.jsp文件,当然文件位置也要有对应变化,要把jsp文件放到WEB-INF文件夹下。这样有更好的安全性,因为用户无法直接访问Tomcat服务器下项目中WEB-INF下的文件,可以起到一定的保护作用。
5.3.3?Spring MVC的体系结构
下面详细介绍Spring MVC的体系结构,图5-8为Spring MVC体系结构图。
图5-8?Spring MVC 体系结构图
分析Spring MVC体系结构图,可以得到整个流程如下。
(1)?用户向服务器发送请求,Spring 前端控制Servlet,请求被DispatcherServlet捕获。
(2)DispatcherServlet对请求URL进行解析,得到请求资源标识符(URI);然后根据该URI调用HandlerMapping,获得该Handler配置的所有相关的对象(包括Handler对象和Handler对象对应的拦截器);最后以HandlerExecutionChain对象的形式返回。
(3)DispatcherServlet 根据获得的Handler,选择一个合适的HandlerAdapter。如果成功获得HandlerAdapter,此时将开始执行拦截器的preHandler(...)方法。
(4)??提取Request中的模型数据,填充Handler输入参数,开始执行Handler(Controller)。 在填充Handler输入参数的过程中,Spring将根据配置做一些额外的工作。
① HttpMessageConveter:将请求消息(如JSON、XML等数据)转换成一个对象,将对象转换为指定的响应信息。
② 数据转换:对请求消息进行数据转换,如String转换成Integer、Double等。
③ 数据根式化:对请求消息进行数据格式化,如将字符串转换成格式化数字或格式化日期等。
④ 数据验证:验证数据的有效性(长度、格式等),将验证结果存储到BindingResult或Error中。
(5)Handler执行完成后,向DispatcherServlet?返回一个ModelAndView对象。
(6)根据返回的ModelAndView,选择一个适合的ViewResolver(必须是已经注册到Spring容器中的ViewResolver)返回给DispatcherServlet?。
(7)ViewResolver 结合Model和View来渲染视图。
(8)将渲染结果返回给客户端。
5.4
基于注解的控制器配置
在5.2节已经学习了Spring MVC的基本概念,对Spring MVC有了基本的了解。配置控制器的传统方式是采用XML配置形式。随着现在基于注解的方式在Java EE项目中逐渐流行,Spring MVC 2.5版本后也支持基于注解的方式来配置控制器。
与5.2节基于xml配置的初始步骤相同,加入Spring MVC的包,并在项目的web.xml配置文件中加入DispatchServlet的配置。在与web.xml文件相同的位置,新建一个名为springapp-servlet.xml的xml文件。该springapp-servlet.xml配置文件中定义了用户请求的路径和对应的控制器的映射关系。
基于注解的控制器配置的实现步骤如下。
(1)修改控制器类
修改之前的HelloController类。为了和5.2节的HelloController区分,将这个类定义为HelloController2。代码如下所示。
//代码清单5-3 HelloController2.java
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
//控制器注解
@Controller
public class HelloController2{
//返回ModelAndView对象
@RequestMapping(value="/helloController2")
public ModelAndView handleRequest(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
//向Request域中放入一条信息,给前端jsp用
request.setAttribute("message", "hello,springmvc");
//返回jsp的路径
return new ModelAndView("hello");
}
}
这个类和5.2节的HelloController类有以下两点不同。
① 该类不需要继承其他类,只有@Controller注解。
② handleRequest方法前有一个@RequestMapping(value="/helloController2")注解。
(2)修改springapp-servlet.xml文件,添加如图5-9所示的内容。
图5-9 添加对注解的支持
在MyEclipse中,可以通过可视化的方式添加XML中的命名空间(XML Namespace),如图5-10所示。打开springapp-servlet.xml文件,在选项卡一栏中由Source模式切换到Namespaces模式,再勾选context和mvc选项,对应的springapp-servlet.xml文件中就会加上context和mvc命名空间。
图5-10 可视化添加命名空间
(3)部署运行。
将项目部署到Tomcat服务器,启动Tomcat服务器,在浏览器中输入地址http://localhost:
8080/ssmBook2020_ch5/helloController2.htm。如果配置正确,能看到和5.2节同样的hello.jsp的页面结果。
在本例中,@RequestMapping(value="/helloController2")中的value确定了在用户访问浏览器时该控制器的URL地址,注意还要加上.htm的后缀才能被Spring MVC的Servlet拦截。
5.5
Spring MVC注解详解
5.5.1?在类前注解
@RequestMapping注解除了可以标注在方法上,也可以标注在类上,放于@Controller之后,标注在类上的作用类似于父路径。例如:
@Controller
@RequestMapping("/user")
public class HelloController3{
//返回ModelAndView对象
@RequestMapping(value="/helloController3")
public ModelAndView handleRequest(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException{
//向Request域中放入一条信息,给前端jsp用
request.setAttribute("message", "hello,springmvc 3");
//返回jsp的路径
return new ModelAndView("hello");
}
}
此时,访问地址变为http://localhost:8080/ssmBook2020_ch5/user/helloController3.htm。user成了helloController3的上一级路径,并且无法跳过user来访问helloController3.htm。访问成功的效果如图5-11所示。
图5-11?@RequestMapping标注在类名前面
5.5.2?RequestMapping注解属性
RequestMapping注解有7个属性,从它的源代码中可以看到7个属性的方法名称。
public interface RequestMapping extends Annotation {
//指定映射的名称
public abstract String name();
//指定请求路径的地址
public abstract String[] value();
//指定请求的方式,是一个RequsetMethod数组,可以配置多个方法
public abstract RequestMethod[] method();
//指定参数的类型
public abstract String[] params();
//指定请求数据头信息
public abstract String[] headers();
//指定数据请求的格式
public abstract String[] consumes();
//指定返回的内容类型
public abstract String[] produces();
}
下面主要介绍使用最频繁的params和method两个属性。
1.??params属性
params属性表示用户传递的参数名。例如:
@Controller
@RequestMapping
public class HelloController4{
@RequestMapping(value="eg4/select.htm",params="id")
public String selectById(String id){
System.out.println("id:"+id);
return "hello";
}
}
在浏览器中输入地址http://localhost:8080/ssmBook2020_ch5/eg4/select.htm?id=8时,id=8的参数值如果采用纯Servlet方式,要使用request.getParameter()方法才能获得值。而Spring MVC可以将其自动注入方法中,在服务器可以得到id为8的值。这是非常有用的一个功能,可以将用户从一次又一次的Request读取多个请求参数的枯燥过程中解放出来。
2.??method属性
method属性则是指定了只响应指定的请求方式。下面定义一个方法指定method为get。
@RequestMapping(value="eg4/test.htm",method=RequestMethod.GET)
public String test(){
return "test";
}
在/WEB-INF/jsp/文件夹下新建一个文件test.jsp,其主要内容如下。
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
My JSP 'test.jsp' starting page
welocme test.jsp
然后在浏览器中输入地址http://localhost:8080/ssmBook2020_ch5/eg4/test.htm,可以得到如图5-12所示的结果页面。
图5-12?test.jsp结果页面
如果将其注解改为method=RequestMethod.POST,再次刷新浏览器,Web服务器将会报告405的错误码:Method Not Allowed,如图5-13所示。该提示不允许使用GET方式来访问该地址(直接在浏览器中输入地址默认是以GET方式访问的)。
图5-13 405错误
为了测试POST方式能否正常访问,除了使用form表单来设置提交请求的方式之外,还可以使用第三方工具来完成,其中最著名的工具软件是cURL。
5.5.3?cURL工具软件
1.??cURL简介
cURL(CommandLine Uniform Resource Locator)是利用URL语法在命令行方式下工作的开源文件传输工具。它被广泛应用在UNIX和多种Linux发行版中,并且有DOS和Windows 32、Windows 64下的移植版本,用于测试模拟向Web服务器发送和接收数据非常方便。
2.??cURL下载
cURL的下载地址为https://curl.haxx.se/download.html,下载后解压只有一个文件——curl.exe,将其放到D盘的根目录,然后在Windows的DOS命令窗口中运行它。
cURL的命令参数非常多,一般用于RESTful Web Services测试,涉及以下4种参数。
(1)-d/–data :POST数据内容。
(2)-X/–request :指定请求的方法(使用-d时自动设为POST)。
(3)-H/–header :设定header信息。
(4)-I/–head:只显示返回的HTTP头信息。
3. 发送请求
默认cURL使用GET方式请求数据,在这种方式下直接通过URL传递数据。
在命令行中输入访问地址,其语法格式如下所示。
curl http://localhost:8080/ssmBook2020_ch5/eg4/test.htm
以上默认是以GET方式发出的请求,在设置method=RequestMethod.GET的情况下可以正确得到返回结果,如图5-14所示。
@RequestMapping(value="eg4/test.htm",method=RequestMethod.GET)
public String test(){
return "test";
}
图5-14 cURL返回结果
如果使用POST请求地址,参数-d表示以POST请求发送数据,其命令如下。
curl -d 0 http://localhost:8080/ssmBook2020_ch5/eg4/test.htm
该命令中的0是指无意义地发送数据,以避免将URL当成数据而不是地址。服务器返回的是405的错误代码。如果将注解的参数改为method=RequestMethod.POST,再次进行POST请求测试,就可以得到正确的页面代码。
cURL工具对于将来在开发中测试Web服务是很方便的,可以大大提高效率。当然除了cURL,还有一些基于浏览器插件的工具软件,如基于Chrome浏览器的Simple REST Client和Postman-REST Client插件,读者可以根据需要自行下载使用。
5.6
本 章 小 结
本章介绍了如何利用Spring MVC框架开发Java Web程序。Spring MVC的核心组件是DispatcherServlet, 它在项目中响应用户所有的请求,对这些请求进行调度和分发。本章首先通过一个入门案例让大家对Spring MVC有了基本的了解,然后讲解了Spring MVC的体系结构和原理,最后介绍了使用注解来配置Spring MVC控制器的方法,通过注解可以使项目中的配置文件更加简洁。
第6章将使用Spring MVC重构网上书店案例。
习?题?5
1.??Spring MVC对比原生Servlet的优点有哪些?
2. 请描述Spring MVC框架响应用户请求的过程。
3.??ModelAndView对象的常见使用方法有哪些?
86
87