Spring MVC教程(简明版)
虽然 Spring Boot 近几年发展迅猛,但是 Spring MVC 在 Web 开发领域仍然占有重要的地位。本文主要讲解 Spring MVC 的核心:DispatcherServlet 类、拦截器及控制器的相关知识。
Spring MVC 是一种将业务、视图、数据分离的设计模式。它不仅出现在 Java 后端开发中,在前端开发中也经常使用这种设计模式。Spring MVC 提供了高度可配置的 Web 框架和多种视图解决方案,并且提供了基于 Servlet 的接口,可以灵活地处理请求。

图1 Spring MVC的工作流程图
从图 1 中可以看到,Spring MVC 框架的主要工作流程可以分为以下几步:
Spring MVC 框架提供了很多重要的接口,用于完成一个 HTTP 请求的处理,主要的接口如表 1 所示。
DispatcherServlet 类需要提前声明,可以采用 Java 配置或 web.xml 方式进行声明。它通过请求映射、视图解析及统一异常处理等 Spring 配置发现真正的请求处理组件,从而完成对请求的处理。
DispatcherServlet 类处理请求的步骤如下:
DispatcherServlet 类通过 web.xml 方式可以声明,代码如下:
DispatcherServlet 类也可以通过实现 WebApplicationInitializer 接口来声明。以下是摘自 Spring 官网的一段示例代码:
Handler-Interceptor 接口提供了 3 个方法:
下面展示一个简单的 Interceptor 登录拦截器的例子:
如果想让 Interceptor 生效,还需要声明一下,代码如下:
根据不同的请求方法,Spring MVC 还提供了一些更简单的注解,具体如下:
下面给出一个请求注解与参数注解的示例:
声明:《Java系列教程》为本站“54笨鸟”官方原创,由国家机构和地方版权局所签发的权威证书所保护。
Spring MVC 是一种将业务、视图、数据分离的设计模式。它不仅出现在 Java 后端开发中,在前端开发中也经常使用这种设计模式。Spring MVC 提供了高度可配置的 Web 框架和多种视图解决方案,并且提供了基于 Servlet 的接口,可以灵活地处理请求。
Spring MVC 的工作流程
Spring MVC 框架主要由核心 Servlet(DispatcherServlet)、处理器映射(Handler-Mapping)、控制器(Controller)、视图解析器(ViewResolver)、模型(Model)及视图(View)等几部分组成,其主要的工作流程如图 1 所示。
图1 Spring MVC的工作流程图
从图 1 中可以看到,Spring MVC 框架的主要工作流程可以分为以下几步:
- 在浏览器中输入 URL 地址后,所有的请求都被 DispatcherServlet 拦截。
- DispatcherServlet 通过 HandlerMapping 解析 URL 地址,找到匹配的能处理该请求的 Controller,然后请求被 Controller 处理。
- Controller 通过调用具体的业务逻辑,返回 ModelAndView。
- DispatcherServlet通过ViewResolver(视图解析器),组装 Model 数据与对应的视图,如某个 JSP 页面等。
- 将视图结果展现在浏览器页面上。
Spring MVC 框架提供了很多重要的接口,用于完成一个 HTTP 请求的处理,主要的接口如表 1 所示。
接 口 | 说 明 |
---|---|
HandlerMapping | 通过一系列拦截器将请求映射到一个控制器(Controller)上 |
HandlerAdapter | DispatcherServlet的辅助类,辅助映射请求处理 |
HandlerExceptionResolver | 解析异常,可以映射到一个处理器、视图或其他目标对象上 |
ViewResolver | 视图解析器,返回对应的真正视图 |
LocalResolver | 区域解析器 |
MultipartResolver | 处理文件上传等请求 |
DispatcherServlet 类
DispatcherServlet 类是 Servlet 的一种实现,置于 Controller 前,对所有的请求提供统一的处理逻辑。DispatcherServlet 类需要提前声明,可以采用 Java 配置或 web.xml 方式进行声明。它通过请求映射、视图解析及统一异常处理等 Spring 配置发现真正的请求处理组件,从而完成对请求的处理。
DispatcherServlet 类处理请求的步骤如下:
- DispatcherServlet 类将 WebApplicationContext 绑定到请求中,WebApplication-Context 会持有 Controller、ViewResolver、HandlerMapping、Service 及 Repository 等。
- HandlerMapping 通过匹配规则授权给一个 Handler 来处理具体的逻辑,此处的 Handler 是一个处理链,整个处理过程会通过拦截器(Interceptor)和控制器(Controller)等来处理。
- 返回 Model 数据并渲染视图。
DispatcherServlet 类通过 web.xml 方式可以声明,代码如下:
<?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_4_0.xsd" version="4.0"> <context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/dispatcher-servlet.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.Context LoaderListener </listener-class> </listener> <servlet> <servlet-name>dispatcher</servlet-name> <servlet-class>org.springframework.web.servlet.Dispatcher Servlet </servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dispatcher</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
DispatcherServlet 类也可以通过实现 WebApplicationInitializer 接口来声明。以下是摘自 Spring 官网的一段示例代码:
public class MyWebApplicationInitializer implements WebApplication Initializer { @Override public void onStartup(ServletContext servletCxt) { //加载Spring Web应用上下文 AnnotationConfigWebApplicationContext ac = new AnnotationConfigWebApplicationContext(); ac.register(AppConfig.class); ac.refresh(); //创建DispatcherServlet DispatcherServlet servlet = new DispatcherServlet(ac); //注册DispatcherServlet ServletRegistration.Dynamic registration = servletCxt. addServlet("app", servlet); registration.setLoadOnStartup(1); registration.addMapping("/app/*"); } }
HandlerInterceptor 拦截器
为了实现一些特殊功能,如在请求处理之前进行用户校验或日志记录等,Spring MVC 提供了灵活的处理方式,即定义拦截器。自定义拦截器实现 HandlerInterceptor 接口,然后实现对应的抽象方法,这样可以做一些预处理或请求之后的处理。Handler-Interceptor 接口提供了 3 个方法:
- preHandle(HttpServletRequest request, HttpServletResponse response, Object handler):默认返回 true。该方法是在处理器执行之前调用,通过返回 true 或 false,判断是否继续执行后面的处理链。返回 true 表示继续向后执行,返回 false 表示中断后续处理。
- postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView):该方法是在处理器处理请求之后及解析视图之前调用,可以通过该方法对请求后的模型和视图进行修改。
- afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex):该方法是在处理器处理完请求之后,即视图渲染结束后执行。
下面展示一个简单的 Interceptor 登录拦截器的例子:
//自定义拦截器 public class LogInterceptor implements HandlerInterceptor { //处理器处理请求之后且视图渲染结束执行 @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,Exception ex) throws Exception { System.out.println("afterCompletion方法是在处理器处理完请求之后,即视图渲染结束后执行"); } //处理器处理请求之后,即解析视图之前调用 @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("postHandle方法是在处理器处理请求之后解析视图之前调用"); } //处理器处理请求之前执行 @Override public boolean preHandle(HttpServletRequest request,HttpServletResponse response, Object handler)throws Exception { System.out.println("preHandle方法是在处理器执行之前调用"); return true; } }
如果想让 Interceptor 生效,还需要声明一下,代码如下:
<mvc:interceptors> <!--配置一个全局拦截器,拦截所有请求 --> <bean class="com.cn.springmvc.LogInterceptor" /> </mvc:interceptors>
注意:postHandle() 方法很少与 @ResponseBody 和 ResponseEntity 配合使用,因为 @ResponseBody 和 ResponseEntity 已经提交了响应,无法再修改。
Spring MVC 注解
Spring MVC 框架提供了大量的注解,如请求注解、参数注解、响应注解及跨域注解等。这些注解提供了解决 HTTP 请求的方案。1) 请求注解
请求注解声明在类或者方法中用于声明接口类或者请求方法的类型。1. @Controller 注解
@Controller 注解声明在类中,表示该类是一个接口类。@RestController 也是声明接口类,它是一个组合注解,由 @Controller 与 @ResponseBody 注解组合而成。2. @RequestMapping 注解
@RequestMapping 注解声明在类或者方法中,可以指定路径、方法(GET、HEAD、POST、PUT、PATCH、DELETE、OPTIONS或TRACE等)或参数等。根据不同的请求方法,Spring MVC 还提供了一些更简单的注解,具体如下:
- @GetMapping:相当于@RequestMapping(method = {RequestMethod.GET})。
- @PostMapping:相当于@RequestMapping(method = {RequestMethod.POST})。
- @PutMapping:相当于@RequestMapping(method = {RequestMethod.PUT})。
- @DeleteMapping:相当于@RequestMapping(method = {RequestMethod.DELETE})。
- @PatchMapping:相当于@RequestMapping(method = {RequestMethod.PATCH})。
2) 参数注解
参数注解可以对方法的请求参数进行注解,用于获取 HTTP 请求中的属性值。常用的参数注解如下:- @PathVariable:URL 路径参数,用于将 URL 路径中的参数映射到对应方法的参数中。
- @RequestBody:可以映射请求的 Body 对象。
- @RequestHeader:请求 Header 的映射。
- @CookieValue:用于获取 Cookie 的属性值。
- @SessionAttribute:用于获取 Session 的属性值。
下面给出一个请求注解与参数注解的示例:
//定义HiController @RestController @RequestMapping("/hi") public class HiController { //请求路径/hi/mvc/{id} @RequestMapping("/mvc/{id}") public ModelAndView sayHi(@PathVariable Integer id){ System.out.println(id); //定义视图模型 ModelAndView modelAndView = new ModelAndView(); modelAndView.setViewName("say"); modelAndView.addObject("name","mvc"); return modelAndView; } }对应的 say.jsp 代码如下:
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> hi ,${name} </body> </html>
3) 异常注解
有时接口请求的业务处理逻辑会产生异常,为了全局统一异常处理,返回同一个异常页面,可以使用 @ExceptionHandler 注解。@ExceptionHandler 注解声明在方法中,用于提供统一的异常处理。具体的示例代码如下:public class BaseController { //统一异常处理 @ExceptionHandler public String exceptionHandler(HttpServletRequest request, Exception ex){ request.setAttribute("ex", ex); return "error"; } }在以上代码中,用 @ExceptionHandler 声明 exceptionHandler() 方法,如果接口处理有异常,则跳转到 error.jsp 页面。其他接口类继承 BaseController 类的示例代码如下:
@RestController @RequestMapping("/hi") public class TestExceptionController extends BaseController { @RequestMapping("/error") public ModelAndView sayHi(){ throw new RuntimeException("testExceptionHandler"); } }为了方便测试,处理方法直接抛出异常,则浏览器跳转到 error.jsp 页面。error.jsp 页面的示例代码如下:
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> hi,error </body> </html>
4) 跨域注解
后台服务器如果要满足多个客户端的访问,则需要设置跨域访问。@CrossOrigin 注解提供了跨域访问的可能性,它可以声明在类中,也可以声明在方法中。代码如下://声明在类中的跨域注解 @CrossOrigin(maxAge = 3600) @RestController @RequestMapping("/account") public class AccountController { //声明在方法中的跨域注解 @CrossOrigin("https://domain2.com") @GetMapping("/{id}") public Account retrieve(@PathVariable Long id) { ... } @DeleteMapping("/{id}") public void remove(@PathVariable Long id) { ... } }如果要全局配置跨域,则需要实现 WebMvcConfigurer 接口的 addCorsMappings() 方法,代码如下:
@Configuration @EnableWebMvc public class WebConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { //全局配置跨域属性 registry.addMapping("/api/**") .allowedOrigins("https://domain2.com") .allowedMethods("PUT", "DELETE") .allowedHeaders("header1", "header2", "header3") .exposedHeaders("header1", "header2") .allowCredentials(true) .maxAge(3600); } }
5) 请求跳转
请求跳转可以分为“主动跳转”“被动跳转”,其中,“被动跳转”又称为“重定向”。Spring MVC 提供了 forword 和 redirect 关键字用于实现主动跳转和重定向,示例代码如下:@RestController @RequestMapping("/hi") public class HiController { @RequestMapping("/forward") public String testForward(){ //请求forward跳转 return "forward:/hi/error"; } @RequestMapping("/redirect") public String testRedirect(){ //请求redirect跳转 return "redirect:/hi/error"; } }
声明:《Java系列教程》为本站“54笨鸟”官方原创,由国家机构和地方版权局所签发的权威证书所保护。