3.2.3 请求处理方法可返回的类型

每个请求处理方法可以返回以下类型的返回结果:

java提供的类型

  • void
  • java.util.Map<K,V>
  • java.lang.String
  • java.util.concurrent.Callable

Spring提供的类型

  • org.springframework.web.portlet.ModelAndView
  • org.springframework.web.servlet.View
  • org.springframework.web.context.request.async.DeferredResult
  • org.springframework.ui.Model

其他类型

  • HttpEntity
  • ResponseEntity

3.2.2 请求处理方法中可出现的参数类型

每个请求处理方法可以有多个不同类型的参数。
如果需要访问HttpServletRequest对象,则可以添加HttpServletRequest作为参数, Spring会将对象正确地传递给方法:

1
2
3
4
@RequestMapping(value="/login")
public String login(httpServletRequest request){
return "login";
}

如果需要访问HttpSession对象,则可以添加HttpSession作为参数, Spring会将对象正确地传递给方法:

1
2
3
4
@RequestMapping(value="/login")
public String login(HttpSession session){
return "login";
}

可以在请求处理方法中出现的参数类型

下面是可以在请求处理方法中出现的参数类型:

java类型

java.io包

  • java.io.InputStreamjava.io.Reader- java.io.OutputStreamjava.io.Writer

java.util包

  • java.util.Locale
  • java.util.Map

java.security包

  • java.security.PrincipalHttpEntity<?>

javax.servlet包

  • javax.servlet.ServletRequestjavax.servlet.http.HttpServletRequest
  • javax.servlet.ServletResponsejavax.servlet.http.HttpServletResponse
  • javax.servlet.http.HttpSession

Spring类型

validation包

  • org.springframework.validation.Errors,
  • org.springframework.validation.BindingResult

web包

  • org.springframework.web.context.request.WebRequestorg.springframework.web.context.request.NativeWebRequest
  • org.springframework.web.bind.support.SessionStatus
  • org.springframework.web.util.UriComponentsBuilder
  • org.springframework.web.servlet.mvc.support.RedirectAttributes

ui包

  • org.springframework.ui.Model
  • org.springframework.ui.ModelMap

validation包

  • org.springframework.validation.Errors
  • org.springframework.validation.BindingResult

重要类型

WebRequest类

这里重点说明一下org.springframework.web.context.request.WebRequest这个对象。 WebRequestSpring MVC提供的统一请求访问接口,

  1. WebRequest可以访问请求相关数据,如参数数据、
  2. WebRequest可以访问请求头数据,
  3. WebRequest 可以访问请求作用域的数据
  4. WebRequest可以访问会话作用域中的数据

但是WebRequest无法访问Cookie区数据,

NativeWebRequest继承了WebRequest,并提供访问本地Servlet API的方法。

WebRequest重点方法

WebRequest中有如下这些重点方法:

方法 描述
getParameter(String name) 根据请求参数名获取请求参数值。等同于Servlet APIrequest.getParameter(String name)
getHeader(String name) 根据请求头参数名获取请求头参数值,等同于ServletAPIrequest.getHeader(String name)
setAttribute(String name,Object value,int scope) 保存数据到指定的作用域
getAttribute(String name,int scope) 从指定的作用域中提取数据
setAttribute方法

setAttribute方法等同于Servlet APIscope(request,session).setAttribute(String name,Object value)方法。参数中的int scopeWebRequest对象中定义的两个常量SCOPE_REQUEST(请求作用域)、SCOPE_SESSION(会话作用域)

1
2
3
4
// 保存名称为username的值到request作用域
webRequest.setAttribute("username","张三",WebRequest.SCOPE_REQUEST);
// 保存名称为bookname的值到session作用域
webRequest.setAttribute("bookname","高等数学",WebRequest.SCOPE_SESSION);
getAttribute方法

getAttribute方法等同于Servlet APIscope(request,session).getAttribute(String name)方法。

1
2
3
4
// 从request作用域中取出名称为username的值
webRequest.getAttribute("username",WebRequest.SCOPE_REQUEST);
// 从session作用域中取出名称为bookname的值
webRequest.getAttribute("bookname",WebRequest.SCOPE_SESSION);

从框架的无侵入性来说,建议使用WebRequest对象替代Servlet APIHttpServletRequest对象和HttpSession对象。

Model类型

org.springframework.ui.Model是其中最重要的类型。这不是一个Servlet API类型,而是一个Spring MVC类型,其中包含了Map对象用来存储数据。
如果方法中添加了Model参数,则每次调用请求处理方法时, Spring MVC都会创建Model对象,并将其作为参数传递给方法。

可以出现在请求处理方法参数列表中的注解

  • @PathVariable注解、
  • @MatrixVariable注解、
  • @RequestParam注解、
  • @RequestHeader注解、
  • @RequestBody注解、
  • @RequestPart注解

3.2 @RequestMapping注解

3.2.1 @RequestMapping注解简介

RequestMapping注解指定处理请求的类或方法

开发者需要在控制器内部为每一个请求动作开发相应的处理方法。org.springframework.web.bind.annotation.RequestMapping注解指示Spring用哪一个类或方法来处理请求动作,该注解可用于类或方法。
提示:@RequestMapping注解虽然也包含在org.springframework.web.bind.annotation里面,但是严格来说,它并不属于参数绑定注解

RequestMapping注解放在类前面

@RequestMapping注解可以用来注释一个控制器类,在这种情况下,所有方法都将映射为相对于类级别的请求,表示该控制器处理的所有请求都被映射到value属性所指示的路径下。

程序 RequestMapping注解示例

示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Controller
@RequestMapping(value="/user")
public class UserController
{
@RequestMapping(value="/register")
public String register()
{
return "register";
}
@RequestMapping(value="/login")
public String login(){
return "login";
}
}

由于UserController类中添加了value="/user"@RequestMapping注解,因此要在所有相关路径前面加上/user",此时方法被映射到如下请求URL:

1
2
http://localhost:8080/user/register
http://localhost:8080/user/login

@RequestMapping注解的属性

使用@RequestMapping注解可指定下标所示的属性。

属性 类型 是否必要 说明
value String数组 用于将指定请求的实际地址映射到方法上
name Sting 给映射地址指定一个别名
method RequestMethod数组 映射指定请求的方法类型,包括
  • RequestMethod.GET
  • RequestMethod.POST
  • RequestMethod.HEAD
  • RequestMethod.OPTIONS
  • RequestMethod.PUT
  • RequestMethod.PATCH
  • RequestMethod.DELETE
  • RequestMethod.TRACE
几种类型
consumes String数组 指定处理请求的提交内容类型(Content-Type),例如:
  • application/json
  • text/html
parms String数组 指定request中必须包含某些参数值时,才让该方法处理
headers String数组 指定 request中必须包含某些指定的 header值时,才能让该方法处理请求
path String数组 Servlet环境中只有:URI路径映射(例如“/myPath.do”)。也支持如ant的基于路径模式(例如“/myPath/*”)。在方法层面上,支持相对路径(例如“ edit. do”)

@RequestMapping注解支持的常用属性示例如下。

1. value属性 地址映射

@RequestMapping是一个用来处理请求地址映射的注解,可以使用@RequestMapping注释一个方法或类,一个采用@RequestMapping注释的方法将成为一个请求处理方法,例如:

1
2
3
4
@RequestMapping(value="/hello")
public ModelAndview hello(){
return …;
}

该示例使用@RequestMapping注解的value属性将URL映射到方法上。在这个例子中,将hello映射到hello方法上,使用如下URL访问应用时将由hello方法进行处理。

1
http://localhost:8080/context/hello

只有value属性时属性名value可以省略

由于value属性是@RequestMapping注解的默认属性,因此,如果只有唯一的value属性时,则可以省略``value`属性名,即如下两个注解含义相同。

1
2
@RequestMapping(value="/hello")
@RequestMapping("/hello")

有多个属性时不可省略value属性名

但如果有超过一个属性,就必须写上value属性名称。

value属性值可以是空字符串

value属性的值也可以是一个空字符串,如下所示:

1
@RequestMapping(value="")

此时该方法被映射到如下请求URL

1
http://localhost:8080/context

2. method属性 HTTP请求方式

该属性用来指示该方法仅处理哪些**HTTP请求方式**。

只支持单个HTTP请求方式

1
@RequestMapping(value="/hello", method=RequestMethod.POST)

以上代码method=RequestMethod.POST表示该方法只支持POST请求。

只支持多个HTTP请求方式

也可以同时支持多个HTTP请求方式。如:

1
2
3
4
@RequestMapping(
value="/hello",
method={RequestMethod.POST,RequestMethod.GET}
)

支持任意HTTP请求方式

如果没有指定method属性值,则请求处理方法可以处理任意的HTTP请求方式。

Spring4.3 新增的HTTP请求方式注解

提示:Spring4.3之后,新增了@GetMapping@PostMapping@PutMapping@DeleteMapping@PatchMapping注解,这几个注解可以指定的属性和@RequestMapping注解类似,区别在于@GetMapping注解只支持GET方式的请求;@PostMapping注解只支持POST方式的请求;@PutMapping@DeleteMapping@PatchMapping分别对应PUT请求、DELETE请求和PATCH请求,使用比较少。

3. consumes属性 请求提交的内容类型

该属性指定处理请求的提交内容类型(Content-Type)。

1
2
3
4
5
@RequestMapping(
value="/hello",
method=RequestMethod.POST,
consumes="application/json"
)

表示方法仅处理requestContent-Type为“application/json”类型的请求。

4. produces属性 响应返回内容类型

该属性指定返回的内容类型,返回的内容类型必须是request请求头(Accept)中所包含的类型

1
2
3
4
5
6
RequestMapping(
value="/hello",
method=RequestMethod.POST,
consumes="application/json",
produces="application/json"
)

方法仅处理request请求中Accept头中包含了"application/json"POST请求,同时指明了返回的内容类型为application/json

5. params属性 请求必须带有的参数值

params属性指定``request`中必须包含某些参数值时,才让该方法处理。例如如下注解的

1
2
3
4
5
@RequestMapping(
value="/hello",
method=RequestMethod.POST,
params="myParam=myValue"
)

方法仅处理请求参数中有名为"myParam"并且值为"myValue"的请求。

6. headers属性 请求必须带有的header值

headers属性指定request`中必须包含某些指定的header`值时,才能让该方法处理请求

1
2
3
4
5
@RequestMapping(
value="/hello",
method=RequestMethod.POST,
headers="Referer=http://www.fkit.org/"
)

方法仅处理header中包含了指定Referer请求头和对应值为http://www.fkit.org/的请求。

3.1 @Controller注解

org.springframework.stereotype.Controller注解用于指示Spring类的实例是一个控制器。

@Controller注解优点

使用@Controller注解的类不需要继承特定的父类或者实现特定的接口,相对之前的版本实现Controller接口变得更加简单。
而且**Controller接口的实现类只能处理一个单一请求动作,而@Controller注解的控制器可以支持同时处理多个请求动作,更加灵活**。

@Controller的作用

@Controller用于标记一个类,使用它标记的类就是一个Spring MVC Controller对象,即一个控制器类。

组件扫描

Spring使用扫描机制査找应用程序中所有基于注解的控制器类。**分发处理器会扫描使用了该注解的类的方法,并检测该方法是否使用了@RequestMapping注解**,而使用@RequestMapping注解的方法才是真正处理请求的处理器
为了保证Spring能找到控制器,需要完成如下这两件事情:

  1. Spring MVC的配置文件的头文件中引入spring-context
  2. 使用<context:component-scan/>元素
    • 该元素的功能为:启动包扫描功能,以便注册带有@Controller@Service@Repository@Component等注解的类成为SpringBean
    • <context:component-scan/>元素的base-package属性指定了需要扫描的类包,类包及其递归子包中所有的类都会被处理。
      配置文件中的示例如下所示:
      1
      <context:component-scan base-package="org.fkit.controller" />

应该将所有控制器类都放在基本包下,并且指定扫描该包,即org.fkit.controller,而不应该指定扫描org.fkit包,以免Spring MVC扫描了无关的包.

示例: @Controller注解的使用

项目结构

展开/折叠
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
G:\Desktop\随书源码\Spring+Mybatis企业应用实战(第2版)\codes\03\ControllerTest
├─src\
│ └─org\
│ └─fkit\
│ └─controller\
│ └─HelloWorldController.java
└─WebContent\
├─META-INF\
│ └─MANIFEST.MF
└─WEB-INF\
├─content\
│ └─helloWorld.jsp
├─lib\
│ ├─commons-logging-1.2.jar
│ ├─spring-aop-5.0.1.RELEASE.jar
│ ├─spring-aspects-5.0.1.RELEASE.jar
│ ├─spring-beans-5.0.1.RELEASE.jar
│ ├─spring-context-5.0.1.RELEASE.jar
│ ├─spring-context-indexer-5.0.1.RELEASE.jar
│ ├─spring-context-support-5.0.1.RELEASE.jar
│ ├─spring-core-5.0.1.RELEASE.jar
│ ├─spring-expression-5.0.1.RELEASE.jar
│ ├─spring-instrument-5.0.1.RELEASE.jar
│ ├─spring-jcl-5.0.1.RELEASE.jar
│ ├─spring-jdbc-5.0.1.RELEASE.jar
│ ├─spring-jms-5.0.1.RELEASE.jar
│ ├─spring-messaging-5.0.1.RELEASE.jar
│ ├─spring-orm-5.0.1.RELEASE.jar
│ ├─spring-oxm-5.0.1.RELEASE.jar
│ ├─spring-test-5.0.1.RELEASE.jar
│ ├─spring-tx-5.0.1.RELEASE.jar
│ ├─spring-web-5.0.1.RELEASE.jar
│ ├─spring-webflux-5.0.1.RELEASE.jar
│ ├─spring-webmvc-5.0.1.RELEASE.jar
│ └─spring-websocket-5.0.1.RELEASE.jar
├─springmvc-config.xml
└─web.xml

新建一个项目ControllerTest,加入所需的jar文件,示例代码如下

HelloWorldController

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package org.fkit.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

/**
* HelloWorldController是一个基于注解的控制器, 可以同时处理多个请求动作,并且无须实现任何接口。
* org.springframework.stereotype.Controller注解用于指示该类是一个控制器
*/
@Controller
public class HelloWorldController
{
@RequestMapping("/helloWorld")
public String helloWorld(Model model)
{
// 向模型之中添加一个属性
model.addAttribute("message", "Hello World!");
return "helloWorld";
}

}

HelloWorldController是一个基于@Controller注解的控制器,helloWorld方法上面的@RequestMapping注解用来映射一个请求,@RequestMapping注解的参数:"/helloWorld"表示/helloWorld这个请求由helloWorld方法进行处理。 helloWorld方法接收一个org.springframework.ui.Model类型的参数,本例在model中添加了一个名为message的字符串对象,该对象可以在返回的视图当中通过request对象获取。最后,方法返回一个字符串helloWorld作为视图名称.

springmvc-config.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?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:p="http://www.springframework.org/schema/p"
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/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">

<!-- spring可以自动去扫描base-pack下面的包或者子包下面的java文件 -->
<!-- 如果扫描到有Spring的相关注解的类,则把这些类注册为Spring的bean -->
<context:component-scan
base-package="org.fkit.controller" />
<!-- 默认装配方案 -->
<mvc:annotation-driven />
<!-- 静态资源处理 -->
<mvc:default-servlet-handler />

<!-- 视图解析器 p:prefix属性表示前缀 p:suffix 表示后缀 -->
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver"
p:prefix="/WEB-INF/content/" p:suffix=".jsp" />

</beans>

springmvc-config.xml文件配置信息解释如下:

映射URL和控制器

  • 由于使用了@Controller注解,因此不需要再在配置文件中使用XML如下描述Bean
    1
    2
    <!-- 配置Handle,映射"/hello"请求 -->
    <bean name="/hello" class="org.fkit.controller.HelloController"/>
  • <context:component-scan base-package="org.fkit.controller" />指定需要Spring扫描org.fkit.controller包及其子包下面的所有java文件。

使用默认装配方案

  • <mvc: annotation-driven />是一种简写形式,可以让初学者快速应用默认配置方案<mvc:annotation-driven/>会自动注册RequestMappingHandlerMappingRequestMappingHandlerAdapter两个Bean,这是Spring MVC@Controllers分发请求所必需的,并且还提供了:
    • 数据绑定支持、
    • @NumberFormatAnnotation支持、
    • @DateTimeFormat支持、
    • @Valid支持、
    • 读写XML的支持(JAXB)
    • 和读写JSON的支持(默认为Jackson)等功能。本例处理Ajax请求时,就使用到了对JSON的支持功能。

静态资源处理器

<mvc: default-servlet-handler/>Spring MVC静态资源处理器,在web.xml中,如果将DispatcherServlet请求映射配置为"/",则Spring MVC将捕获Web容器所有的请求,包括静态资源的请求,而引入类似<script src="js/jquery-1.1.0.min.js"/>这种静态资源文件的时候,DispatcherServlet会将"/"看成请求路径,找不到它的时候会导致提示404错误。
当在springmvc-config.xml中配置<mvc:default-servlet-handler />后,会在Spring MVC上下文中定义一个org.springframework.webservlet.resource.DefaultServletHttpRequestHandler,它就像一个检查员,对进入DispatcherServletURL进行筛查:

  • 如果发现是静态资源的请求,就将该请求转由web应用服务器默认的Servlet处理;
  • 如果不是静态资源的请求,才由DispatcherServlet继续处理。

视图解析器

最后配置了视图解析器InternalResourceViewResolver来解析视图,将View呈现给用户。视图解析器中配置的prefix属性表示视图的前缀,suffix表示视图的后缀,
例如返回的视图字符串是"helloWorld",经过视图解析器解析之后,视图的完整路径为前缀helloWorld后缀,也就是:/WEB-INF/content/helloWorld.jsp

使用默认的处理器映射器和处理器适配器

需要注意的是,此处没有配置处理器映射器处理器适配器,当用户没有配置这两项时, Spring会使用默认的处理器映射器和处理器适配器处理请求

web.xml

此外,还需要在web.xml文件中配置Spring MVC的前端控制器DispatcherServlet,因为每次配置基本一致此处不再赘述,读者可自行配置。

测试

部署ControllerTest这个Web应用,在浏览器中输入如下URL来测试应用:

1
http://localhost:8080/ControllerTest/helloWorld

之后,会看到浏览器中显示HelloWorld!,这表示Spring MVC访问成功。

用于参数绑定的注解

Spring MVC用于参数绑定的注解有很多,都在org.springframework.web.bind.annotation包中,根据它们处理的request的不同内容部分可以分为六类。

处理 请求参数和内容 部分的注解

@RequestMapp@RequestParam@GetMapping@PostMapping@PutMapping@DeleteMapping@PatchMapping@Requestbody.、@ResponseBody@RequestPart@RestController

处理 请求URL 部分的注解

@PathVariable@MatrixVariable@CrossOrigin

处理 请求头 部分的注解

@RequestHeader, @CookieValue

处理 属性类型 的注解

RequestAttribute@SessionAttribute@SessionAttributes@ModelAttribute

处理 异常类型 的注解

ExceptionHandler@ControllerAdvice@RestControllerAdvice@ResponseStatus

绑定 表单数据 的注解

@InitBinder

接下来重点讲解常用的Spring MVC注解。@InitBinder注解用于解决类型转换问题,在第6章中讲解。@RequestPart注解用于绑定multipart/form-data"参数,常用于文件上传,在第7章中讲解.

第3章 Spring MVC的常用注解

本章要点

  • @Controller注解
  • @RequestMapping注解
  • @GetMapping注解
  • @PostMapping注解
  • @RequestParam注解
  • @PathVariable注解
  • @MatrixVariable注解
  • @CrossOrigin注解
  • @RequestHeader注解
  • @CookieValue注解
  • @RequestAttribute注解
  • @SessionAttribute注解
  • @SessionAttributes注解
  • @ModelAttribute注解
  • @RequestBody注解
  • @ResponseBody注解
  • @RestController注解
  • @ResponseStatus注解
  • @ExceptionHandle注解
  • @ControllerAdvice注解
  • @RestControllerAdvice注解

Spring从2.5版开始引入注解,用户可以在Spring MVC中使用@Controller@RequestMapping@RequestParam@ModelAttribute等类似的注解。到目前为止, Spring的版本虽然发生了很大的变化,但注解的特性却一直延续下来,并不断扩展,让广大开发者的工作变得更轻松。这都离不开注解的强大作用,本章将重点讲解Spring MVC中常用的注解

2.6 本章小结

本章介绍了Spring MVC的入门知识,包括如何使用Spring MVC开发一个简单的Web应用。
Spring MVC中,开发者无须编写自己的前端控制器,使用Spring提供的DispatcherServlet就可以分派请求。
Spring MVC传统风格的控制器开发方式是实现Controller接口,从Spring2.5开始,提供了一个更好的控制器开发方式,即采用注解类型。
最后,详细分析了Spring MVC请求→响应的完整工作流程。
第3章将重点介绍Spring MVC常用的注解。

2.5 Spring MVC执行的流程

下面将对开发Spring MVC应用的过程进行总结,以让读者对Spring MVC有一个大致的了解。

2.5.1 Spring MVC应用的开发步骤

下面简单介绍 Spring MVC应用的开发步骤。

1 web.xml中定义前端控制器

web.xml文件中定义前端控制器DispatcherServlet来拦截用户请求。
由于Web应用是基于请求/响应架构的应用,所以不管哪个MVC Web框架,都需要在web.xml中配置该框架的核心ServletFilter,这样才可以让该框架介入Web应用。
例如,开发Spring MVC应用的第1步就是在web.xml文件中增加如下配置片段:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!-- 定义前端控制器 -->
<servlet>
<servlet-name>springDispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<!-- springmvc-config.xml文件的路径 -->
<param-value>WEB-INF/springmvc-config.xml</param-value>
</init-param>
<!-- 项目启动时加载前端控制器 -->
<load-on-startup>1</load-on-startup>
</servlet>
<!-- 让spring mvc的前端控制器拦截的请求的模式 -->
<servlet-mapping>
<servlet-name>springDispatcherServlet</servlet-name>
<!-- 拦截所有的请求 -->
<url-pattern>/</url-pattern>
</servlet-mapping>

2 定义包含表单数据的JSP页面

如果需要以POST方式提交请求,则定义包含表单数据的JSP页面。如果仅仅只是以GET方式发送请求,则无须经过这一步。

3 定义处理用户请求的Handle类

定义处理用户请求的Handle类,该类可以实现Controller接口或使用@Controller注解来实现。这一步也是所有MVC框架中必不可少的,因为这个DispatcherServlet就是MVC中的C,也就是前端控制器,该控制器负责接收请求,并将请求分发给对应的Handle类,该Handle类负责调用后台业务逻辑代码来处理请求。

创建Controller实例 调用其方法来处理请求

可能有读者会产生疑问: Controller并未接收到用户请求,它怎么能够处理用户的请求呢?
MVC框架的底层机制是:前端控制器DispatcherServlet接收到用户请求后,通常会对用户请求进行简单预处理,例如解析、封装参数等,然后通过反射来创建Controller实例,并调用Controller的指定方法来处理用户请求:

  • 如果是实现Controller接口的Controller,则调用的是handlRequest方法,
  • 如果是使用基于注解的控制器,则可以调用任意方法

如何知道创建哪个Controller接口的实例

这里又产生了一个问题:当Servlet拦截用户请求后,它如何知道创建哪个Controller接口的实例呢?
有两种解决方案:

  1. 利用xml配置文件:例如在xml配置文件中描述hello请求对应使用HelloController类。这就可以让MVC框架知道当接收到hello请求时要创建哪个Controller接口的实例。
  2. 利用注解:例如使用注解@Controller描述一个类,并使用注解@RequestMapping(value="/hello")描述hello请求对应的方法。这样也可以让MVC框架知道要创建哪个Controller接口的实例并调用哪个方法处理请求。

控制器分类

根据上面的介绍不难发现,在Spring MVC框架中,控制器实际上由两个部分组成:

  1. 即拦截所有用户请求和处理请求的通用代码都由前端控制器DispatcherServlet完成,
  2. 而实际的业务控制(调用后台业务逻辑代码,返回处理结果等)则由Controller处理。

4 配置Handle

使用xml配置Handle

Java领域的绝大部分MVC框架都非常喜欢使用xml文件来进行配置管理,这在以前是一种思维定势。即配置哪个请求对应哪个Controller,从而让前端控制器根据该配置来创建合适的Controller实例,并调用该Controller的业务控制方法。
例如,可以采用如下代码片段来配置Handle:

1
2
<!-- 配置Handle,映射"/hello"请求 -->
<bean name="/hello" class="org.fkit.controller.HelloController"/>

使用注解来配置Handle

Spring2.5之后,推荐使用注解来配置Handle:

1
2
3
4
5
6
7
8
9
10
package org.fkit.controller;
......
@Controller
public class HelloController
{
......
@RequestMapping(value = "/hello")
public ModelAndView hello(){......}
......
}

上面的配置片段指定如果用户请求URLhello,则使用org.fkit.controller.Hellocontroller来处理。现在几乎所有的MVC框架都使用”约定优于配置”的思想,也就是采用约定方式来规定用户请求地址和Handle之间的对应关系。

5 编写视图资源

Handle处理用户请求结束后,通常会返回一个ModelAndView对象,该对象中应该包含返回的视图名或视图名模型,这个视图名就代表需要显示的物理视图资源。如果Handle需要把一些数据传给视图资源,则可以通过模型对象实现。

经过上面5个步骤,即可基本完成一个Spring MVC处理流程的开发,也就是可以执行一次完整的请求→响应过程。

2.5.2 Spring MVC执行的流程

上一节所介绍的Spring MVC应用的开发流程实际上是按请求→响应的流程来开发的,下面通过一个流程图来介绍请求→响应的完整流程。图2.5显示了一次请求→响应的完整流程。
这里有一张图片

Spring MVC 请求响应工作流程

按安照图2.5中所标识的序号, Spring MVC请求→响应的完整工作流程如下

  1. 用户向服务器发送请求,请求被Spring的前端控制器DispatcherServlet截获。
  2. DispatcherServlet对请求URL(统一资源定位符)进行解析,得到URI(请求资源标识符)。然后根据该URI,调用HandlerMapping获得该Handler配置的所有相关的对象,包括Handler对象以及Handler对象对应的拦截器,这些对象会被封装到一个HandlerExecutionChain对象当中返回。
  3. DispatcherServlet根据获得的Handler,选择一个合适的HandlerAdapterHandlerAdapter会被用于处理多种Handler,调用Handler实际处理请求的方法,例如hello方法。
  4. 提取请求中的模型数据,开始执行Handler(也就是Controller)。在填充Handler的入参过程中,根据配置Spring将帮你做一些额外的工作:
  • 消息转换。将请求消息(如JSONXML等数据)转换成一个对象,将对象转换为指定的响应信息。
  • 数据转换。对请求消息进行数据转换,如String转换成IntegerDouble等。
  • 数据格式化。对请求消息进行数据格式化,如将字符串转换成格式化数字或格式化日期等。
  • 数据验证。验证数据的有效性(长度、格式等),验证结果存储到BindingResultError中。
  1. handler执行完成后,向DispatcherServlet返回一个ModelAndView对象, ModelAndView对象中应该包含视图名视图名模型.
  2. 根据返回的ModelAndView对象,选择一个合适的ViewResolver(视图解析器)返回给DispatcherServlet
  3. ViewResolver结合ModelView来渲染视图。
  4. 将视图渲染结果返回给客户端。

只需要开发Handler即可

以上8个步骤, DispatcherServletHandlerMappingHandlerAdapterViewResolver等对象协同工作,来完成Spring MVC请求→响应的整个工作流程,这些对象所完成的工作对于开发者来说都是不可见的,开发者并不需要关心这些对象是如何工作的,开发者只需要在Handler(也就是Controller)当中完成对请求的业务处理
提示关于DispatcherServletHandlerMappingHandlerAdapterViewResolver等对象协同工作的知识,对于还不了解Spring MVC的读者来说,可能会感觉很难理解,因为这一节的知识涉及Spring MVC的源代码和Spring MVC的设计思想,建议初学者学习完Spring MVC的知识之后再回过头来看本节的内容。

2.4 详解DispatcherServlet

2.3节中的第一个Spring MVC应用已经成功运行了。那么,前端控制器DispatcherServlet截获请求后做了什么工作呢? DispatcherServlet又是如何分派请求的呢?

initStrategies方法

分析DispatcherServlet的源代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context); //初始化上传文件解析器
initLocaleResolver(context); //初始化本地化解析器
initThemeResolver(context); //初始化主题解析器
initHandlerMappings(context); //初始化处理器映射器,将请求映射到处理器
initHandlerAdapters(context); //初始化处理器适配器
// 初始化处理器异常解析器,如果执行过程中遇到异常将交给`HandlerExceptionResolver`来解析
initHandlerExceptionResolvers(context);
// 初始化请求到视图名称解析器
initRequestToViewNameTranslator(context);
// 初始化视图解析器,通过`ViewResolver`解析逻辑视图名到具体视图实现
initViewResolvers(context);
// 初始化`flash`映射管理器
initFlashMapManager(context);
}

如何下载Spring源码

提示:org/Springframework/web/Servlet/DispatcherServletSpring框架的源代码,读者可在配套资源中找到Spring源代码。

GitHub下载Spring源码

  • GitHub上上下载你想要的版本的zip文件,
  • 解压.
  • 依次进入路径:
    1
    spring-webmvc\src\main\java\org\springframework\web\servlet\
    即可找到DispatcherServlet的源码
    例如,我电脑上的DispatcherServlet路径为:
    1
    G:\Desktop\随书源码\库文件2\spring-framework-5.2.1.RELEASE\spring-webmvc\src\main\java\org\springframework\web\servlet\DispatcherServlet.java
    initstrategies方法将在WebApplicationContext初始化后自动执行,自动扫描上下文的Bean,根据名称或类型匹配的机制査找自定义的组件,如果没有找到则会装配套Spring的默认组件。

    自定义组件

    如果开发者希望使用自定义类型的组件,则只需要在Spring配置文件中配置自定义的Bean组件即可。 Spring MVC如果发现上下文中有用户自定义的组件,就不会使用默认的组件。

    DispatcherServlet装配每种组件的细节

    以下是DispatcherServlet装配每种组件的细节。

    本地化解析器

本地化解析器。只允许一个实例

  • 查找名为localeResolver、类型为.LocaleResolverBean作为该类型组件。
  • 如果没有找到,则使用默认的实现类AcceptHeaderLocaleResolver作为该类型组件。

主题解析器

主题解析器。只允许一个实例

  • 查找名为themeResolver、类型为ThemeResolverBean作为该类型组件。
  • 如果没有找到,则使用默认的实现类FixedThemeResolve作为该类型组件。

处理器映射器

处理器映射器。允许多个实例

  • 如果detectAllHandlerMappings的属性为true(默认为true),则根据类型匹配机制查找上下文以及Spring容器中所有类型为HandlerMappingBean,将它们作为该类型组件。
  • 如果detectAllHandlerMappings的属性为false,则查找名为handlerMapping,类型为HandlerMappingBean作为该类型组件。
  • 如果通过以上两种方式都没有找到,则使用BeanNameUrLHandlerMapping实现类创建该类型的组件。

处理器适配器

处理器适配器。允许多个实例

  • 如果detectAllHandlerAdapters的属性为true(默认为true),则根据类型匹配机制查找上下文以及Spring容器中所有类型为HandlerAdapterBean,将它们作为该类型组件。
  • 如果detectAllHandlerAdapters的属性为false,则查找名为handlerAdapter、类型为HandlerAdapterBean作为该类型组件。
  • 如果通过以上两种方式都没有找到,则使用DispatcherServlet.properties配置文件中指定的三个实现类分别创建一个适配器,并将其添加到适配器列表中。

视图解析器

视图解析器。允许多个实例

  • 如果detectAllViewResolvers的属性为true(默认为true),则根据类型匹配机制查找上下文以及Spring容器中所有类型为ViewResolverBean,将它们作为该类型组件。
  • 如果detectAllViewResolvers的属性为false,则查找名为viewResolvers、类型为ViewResolverBean作为该类型组件。
  • 如果通过以上两种方式都没有找到,则查找DispatcherServlet.properties配置文件中定义的默认实现类InternalResourceViewResolver作为该类型的组件。

文件上传解析器

文件上传解析器。只允许一个实例

  • 查找名为muliipartResolver、类型为MuliipartResolverBean作为该类型组件。
  • 如果用户没有在上下文中显式定义MuliipartResolver类型的组件,则DispatcherServlet将不会加载该类型的组件。

FlashMap映射管理器

FlashMap映射管理器。

  • 查找名为FlashMapManager、类型为SessionFlashMapManagerBean作为该类型组件,用于管理FlashMap,即数据默认存储在HttpSession中。

小结

DispatcherServlet装配的各种组件,

  • 有些只允许一个实例,比如文件上传解析器MuliipartResolver、本地化解析器LocaleResolver等;
  • 有些则允许多个实例,如处理器映射器HandlerMapping、处理器适配器HandlerAdapter等,

读者需要注意这一点。

如果同一类型的组件存在多个,那么它们之间的优先级顺序如何确定呢?因为这些组件都实现了org.springframework.core.Ordered接口,所以可以通过Order属性确定优先级的顺序,值越小的优先级越高。

示例2 基于注解的控制器

Spring2.5开始新增了基于注解的控制器,也就是说控制器不用实现Controller接口,通过注解类型来描述。下面将SpringMVCTest这个Web应用进行修改演示一个基于注解的控制器Spring MVCWeb应用.
新建一个Dynamic Web Project,也就是新建一个动态Web项目,命名为AnnotationTest。所有步骤和2.3.3节的”第一个Spring MVC应用”示例一样,只是修改两个地方

项目结构

展开/折叠
G:\Desktop\随书源码\Spring+Mybatis企业应用实战(第2版)\codes\02\AnnotationTest
├─src\
│ └─org\
│   └─fkit\
│     └─controller\
│       └─HelloController.java
└─WebContent\
  ├─META-INF\
  │ └─MANIFEST.MF
  └─WEB-INF\
    ├─content\
    │ └─welcome.jsp
    ├─lib\
    │ ├─commons-logging-1.2.jar
    │ ├─spring-aop-5.0.1.RELEASE.jar
    │ ├─spring-aspects-5.0.1.RELEASE.jar
    │ ├─spring-beans-5.0.1.RELEASE.jar
    │ ├─spring-context-5.0.1.RELEASE.jar
    │ ├─spring-context-indexer-5.0.1.RELEASE.jar
    │ ├─spring-context-support-5.0.1.RELEASE.jar
    │ ├─spring-core-5.0.1.RELEASE.jar
    │ ├─spring-expression-5.0.1.RELEASE.jar
    │ ├─spring-instrument-5.0.1.RELEASE.jar
    │ ├─spring-jcl-5.0.1.RELEASE.jar
    │ ├─spring-jdbc-5.0.1.RELEASE.jar
    │ ├─spring-jms-5.0.1.RELEASE.jar
    │ ├─spring-messaging-5.0.1.RELEASE.jar
    │ ├─spring-orm-5.0.1.RELEASE.jar
    │ ├─spring-oxm-5.0.1.RELEASE.jar
    │ ├─spring-test-5.0.1.RELEASE.jar
    │ ├─spring-tx-5.0.1.RELEASE.jar
    │ ├─spring-web-5.0.1.RELEASE.jar
    │ ├─spring-webflux-5.0.1.RELEASE.jar
    │ ├─spring-webmvc-5.0.1.RELEASE.jar
    │ └─spring-websocket-5.0.1.RELEASE.jar
    ├─springmvc-config.xml
    └─web.xml

1. Controller类的实现

HelloController类不需要Controller接口,改为使用注解类型来描述,处理/hello请求。示例代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package org.fkit.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

/**
* HelloController是一个基于注解的控制器, 可以同时处理多个请求动作,并且无须实现任何接口。
* org.springframework.stereotype.Controller注解用于指示该类是一个控制器
*/
@Controller
public class HelloController
{
/**
* org.springframework.web.bind.annotation.RequestMapping注解
* 用来映射请求的的URL和请求的方法等。本例用来映射"/hello" hello只是一个普通方法。
* 该方法返回一个包含视图路径或视图路径和模型的ModelAndView对象。
*/
@RequestMapping(value = "/hello")
public ModelAndView hello()
{
System.out.println("hello方法 被调用");
// 创建准备返回的ModelAndView对象,
// 该对象通常包含了返回视图的路径、模型的名称以及模型对象
ModelAndView mv = new ModelAndView();
// 添加模型数据 可以是任意的POJO对象
mv.addObject("message", "Hello World!");
// 设置逻辑视图名,视图解析器会根据该名字解析到具体的视图页面
mv.setViewName("/WEB-INF/content/welcome.jsp");
// 返回ModelAndView对象。
return mv;
}
}

HelloController是一个基于注解的控制器,org.springframework.stereotype.Controller注释类型用于指示Spring类的实例是一个控制器。org.springframework.web.bind.annotation.RequestMapping注释类型用来映射一个请求和请求的方法, value="/hello"表示请求由hello方法进行处理。方法返回一个包含视图名或视图名和模型的ModelAndView对象,这和2.3.4节中的示例一样。

2. 修改Spring MVC的配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?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/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">

<!-- spring可以自动去扫描base-pack下面的包或者子包下面的java文件, -->
<!-- 如果扫描到有Spring的相关注解的类,则把这些类注册为Spring的bean -->
<context:component-scan
base-package="org.fkit.controller" />
<!-- 配置处理映射器 -->
<bean
class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>
<!-- 配置处理器适配器 -->
<bean
class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/>
<!-- 视图解析器 -->
<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver"/>
</beans>

由于使用了注解类型,因此不需要再在配置文件中使用XML描述Bean

扫描查找机制

Spring使用扫描机制査找应用程序中所有基于注解的控制器类。<context:component-scan base-package="org.fkit.controller" />指定需要Spring扫描org.fkit.controller包及其子包下面的所有Java文件

此处还配置类型的处理器映射器RequestMappingHandlerMapping,它根据请求査找映射;
同时配置了annotation类型的处理器适配器RequestMappingHandlerAdapter,来完成对Hellocontro11er类的@RequestMapping标注方法的调用;
最后配置了视图解析器InternalresourceviewResolver来解析视图,将View呈现给用户。

需要注意的是,在Spring4.0之后,处理器映射器处理器适配器的配置还可以使用更简便的方式,笔者在此处显示配置处理过程,是希望读者能够了解Spring MVC的每一个动作,之后可以更好地理解Spring MVC的工作流程

3. 测试

使用Eclipse部署AnnotationTest这个Web应用,在浏览器中输入如下URL来测试应用

1
http://localhost:8080/AnnotationTest/hello

此时浏览器上显示Hello World!字符串,这表示Spring MVC访问成功

示例1:基于Controller接口的控制器

1.增加Spring的支持

首先,使用Eclipse新建一个Dynamic Web Project,也就是新建一个动态Web项目,命名为SpringMVCTest

项目结构

展开/折叠
G:\Desktop\随书源码\Spring+Mybatis企业应用实战(第2版)\codes\02\SpringMVCTest
├─mytree
├─src\
│ └─org\
│   └─fkit\
│     └─controller\
│       └─HelloController.java
└─WebContent\
  ├─META-INF\
  │ └─MANIFEST.MF
  └─WEB-INF\
    ├─content\
    │ └─welcome.jsp
    ├─lib\
    │ ├─commons-logging-1.2.jar
    │ ├─spring-aop-5.0.1.RELEASE.jar
    │ ├─spring-aspects-5.0.1.RELEASE.jar
    │ ├─spring-beans-5.0.1.RELEASE.jar
    │ ├─spring-context-5.0.1.RELEASE.jar
    │ ├─spring-context-indexer-5.0.1.RELEASE.jar
    │ ├─spring-context-support-5.0.1.RELEASE.jar
    │ ├─spring-core-5.0.1.RELEASE.jar
    │ ├─spring-expression-5.0.1.RELEASE.jar
    │ ├─spring-instrument-5.0.1.RELEASE.jar
    │ ├─spring-jcl-5.0.1.RELEASE.jar
    │ ├─spring-jdbc-5.0.1.RELEASE.jar
    │ ├─spring-jms-5.0.1.RELEASE.jar
    │ ├─spring-messaging-5.0.1.RELEASE.jar
    │ ├─spring-orm-5.0.1.RELEASE.jar
    │ ├─spring-oxm-5.0.1.RELEASE.jar
    │ ├─spring-test-5.0.1.RELEASE.jar
    │ ├─spring-tx-5.0.1.RELEASE.jar
    │ ├─spring-web-5.0.1.RELEASE.jar
    │ ├─spring-webflux-5.0.1.RELEASE.jar
    │ ├─spring-webmvc-5.0.1.RELEASE.jar
    │ └─spring-websocket-5.0.1.RELEASE.jar
    ├─springmvc-config.xml
    └─web.xml

复制Spring的jar包到项目lib目录

为了让Web应用具有Spring支持的功能,将spring-framework-5.0.1.RELEASE解压文件夹下的libs文件夹下所有Spring框架的class文件的jar包和Spring所依赖的commons-logging-1.2.jar复制到Web应用的lib文件夹下,也就是SpringMVCTest\WebContent\WEB-INF路径下.
可以在libs目录下打开cmd,使用如下copy命令复制即可:

1
copy *.RELEASE.jar E:\workspace_shizhan\SpringMVCTest\WebContent\WEB-INF\lib

复制效果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
G:\Desktop\随书源码\库文件\spring-framework-5.0.1.RELEASE\libs>copy *.RELEASE.jar E:\workspace_shizhan\SpringMVCTest\WebContent\WEB-INF\lib
spring-aop-5.0.1.RELEASE.jar
spring-aspects-5.0.1.RELEASE.jar
spring-beans-5.0.1.RELEASE.jar
spring-context-5.0.1.RELEASE.jar
spring-context-indexer-5.0.1.RELEASE.jar
spring-context-support-5.0.1.RELEASE.jar
spring-core-5.0.1.RELEASE.jar
spring-expression-5.0.1.RELEASE.jar
spring-instrument-5.0.1.RELEASE.jar
spring-jcl-5.0.1.RELEASE.jar
spring-jdbc-5.0.1.RELEASE.jar
spring-jms-5.0.1.RELEASE.jar
spring-messaging-5.0.1.RELEASE.jar
spring-orm-5.0.1.RELEASE.jar
spring-oxm-5.0.1.RELEASE.jar
spring-test-5.0.1.RELEASE.jar
spring-tx-5.0.1.RELEASE.jar
spring-web-5.0.1.RELEASE.jar
spring-webflux-5.0.1.RELEASE.jar
spring-webmvc-5.0.1.RELEASE.jar
spring-websocket-5.0.1.RELEASE.jar
已复制 21 个文件。

复制commons-logging的jar包到项目lib目录

下载commons-logging-1.2,解压,然后进入解压后的目录,输入如下copy命令来复制commons-logging-1.2.jar到项目路径中:

1
copy *1.2.jar E:\workspace_shizhan\SpringMVCTest\WebContent\WEB-INF\lib

复制效果:

1
2
3
G:\Desktop\随书源码\库文件\commons-logging-1.2>copy *1.2.jar E:\workspace_shizhan\SpringMVCTest\WebContent\WEB-INF\lib
commons-logging-1.2.jar
已复制 1 个文件。

复制后的libs目录文件列表

此时libs目录下的文件如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
E:\workspace_shizhan\SpringMVCTest\WebContent\WEB-INF\lib
├─commons-logging-1.2.jar
├─spring-aop-5.0.1.RELEASE.jar
├─spring-aspects-5.0.1.RELEASE.jar
├─spring-beans-5.0.1.RELEASE.jar
├─spring-context-5.0.1.RELEASE.jar
├─spring-context-indexer-5.0.1.RELEASE.jar
├─spring-context-support-5.0.1.RELEASE.jar
├─spring-core-5.0.1.RELEASE.jar
├─spring-expression-5.0.1.RELEASE.jar
├─spring-instrument-5.0.1.RELEASE.jar
├─spring-jcl-5.0.1.RELEASE.jar
├─spring-jdbc-5.0.1.RELEASE.jar
├─spring-jms-5.0.1.RELEASE.jar
├─spring-messaging-5.0.1.RELEASE.jar
├─spring-orm-5.0.1.RELEASE.jar
├─spring-oxm-5.0.1.RELEASE.jar
├─spring-test-5.0.1.RELEASE.jar
├─spring-tx-5.0.1.RELEASE.jar
├─spring-web-5.0.1.RELEASE.jar
├─spring-webflux-5.0.1.RELEASE.jar
├─spring-webmvc-5.0.1.RELEASE.jar
└─spring-websocket-5.0.1.RELEASE.jar

这样该Web应用已经加入了Spring的必需类库。但还需要修改web.xml文件,让该文件负责加载Spring框架。

2. 配置前端控制器DispatcherServlet

eclipse生成web.xml

进入WebContent\WEB-INF目录,打开web.xml文件,如果没有web.xml文件,可以在项目上右键,选择Java EE Tools,然后选择Generate Deployment Descriptor Stub即可生成web.xml文件.

web.xml中配置前段控制器

在人在该文件中配置Spring MVC的前端控制器DispatcherServlet,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
id="WebApp_ID" version="3.1">
<!-- 定义Spring MVC的前端控制器 -->
<servlet>
<!-- 默认去应用程序文件夹下的`WEB-INF`文件夹下査找对应的`springmvc-servlet.xml`作为配置文件 -->
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- Spring MVC配置文件的路径 -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/springmvc-config.xml</param-value>
</init-param>
<!-- 在启动时立即加载 -->
<load-on-startup>1</load-on-startup>
</servlet>
<!-- 让Spring MVC的前端控制器拦截所有请求 -->
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>

web.xml文件的内容告诉Web容器,将使用Spring MVCDispatcherServlet,并通过配置url-pattern元素的值为"/",将所有的URL都映射到该Servlet

3 配置Spring MVC的Controller

接下来是Spring MVC的配置文件,创建/WEB-INF/springmvc-config.xml文件,写入如下配置信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">

<!-- 配置Handle,映射"/hello"请求 -->
<bean name="/hello" class="org.fkit.controller.HelloController"/>

<!-- 处理映射器 将bean的name作为url进行查找,需要在配置Handle时指定name(即url) -->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>

<!-- SimpleControllerHandlerAdapter是一个 处理器适配器 ,所有处理适配器都要实现 HandlerAdapter接口-->
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>

<!-- 视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"/>
</beans>
  • springmvc-config.xml文件声明了HelloController业务控制器类,并将其映射到/hello请求。
  • 此处还配置了一个处理器映射器BeanNameUrlHandlerMapping,这样可以Bean的名称为URL进行查找;
  • 同时配置了处理器适配器SimpleControllerHandlerAdapter,来完成对HelloController类的handleRequest方法的调用;
  • 最后配置了视图解析器InternalResourceViewResolver来解析视图,将View呈现给用户。

需要注意的是,在Spring4.0之后,如果不配置处理器映射器、处理器适配器和视图解析器,也会使用默认的完成Spring内部MVC工作,笔者在此处显示配置处理过程,是希望读者能够了解Spring MVC的每一个动作,之后可以更好地理解Spring MVC的工作流程。

4. Controller类的实现

HelloController类实现了Controller接口,用来处理/hello请求。示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package org.fkit.controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

/**
* HelloController是一个实现Controller接口的控制器, 可以处理一个单一的请求动作
*/
public class HelloController implements Controller
{
/**
* handleRequest是Controller接口必须实现的方法。
* 该方法的参数是对应请求的HttpServletRequest和对应响应的HttpServletResponse。
* 该方法必须返回一个包含视图路径或视图路径和模型的ModelAndView对象。
*/
@Override
public ModelAndView handleRequest(HttpServletRequest request,
HttpServletResponse response) throws Exception
{
System.out.println("handleRequest 被调用");
// 创建准备返回的ModelAndView对象,该对象通常包含了返回视图的路径、模型的名称以及模型对象
ModelAndView mv = new ModelAndView();
// 添加模型数据 可以是任意的POJO对象
mv.addObject("message", "Hello World!");
// 设置逻辑视图名,视图解析器会根据该名字解析到具体的视图页面
mv.setViewName("/WEB-INF/content/welcome.jsp");
// 返回ModelAndView对象。
return mv;
}
}

HelloController是一个实现Controller接口的控制器,它可以处理一个单的请求动作。 handleRequestController接口必须实现的方法, Controller调用该方法来处理请求。该方法的参数是对应请求的HttpServletRequestHttpServletResponse,该方法必须返回一个包含视图名或视图名和模型的ModelAndView对象。本例返回的模型中包含了一个名为message的字符串对象,返回的视图路径为/WEB-INF/content/welcome.jsp,因此,请求将被转发到welcome.jsp页面。
提示:Spring MVC建议把所有的视图页面存放在WEB-INF文件夹下,这样可以保护视图页面,避免直接向视图页面发送请求。上面的HelloController类的handleRequest方法处理完请求后, Spring MVC会调用/WEB-INF/content/文件夹下的welcome.jsp呈现视图.

5. View页面

SpringMVCTest包含一个视图页面welcome.jsp,用来显示欢迎信息。

此处的JSP页面使用了EL表达式来简化页面开发,关于EL表达式的使用可参考附录A EL表达式和JSTL标签库”的内容

1
2
3
4
5
6
7
8
9
10
11
12
13
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>欢迎页面</title>
</head>
<body>
<!-- 页面可以访问Controller传递传递出来的message -->
${requestScope.message}
</body>
</html>

6.测试应用

使用Eclipse部署SpringMVCTest这个Web应用,在浏览器中输入如下URL来测试应用,

1
http://localhost:8080/SpringMVCTest/hello

浏览器上将会显示Hello World!这个字符串,这表示Spring MVC访问成功。

提示:使用MVC框架就应该严格遵守MVC思想。MVC框架不赞成浏览器直接访问Web应用的视图页面,用户的所有请求都只应向控制器发送,由控制器调用模型组件、视图组件向用户呈现数据.