本片文章是我记录学习 Spring MVC 的学习笔记,作为初学者,对于这个框架的理解可能并不深刻,所以这篇文章主要讲述的是 Spring MVC 框架的使用,所以对于有些内容为什么要这么做,这么做有什么好处,由于才疏学浅,却不是我能解释的,所以本篇文章以代码偏多,文字解释偏少。
Hello Spring MVC
先简单的的把 Spring MVC 用起来,然后在解释一下 Spring MVC 的用法。首先在 pom.xml 中导入需要的包
<dependencies>  | 
spring-webmvc 就是我们需要的包,而 junit 不用说,是用来测试用的。Java 底层对浏览器做出响应是基于 Servlet,所以 Spring MVC 也是基于 Servlet 的,所以我们还导入了 Servlet 的包,以及对 JSP 的支持和 JSTL 语言的支持的包。
由于我们建立的只是一个普通的 Maven 工程,我们要对项目添加支持,使其成为一个 Web 项目,单击项目右键选择 Add Framework Support...,如下
接着勾选 WebApplication,点击 OK
这时会在你的项目中为你生成一个 web 目录,如下
这时我们配置 web.xml 如下
  | 
我们知道 JavaWeb 的工作流程是根据浏览器的请求地址去找对应的 Servlet 去做处理,这里我们配置对所有的请求(/)都会使用 Spring 提供的 DispatchServlet 进行处理。具体的处理流程稍后会介绍,从上面的配置看出,DispatchServlet 还需要一个配置文件地址的参数,这里我们写为了 classpath:springmvc-config.xml,而这个文件还没有,所以我们在 src/main/resources 中新建 springmvc-config.xml,内容如下
  | 
我们在这个配置文件中对 com.controller 中的类开启了 mvc 注解支持。还配置了一个 ServletHandler 和 ViewResolver,这两个东西与后面讲的 Spring MVC 执行流程有关,后面再说。
现在新建 com.controller.HandleRequest.java,内容如下
package com.controller;  | 
该类的作用是当浏览器访问 /hello 时,会返回浏览器一个字符串 "Hello Spring MVC",现在使用 tomcat 服务器启动该项目,并且在地址栏后输入 /hello,这时我们会得到一个错误如下
这是因为我们没有在项目中加入所依赖的包,这时我们在 IDEA 中的左上角找到 File 并点击,选择 Project Structure,接着在 Project Structure 中选择 Artifacts
这时选择你的项目,比如我的是 mvc-hello,右键选择 Put into Output Root
接下来重新启动 Tomcat,然后在浏览器地址栏后输入 /hello
就可以看到 Hello Spring MVC 在浏览器上显示了出来。
Spring MVC的执行流程
上图演示了 Spring MVC 执行的流程,在这里稍作解释
- 用户发送请求至前端控制器 
DispatcherServlet DispatcherServlet收到请求调用处理器映射器HandlerMapping- 处理器映射器根据请求 
url找到具体的处理器,生成处理器执行链HandlerExecutionChain(包括处理器对象和处理器拦截器)一并返回给DispatcherServlet。 DispatcherServlet根据处理器Handler获取处理器适配器HandlerAdapter,执行HandlerAdapter处理一系列的操作,如:参数封装,数据格式转换,数据验证等操作- 执行处理器 
Handler(Controller,也叫页面控制器) Handler执行完成返回ModelAndViewHandlerAdapter将Handler执行结果ModelAndView返回到DispatcherServletDispatcherServlet将ModelAndView传给ViewReslover视图解析器ViewReslover解析后返回具体ViewDispatcherServlet对View进行渲染视图(即将模型数据model填充至视图中)DispatcherServlet响应用户
现在稍加解释上面牵涉到的组件:
DispatcherServlet:前端控制器,用户请求到达前端控制器,它就相当于MVC模式中的C,DispatcherServlet是整个流程控制的中心,由它调用其它组件处理用户的请求,DispatcherServlet的存在降低了组件之间的耦合性,系统扩展性提高HandlerMapping:处理器映射器,HandlerMapping负责根据用户请求的url找到Handler即处理器,SpringMVC提供了不同的映射器实现不同的映射方式,根据一定的规则去查找,例如:xml配置方式,实现接口方式,注解方式等Handler:处理器,Handler是继DispatcherServlet前端控制器的后端控制器,在DispatcherServlet的控制下Handler对具体的用户请求进行处理。由于Handler涉及到具体的用户业务请求,所以一般情况需要程序员根据业务需求开发HandlerHandlAdapter:处理器适配器,通过HandlerAdapter对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行ModelAndView是SpringMVC的封装对象,将Model和View封装在一起。ViewResolver:视图解析器,ViewResolver负责将处理结果生成View视图,ViewResolver首先根据逻辑视图名解析成物理视图名即具体的页面地址,再生成View视图对象,最后对View进行渲染将处理结果通过页面展示给用户。View:是SpringMVC的封装对象,是一个接口,SpringMVC框架提供了很多的View视图类型,包括:jspview,pdfview,jstlView、freemarkerView、pdfView等。一般情况下需要通过页面标签或页面模版技术将模型数据通过页面展示给用户,需要由程序员根据业务需求开发具体的页面。
Spring MVC注解
@Controller
@Controller 注解表明了一个类是作为控制器的角色而存在的。Spring 不要求你去继承任何控制器基类,也不要求你去实现 Servlet 的那套 API。当然,如果你需要的话也可以去使用任何与 Servlet 相关的特性和设施。
@Controller 注解可以认为是被标注类的原型(stereotype),表明了这个类所承担的角色。分派器(DispatcherServlet)会扫描所有注解了 @Controller 的类,检测其中通过 @RequestMapping 注解配置的方法(详见下一小节)。
当然,你也可以不使用 @Controller 注解而显式地去定义被注解的 bean,这点通过标准的 Spring bean 的定义方式,在 dispather 的上下文属性下配置即可做到。但是 @Controller 原型是可以被框架自动检测的,Spring 支持 classpath 路径下组件类的自动检测,以及对已定义 bean 的自动注册。
@RestController
这里说的是与 @Controller,被 @Controller 注解的类,如果在方法中方法字符串,则会被视图解析器解析,即如下
package com.controller;  | 
当访问 /hello 时,会执行 sayHello 方法,这时返回的 hello 会被视图解析器解析,视图解析器会根据在 springmvc-config.xml 中的配置进行拼接
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">  | 
所以上面方法的 hello 会被拼接成 WEB-INF/jsp/hello.jsp,接着会返回该 jsp 页面。
而使用 @RestController 注解,则不会经过视图解析器,而是会将该结果直接返回,就像在前一节演示的例子一样。因为最近流行前后端分离,所以后端不需要写页面了,只需要将前端请求的数据返回,而前端负责渲染展示数据,所以 @RestController 类用的还是比较多的。
@RequestMapping
你可以使用 @RequestMapping 注解来将请求 URL,如 /appointments 等,映射到整个类上或某个特定的处理器方法上。一般来说,类级别的注解负责将一个特定(或符合某种模式)的请求路径映射到一个控制器上,同时通过方法级别的注解来细化映射,即根据特定的 HTTP 请求方法("GET" "POST"方法等)、HTTP 请求中是否携带特定参数等条件,将请求映射到匹配的方法上。
现在来讲一下 @RequestMapping 中的属性,@RequestMapping 的源码如下
public RequestMapping {  | 
name, value, path:是用来设置匹配的url路径的method:指定请求的method类型,GET、POST、PUT、DELETE等consumes:指定处理请求的提交内容类型(Content-Type),例如application/json, text/htmlproduces:指定返回的内容类型,仅当request请求头中的(Accept)类型中包含该指定类型才返回params:指定request中必须包含某些参数值时,才让该方法处理headers:指定request中必须包含某些指定的header值,才能让该方法处理请求
给个例子:
除了可以使用 method 属性指定请求办法外,还可以使用注解:
@GetMapping("/hello")@PostMapping("/hello")......
这些注解与 RequestMapping 具有相同的属性,除了 method 属性。
@RequestParam
该注解类型用于将指定的请求参数赋值给方法中的形参,那么 @RequestParam 注解有什么属性呢? 它有 4 种属性,下面将逐一介绍这四种属性:
name属性:该属性的类型是String类型,它可以指定请求头绑定的名称value属性:该属性的类型是String类型,它可以设置是name属性的别名required属性:该属性的类型是boolean类型,它可以设置指定参数是否必须绑定defalutValue属性:该属性的类型是String类型,它可以设置如果没有传递参数可以使用默认值
  | 
@PathVaribale
@PathVaribale 注解,该注解类型可以非常方便的获得请求 url 中的动态参数。@PathVaribale 注解只支持一个属性 value,类型 String,表示绑定的名称,如果省略则默认绑定同名参数
  | 
@CookieValue
绑定 cookie 的值到 Controller 方法参数
( "/hello" )  | 
@RequestHeader
@RequestHeader 注解,该注解类型用于将请求的头的信息区域数据映射到功能处理方法的参数上。那么@RequestHeader 注解有什么属性呢? 它和 @RequestParam 注解一样,也有 4 种属性,分别如下
name属性:该属性的类型是String类型,它可以指定请求头绑定的名称value属性:该属性的类型是String类型,它可以设置是name属性的别名required属性:该属性的类型是boolean类型,它可以设置指定参数是否必须绑定defalutValue属性:该属性的类型是String类型,它可以设置如果没有传递参数可以使用默认值
  | 
@RequestBody
@RequestBody 注解是将 HTTP 请求正文插入方法中
  | 
@RequestBody 注解常用来处理 Content-type 不是默认的 application/x-www-form-urlcoded 编码的内容,比如说:application/json 或者是 application/xml 等。一般情况下来说常用其来处理 application/json 类型。
对于前端使用而言,form 表单的 enctype 属性为编码方式,常用有两种:application/x-www-form-urlencoded 和 multipart/form-data,默认为 application/x-www-form-urlencoded,所以在前端传输数据时,需要将 Content-type 显示指定为 application/json。
总结:
ReuqestBody主要是处理json串格式的请求参数,要求使用方指定header Content-type:application/jsonRequestBody通常要求调用方使用post请求
@ResponseBody
@ResponseBody 注解的作用是将 Controller 的方法返回的对象通过适当的转换器转换为指定的格式之后,写入到 response 对象的 body 区,通常用来返回 JSON 数据或者是 XML 数据,需要注意的呢,在使用此注解之后不会再走视图处理器,而是直接将数据写入到输入流中,它的效果等同于通过 response 对象输出指定格式的数据。
  | 










