Spring MVC
Spring MVC 是 javaWeb 的开发模块,是一个架构模式,是整个 javaWeb 开发领域 最重要的一个技术。
Spring MVC 文档
- Spring MVC 是 Spring 体系的 轻量级Web MVC框架。
- Spring MVC 的核心 Controller 控制器,用于处理请求,产生响应。
- Spring MVC 基于 Spring IOC 容器运行,所有对象被 IOC 管理。
- Spring 5.x 版本变化:
- Spring 5.x 最低要求 JDK8 与 J2EE 7(Servlet 3.1 / Tomcat 8.5+)。
- Spring 5.x 支持 JDK8/9,可以使用新特性。
- Spring 5.x 最重要的新特性支持 响应式编程(事件,一种新的开发理念和风格)。
- 模型就是 XxService 类,用来处理业务逻辑。
Spring MVC 环境配置 与 启动
- 创建一个 Maven WebAPP 项目。
- Maven 依赖 spring-webmvc。
- 打开 pom.xml 文件,增加 spring-webmvc 依赖。
- spring-webmvc 包含了 spring-context
- 打开 pom.xml 文件,增加 spring-webmvc 依赖。
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.24</version>
</dependency>
<!-- jdk17需要引入servlet、jsp -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<!-- provided表明该包只在编译和测试的时候用,所以,当启动tomcat的时候,就不会冲突了 -->
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.3</version>
<scope>provided</scope>
</dependency>
</dependencies>
- 配置 SpringMVC
- 在 main/resource 下新建 springmvc.xml
<!-- 引入 applicationContext.xml 文件 -->
<import resource="classpath:applicationContext.xml"/>
<!-- 配置视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 能够自动为视图加上前缀,后缀 -->
<property name="prefix" value=""/>
<property name="suffix" value=""/>
</bean>
- web.xml 配置 DispatcherServlet。
- 打开 web.xml 文件,在 标签中添加 子标签。
- < servlet>
- < servlet-name>:名字可以自定义,但一般叫做 springmvc
- < servlet-class>:servlet 实现哪个类:org.springframework.web.servlet.DispatcherServlet
- DispatcherServlet 前端控制器是 Spring MVC 最核心的类,DispatcherServlet 用于拦截 Http 请求,并根据请求的 URL 调用与之对应的 controller 方法,来完成 Http 请求的处理。
- < init-param>:初始化参数,告诉 DispatcherServlet 在 IOC 容器启动时加载 哪个目录下的哪个 xml 文件。
- < param-name>:contextConfigLocation,上下文配置文件的路径所在。
- < param-value>:classpath:springmvc.xml
- 若没有单独设置 springmvc.xml,就用 applicationContext.xml
- < load-on-startup>0:启动时加载,在Web应用启动时自动创建 Spring IOC 容器,并初始化 DispatcherServlet。
- 如果缺省了,会在第一次访问 url 时才进行创建。
- < servlet-mapping>
- < servlet-name>:与 的 name 相同,说明指向 DispatcherServlet 类。
- < url-pattern>:拦截模式,
- /:一般用这个
- 看官方文档可知,如果我们的项目中配置了"/“,会覆盖掉tomcat中的默认servlet,当其他的url-pattern匹配不上时都会走这个servlet,它会匹配到后缀型url,它除了能够处理静态资源如“.js”,“.css”,”.png"等,还能够处理HTTP缓存请求,媒体(音频/视频)数据流和文件下载简历。说到为什么JSP页面的请求(.jsp)并不会命中这个servlet,那是因为当有这种url请求时,servlet容器内建的JSP servlet将会被调用,而这个容器内建的JSP servlet已经默认地映射在了.jsp上。
- /:一般用这个
<?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">
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>0</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
- 配置 applicationContext 的 mvc 标记。
- 在 main/resource 下新建 applicationContext.xml 文件。
- < context:component-scan>:在spring Ioc初始化过程中,自动创建并管理 指定包 及子包中拥有以下注解的对象.
- @Repository @Service @Controller @Component
- < mvc:annotation-driven/>:启用 Spring MVC 的注解开发模式。不包含@GetMapper等注解
- < mvc:default-servlet-handler/>:放行 静态资源(图片/JS/CSS等),可提高执行效率。
- 因为 所有的请求都会被 DispatcherServlet 拦截,而 静态资源(图片/JS/CSS等)很多时候没必要。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mv="http://www.springframework.org/schema/mvc"
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">
<context:component-scan base-package="com.imooc.springmvc">
</context:component-scan>
<!-- 启用 Spring MVC 的注解开发模式 -->
<mvc:annotation-driven/>
<!-- 将 图片/JS/Css等 静态资源排除在外,可提高执行效率 -->
<mvc:default-servlet-handler/>
</beans>
- 开发 Controller 控制器。
- 在 main/java 常见的项目下创建一个 controller 包,用于存放控制器,然后在 controller 包中新建一个类 控制器类。
- 控制器类用于 处理 Http 的请求并返回响应。
- 为类加上注解 @Controller 声明其为控制器类。
- 在类中创建方法,为方法加上两个注解:
- @GetMapping(“/url”):将当前方法绑定某个 Get 请求的 Url。(也可以用其他的绑定注解)
- @ResponseBody:直接向响应输出字符串数据,不跳转页面。
- 在 main/java 常见的项目下创建一个 controller 包,用于存放控制器,然后在 controller 包中新建一个类 控制器类。
package com.imooc.springmvc.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class TestController {
@GetMapping("/t") // localhost:8080/test_springmvc/t
@ResponseBody // 直接向响应输出字符串数据,不跳转页面
public String test() {
return "<h1>Hello Spring MVC</h1>";
}
}
接收请求参数
普通数据
- 使用 Controller 方法参数 接收:
- 方法形参名 与 请求参数名 一致,支持强制类型转换。
- 不区分 Get / Post。不过 Get 请求在提交的时候 参数会显示在 URL 里。
- 若 强制类型转换不成功,网页会报 400-Bad Request 错误,编辑器会报 NumberFormatException 异常。
- @RequestParam(“请求参数名”):在 方法形参 前使用该注解 ,能够将注解里的 请求参数 注入 到方法形参中,而不需要名字一致。
- defaultValue 如果本次请求没有携带这个参数,或者参数为空,那么就会启用默认值
- name 或value 绑定本次参数的名称,要跟URL上面的一样
- required 这个参数是不是必须的
- 方法形参名 与 请求参数名 一致,支持强制类型转换。
public String getMapping(@RequestParam("manager_name") String managerName) {
return managerName;
}
- 使用 Java Bean 接收数据。
- 方法形参为实体类对象,实体类的 属性名与请求参数名一致 且 实现了 getter/setter 方法,这时 Spring MVC 会自动完成数据注入操作。
@PostMapping("/p")
@ResponseBody
public String postMapping(User user) {
return user.toString();
}
- 所有满足请求参数名条件的属性都会被赋值,不管是实体类对象还是形参。
- 接收 JSON 数据
- @RequestBody :修饰参数(请求体中的数据),表示该参数接收一个 JSON 数据,并会自动转换为 参数对象(例如 bean对象)
- 不能用于接收 GET
- 注:一个请求,只有一个RequestBody;一个请求,可以有多个RequestParam。
- @RequestBody :修饰参数(请求体中的数据),表示该参数接收一个 JSON 数据,并会自动转换为 参数对象(例如 bean对象)
接收 复合 数据
- 利用 数组[] 或者 List 接收请求中的复合数据。
- List 接收复合数据需要在 形参前 用注解 @RequestParam 修饰。
- 通过实体类对象接收请求参数时,可以直接使用 List 接收复合数据,而不需要 注解注释。
@GetMapping("/a")
@RequestBody
public String a(Integer[] purpose1, @RequestParam List<Integer> purpose2) {
}
- 利用 @RequestParam 为参数设置默认值
- @RequestParam(value=“请求参数名”, defaultValue=“默认值”)
@GetMapping("/a")
@RequestBody
public String a(@RequestParam(value = "n", defaultValue = "ANON") String n) {
}
- 使用 @RequestParam 修饰 Map 形参,使用 Map 对象 一次性 接收所有请求参数。
- 请求参数名会作为 key,值会作为 value。
- 不能接收复合数据,若强制接收,只会接收到 复合数据的第一项数据。
@GetMapping("/a")
@RequestBody
public String a(@RequestParam Map map) {
}
日期类型转换
- 将 字符串 强制类型转换为 日期类型,会 出现数转换异常。
- @DateTimeFormat(pattern = “日期模式”):按照指定的 日期格式 将前台的 字符串转换为 Date 对象。
- 可以 直接修饰形参,也可以用 在实体类中修饰属性。
@GetMapping("/a")
@RequestBody
public String a(@DateTimeFormat(pattern = "yyyy-MM-dd") Date date) {
}
@GetMapping("/a")
@RequestBody
public String a(@DateTimeFormat(pattern = "yyyy-MM-dd") Date date) {
}
- 注意:自定义转换器类 的优先级 > 注解转换(@DateTimeFormat)
关联对象赋值
- 一个对象 引用了 另一个对象。
- 例如:
- 我们需要将表单中 被引用控件的 name 增加 关联对象属性名前缀 。
- 注意: 作为引用对象必须在当前类中对其进行 实例化 ,并且 getter/setter,这样才能保证赋值的成功。
- 这样操作后,通过 Spring MVC 接收数据时 会自动的 将属性按照 名称对应关系进行赋值。
响应输出结果
1、@ResponseBody :直接产生响应体的数据,过程不涉及任何视图。
- 可产生 标准字符串 / JSON / XML 等格式数据。
- 产生的信息 会被 StringHttpMessageConverter(编码格式转换器) 所影响。
- 使用:
- 方法返回值为 String。
@PostMapping("/p")
@ResponseBody
public String postMapping(String name) {
return name;
}
- @RestController 代替 @Controller:此时代表此类下的所有方法都默认添加了 @ResponseBody
2、ModelAndView 类:利用模板引擎渲染输出。
- ModelAndView 对象是指 “模型(数据)与视图(界面)” 对象。其有三个对象:model view status
- 通过 ModelAndView 可将包含数据对象 与 模板引擎进行绑定。
- SpringMVC 中默认的 View 是 JSP,也可以配置其他模板引擎。
- 默认 使用 请求转发(forward) 至页面。
- 基础使用:
- 方法返回值必须为 ModelAndView。
@GetMapping("/view")
public ModelAndView showView(String username) {
ModelAndView mv = new ModelAndView("/jsp/view.jsp");
User user = new User();
user.setUsername(username);
mv.addObject("u", user);
return mv;
}
-
ModelAndView 核心对象进阶使用:
- new ModelAndView(“/jsp/view.jsp”):请求转发方式绑定 jsp
- 重定向使用 new ModelAndView(“redirect:/index.jsp”),若请求与模板引擎页面关系不紧密的时候,可以使用。
- addObject(“attributeName”, Object):向 当前请求 添加属性,可直接通过 attributeName 使用。
- setViewName(“URL”):设置视图URL。(在new 时可以使用无参构造方法)
- new ModelAndView(“/jsp/view.jsp”):请求转发方式绑定 jsp
-
视图中使用 请求传过来的属性:${attributeName} EL表达式获取
- 可直接使用 private 修饰的属性和方法。
<body>
<h1>I'm view page</h1><hr/>
<%-- 也可以使用 ${param.username} --%>
<h1>username: ${u.username}</h1>
</body>
3、String:Spring MVC 直接响应 字符串本身。
- 使用率颇高,其实 ModelAndView 就是 这种方式的变种。使用:
- 方法返回 String 类型。
- 方法参数 可以有 ModelMap 或 Model 对象,(直接由Spring提供的)
- addAttribute(“attributeName”, Object):向 当前请求 添加对象,可直接通过 attributeName 使用。
- 方法返回 URL地址 字符串,由 Spring MVC 去处理。
4、void:方法返回值为 void,通过其他方式向前端返回数据
- 通过 HttpServletRequest 做服务端跳转
- public void methodName(HttpServletRequest req, HttpServletResponse resp){}
- req.setAttribute():设置参数
- 使用请求转发或响应重定向转发。此方式 url 需要写全,不会经过视图解析器
public void newsList(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
News news = new News("新闻1", "2022-5-1", "未知", "...");
req.setAttribute("news", news);
req.getRequestDispatcher("/WEB-INF/jsp/news.jsp").forward(req, resp);
}
MVC 转换器
引入 自定义的 类型转换器:
- 创建:使用 Spring 自定义类型转换器方式
- 如果想要在 MVC 中使用,需要 在 Spring MVC 配置的 mvc:annotation-driven/ (启用 Spring MVC 的注解开发模式)中加入属性:
- conversion-service=“conversionService”:指定 bean id,这样之后,在Spring MVC 类型转换的时候一旦遇到对应类型就会使用自定义的转换器来进行类型转换。
<!-- 启用 Spring MVC 的注解开发模式 -->
<mvc:annotation-driven conversion-service="conversionService"/>
- 注意:
- 自定义转换器类 的优先级 > 注解转换(@DateTimeFormat)
- 因此使用 自定义转换器类时,代码部分会有 多重判断,根据原始字符串的 长度、格式 采用不同的模式对其加以解析。
使用MVC提供的转换器
- 在 < mvc:annotation-driven> 启用注解的标签 中,添加字标签来 指定转换器
- 响应消息转换器:
- < mvc:message-converters>:设置消息转换器,这里面设置的转换器可以对响应中的消息进行调整。
- < bean class=“org.springframework.http.converter.StringHttpMessageConverter”>:对响应中的 文本消息 进行转换。
- < property name=“supportedMediaTypes”>:支持的媒体类型,值是 List 集合。
- 设置 response.setContentType() 参数。
- < property name=“supportedMediaTypes”>:支持的媒体类型,值是 List 集合。
- < bean class=“org.springframework.http.converter.StringHttpMessageConverter”>:对响应中的 文本消息 进行转换。
- < mvc:message-converters>:设置消息转换器,这里面设置的转换器可以对响应中的消息进行调整。
<!-- 启用 Spring MVC 的注解开发模式 -->
<mvc:annotation-driven conversion-service="conversionService">
<!-- 设置消息转换器,这里面设置的转换器可以对响应中的消息进行调整。 -->
<mvc:message-converters>
<!-- 对响应中的文本消息进行转换 -->
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<!-- 支持的媒体类型,值是 List 集合。 -->
<property name="supportedMediaTypes">
<list>
<!-- 底层调用servlet的 response.setContentType("text/html;charset=utf-8") -->
<value>text/html;charset=utf-8</value>
</list>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
全局异常处理
@ControllerAdvice
就是@Controller 的增强版。@ControllerAdvice主要用来处理 全局数据。
全局异常处理 结合 @ExceptionHandler
-
项目中,可能会抛出多个异常,我们不可以直接将异常的堆栈信息展示给用户,有以下两个原因:
- 用户体验不好
- 非常不安全
-
例如用户上传的文件超过了限制大小
- 只需在系统中定义 类,然后添加 @ControllerAdvice 注解即可。当系统启动时,该类就会被扫描到 Spring 容器中,
- 然后定义 uploadException 方法,
- 在该方法上添加@ExceptionHandler(MaxUploadSizeExceededException.class) 注解,其中定义的 表明该方法用来处理指定类型的异常。
- 方法的参数可以有 异常实例、HttpServletResponse 以及 HttpServletRequest、Model 等,返回值可以是一段JSON、一个ModelAndView、一个逻辑视图名等。此时,上传一个超大文件会有错误提示给用户。
@ControllerAdvice
public class CustomExceptionHandler {
@ExceptionHandler(MaxUploadSizeExceededException.class)
@ResponseBody
public String uploadException(MaxUploadSizeExceededException e) {
return "上传文件大小超出限制!";
}
}
添加全局数据 结合 @ModelAttribute
- @ControllerAdvice是一个全局数据处理组件,因此也可以在@ControllerAdvice中配置全局数据,使用@ModelAttribute注解进行配
- @ModelAttribute(value=“key”):方法的返回值是返回数据的value。此时在任意请求的 Controller 中,通过方法参数中的 Model 都可以获取info 的数据。
@ControllerAdvice
public class GlobalConfig {
@ModelAttribute(value = "info")
public Map<String, String> userInfo() {
HashMap<String, String> map = new HashMap<>();
map.put("username", "罗贯中");
map.put("gender", "男");
return map;
}
}
请求参数预处理 结合 @InitBinder
- @ControllerAdvice 结合 @InitBinder 还能实现请求参数预处理,即将表单中的数据绑定到实体类上时进行一些额外处理。
文件上传
Controller 文件上传方法参数通常为 MultipartFile 对象 和 HttpServletRequest 对象
- req 可以获取一些项目请求相关信息,例如 绝对路径 req.getSession().getServletContext().getRealPath(“/upload”);
- 一般是获取的Tomcat 发布的 target 目录下的地址
- MultipartFile 类:此类作为参数接收 客户端传输过来的 文件对象
- getOriginalFilename():获取文件名
- transforTo(File 或 Path):将接收到的文件传输到给定的目标文件。默认实现只是复制文件输入流
PostMapping("/upload")
public String upload(MultipartFile file, HttpServletRequest req) {
// 获取的Tomcat 发布的 target 目录下的地址
String realPath = req.getSession().getServletContext().getRealPath("/upload");
System.out.println(realPath);
File file1 = new File(realPath);
if (!file1.exists()) {
file1.mkdirs();
}
String filename = file.getOriginalFilename();
try {
// 上传
file.transferTo(Paths.get(realPath, filename));
} catch (IOException e) {
throw new RuntimeException(e);
}
return "/jsp/view";
}
- 前端 表单必须为 post 提交,且 enctype=“multipart/form-data”
- 若想要多个文件,可以 多个 input name 一样,或 添加 multiple 属性.
- 在接收的时候用 MultipartFile[] 即可。
- 若想要多个文件,可以 多个 input name 一样,或 添加 multiple 属性.
<form action="${pageContext.request.contextPath}/upload" method="post" enctype="multipart/form-data">
文件上传:<input type="file" name="upload"/>
</form>
文件上传配置
- StandardServletMultipartResolver:这个处理器兼容性差,它只适用于Servlet 3.0之后的版本,它不需要依赖第三方工具就可以实现件上传。
- CommonsMultipartResolver:这个处理器兼容性较好,可以兼容Servlet 3.0之前的版本,但是它需要依赖commons-fileupload这个第三方工具,所以要想使用这种方式,需要添加 commons-fileload 依赖。
一、StandardServletMultipartResolver
只适用于Servlet 3.0之后的版本,但也 不需要依赖第三方包
- 在 springmvc.xml 中配置 < bean>
- id=“multipartResolver” ,和之前那个方式一样的 id
- class=“org.springframework.web.multipart.support.StandardServletMultipartResolver”
<bean id="multipartResolver" class="org.springframework.web.multipart.support.StandardServletMultipartResolver"/>
-
注意: 这个 Bean 无法直接配置上传文件大小等限制,需要在web.xml文件中进行配置
- 即使不限制文件上传大小,也需要在这个文件中配置 multipart-config
-
web.xml 中 springmvc 下配置
注意:配置在 的末尾处,且此步骤不能省略,至少要有 < multipart-config> -
< multipart-config>
<!-- 配置 StandardServletMultipartResolver 文件上传 -->
<multipart-config>
<!--文件保存的临时目录,这个目录系统不会自动创建-->
<location>E:\\temp</location>
<!--上传单个文件大小-->
<max-file-size>1048576</max-file-size>
<!--上传的总文件大小-->
<max-request-size>1048576</max-request-size>
<!--内存中保存的文件大小-->
<file-size-threshold>4096</file-size-threshold>
</multipart-config>
二、CommonsMultipartResolver
可以兼容Servlet 3.0之前的版本,但是它需要依赖commons-fileupload这个第三方工具
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
- 在 springmvc.xml 中配置
- id=“multipartResolver”
- class=“org.springframework.web.multipart.commons.CommonsMultipartResolver”
- < property name=“” value=“”>
- defaultEncoding:默认编码;
- maxUploadSize:文件上传总大小;value=“1048576”
- maxUploadSizePerFile:上传的单个文件大小;value=“1048576”
- maxInMemorySize:内存中最大的数据量,超过这个数据量,数据就要开始往硬盘中写。;value=“4096”
- uploadTempDir:临时目录,超过 maxInHMemonySize 配置大小后,数据开始往临时目录写,等全部上传完后,再将数据合并到正式的文件上传目录。
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 默认编码 -->
<property name="defaultEncoding" value="UTF-8"/>
</bean>
MVC 的过滤器
CharacterEncodingFilter 转换器:能防止 Post 请求乱码
- 打开 web.xml 文件,添加 标签。
- < filter-name> characterFilter,可更改
- < filter-class> org.springframework.web.filter.CharacterEncodingFilter
- 字符编码过滤器类。将 Post 请求中的字符编码 转换成 目标字符集(< init-param>)。
- < init-param>
- < param-name> encoding
- < param-value> UTF-8
- 要让 < filter> 生效,还需要配置 映射地址
- < filter-name> characterFilter
- < url-pattern> /*:代表对所有的 URL 进行拦截。
<filter>
<filter-name>characterFilter</filter-name>
<!-- 字符编码过滤器类。将 Post 请求中的字符编码 转换成 目标字符集(<init-param>) -->
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<!-- 要让 filter 生效,还需要配置映射地址 -->
<filter-mapping>
<filter-name>characterFilter</filter-name>
<!-- "/*" 代表对所有的 URL 进行拦截 -->
<url-pattern>/*</url-pattern>
</filter-mapping>
拦截器
Spring MVC提供了拦截器功能,它的作用相当于Servlet中的过滤器,只不过拦截器的功能更为强大。
一、创建 自定义拦截器类
实现 HandlerInterceptor 接口
- 其 有 三个 方法 可以重写
- boolean preHandle():
- 调用时间: Controller 方法处理之前(也就是路径跳转之前);
- 执行顺序: 链式 Intercepter 情况下,Intercepter 按照声明的顺序一个接一个执行;
- 返回值: 返回值为 true,则继续执行,false 中断执行,生成时默认 false;
- 应用场景: 常用于强制登陆等操作。
- postHandle():
- 调用前提: preHandle 返回 true;
- 调用时间: Controller 方法处理完之后,DispatcherServlet 进行 视图的渲染之前
- 也就是说在这个方法中你可以对ModelAndView进行操作
- 执行顺序: 链式 Intercepter 情况下,Intercepter 按照声明的顺序 倒着执行。
- 备注: postHandle 虽然 post 打头,但 post、get 方法都能处理。
- afterCompletion():、
- 调用前提:preHandle 返回 true
- 调用时间:DispatcherServlet 进行视图的渲染之后
- 应用场景:多用于清理资源,统一日志处理,统一异常处理
public class TestInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return HandlerInterceptor.super.preHandle(request, response, handler);
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
Exception ex) throws Exception {
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
}
二、springmvc.xml 中 配置 强制登陆拦截器
- < mvc:interceptors>:强制登陆拦截器。
- < mvc:interceptor>:一个集合
- < mvc:mapping path=“”/>:'/**'表示拦截所有请求路径
- < bean class=“自定义的拦截器位置”/>
- < mvc:exclude-mapping path=“/manager/selectByName.do”/>:不拦截指定 URl
- < mvc:interceptor>:一个集合
<!--强制登陆拦截器-->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/> <!-- xml文件里 '/**'表示拦截所有请求路径 -->
<bean class="com.test01.util.MyHandlerInterceptor"/> <!-- 指定自定义拦截器 -->
<!--不拦截2-注册-->
<mvc:exclude-mapping path="/manager/insert.do"/>
</mvc:interceptor>
</mvc:interceptors>
请求处理流程
DispatcherServlet.java 就是封装了 HttpServlet,其中 doDispatch(req, resp) 方法是最为重要的方法,控制着整个流程的执行。
请求流程:
- 用户发送请求至前端控制器 DispatcherServlet
- DispatcherServlet 收到请求调用 HandlerMapping 处理器映射器。
- 处理器映射器根据请求 url 找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给 DispatcherServle。
- DispatcherServlet 通过 HandlerAdapter 处理器适配器 调用处理器
- 执行处理器 Controller,也叫后端控制器
- Controtler 执行完成返回 ModelAndView
- HandlerAdapter 将 Controller 执行结果 ModelAndView 返回给DispatcherServlet
- DispatcherServlet 将 ModaiAndView 传给 ViewReslover 视图解析器
- ViewRasiover 解析后返回具体 View
- DispatcherServlet 对 View 进行渲染视图(即将 模型数据 填充至视图中)
- DispatcherServlet 响应用户
组件介绍:
- DispatherServlet 前端控制器
- 它是整个流程控制的中心。用户请求到达前端控制器后,由它调用其它组件处理请求。它的作用是降低各组件之间的耦合度。
- HandlerMapping 处理器映射器
- HandlerMapping 负责根据用户请求查找Handler,也就是处理器,具体的表现形式可以是类,也可以是方法。
- 比如,标注了@RequestMapping 的每个方法都可以看成一个Handler。Handler负责实际的请求处理,在请求到达后,HandlerMapping 的作用便是找到请求相应的处理器 Handler和 Interceptor。
- Handler (即 Controller),是继 DispatcherServlet 的后端处理器
- 在 DispatcherServlet 的控制下,Handler 对具体的用户请求进行处理。由于 Handler涉及到具体的用户业务请求,所以一般情况需要开发人员根据具体的业务需求编写 Handler
- HandlerAdapter 适配器
- 因为 Spring MVC 中 Handler 可以是任意形式的,只要能够处理请求便可。但是把请求交给 Servlet 的时候,由于Servlet 的方法结构都是 doService (HttpServletRequest req,HittpServletResponse resp)形式的,要让固定的Servlet 处理方法调用 Handler 来进行处理,这一步工作便是 HandlerAdapter要做的事。
- ViewResolver 视图解析器
- 通常在Spring MVC的配置文件中,都会配上一个实现类来进行视图解析。这个组件的主要作用是将 String 类型的视图名和 Locale 解析为 View 类型的视图,只有一个 resolveViewName() 方法。
- 从方法的定义可以看出,Controller层返回的 String类型的视图名 viewName 最终会在这里被解析成为View。View是用来渲染页面的,也就是说,它会将程序返回的参数和数据填入模板中,生成 HTML 文件。ViewResover 在这个过程中主要做两件大事: ViewResover 会找 到渲染所用的模板 和 所用的技术 (第二件大事,其实也就是找到视图的类型,如JSP)并填入参数。默认情况下,SpringMVC会为我们自动配置一个InternalResourceViewResolver,是针对JSP类型视图的。
- HandlerExceptionResolver 处理Handler产生的异常情况的组件
- 具体来说,此组件的作用是根据异常设置 ModelAndView,之后交给渲染方法进行渲染,渲染方法会将 ModelAndView渲染成页面。
- 不过要注意,HandlerExceptionResolver 只用于解析对请求做处理阶段产生的异常,渲染阶段的异常不归它管,这也是Spring MVC组件设计的一大原则一分工明确、互不干涉。
- RequestToViewNameTranslator 请求中查找 ViewName
- 作用是从请求中查找 ViewName。因为 ViewResolver 根据 ViewName 查找 View,但有的 Handler 处理完成之后,没有设置 View,也没有设置 ViewName,便要通过这个组件来从请求中查找ViewName
- LocaleResolver 区域解析
- resolveViewName() 方法需要两个参数,一个是视图名,另一个就是Locale。
- 参数Locale是从哪来的呢?这就是 LocaleResolver 组件要做的事。LocaleResolver 用于从请求中解析出 Locale,比如在中国 Locale 当然就是zh-CN,用来表示一个区域。这个组件也是 i18n的基础。
- ThemeResolver 主题解析
- ThemeResover组件是用来解析主题的。主题就是样式、图片及它们所形成的显示效果的集合。
- Spring MVC 中一套主题对应一个 properties文件,里面存放着与当前主题相关的所有资源,如图片、CSS样式等。创建主题非常简单,只需准备好资源,然后新建一个 “主题名.properties” 并将资源设置进去,放在classpath下,之后便可以在页面中使用了。
静态资源
在Spring MVC中,静态资源默认都是被拦截的,例如 html、 js、css、jpg、 png、txt、pdf等,都是无法直接访问的。因为所有请求都被拦截了。所以,针对静态资源,需要做额外处理,处理方式很简单,直接在Spring MVC的配置文件中,添加配置;
最简单的就是:在 springmvc.xml 中 < mvc:default-servlet-handler/>:放行 静态资源(图片/JS/CSS等)。
在 springmvc.xml 中 指定放行文件:
- < mvc:resources mapping=“” location=“”/>
- location是资源在webapp下的绝对路径,mapping是在html中写的路径
<mvc:resources mapping="html/news.html" location="/html/"/>
<mc:resources mapping="/**" location="/"/>
或在 web.xml 中
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.js</url-pattern>
<url-pattern>/html/*</url-pattern>
<url-pattern>/css/</url-pattern>
</servlet-mapping>
服务器端数据校验
Maven
<!-- 数据校验,版本先不要更新 -->
dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.2.3.Final</version>
</dependency>
在 springmvc.xml 中配置
- 数据校验器
- id=“validatorFactoryBean”
- class=“org.springframework.validation.beanvalidation.LocalValidatorFactoryBean”>
- < property name=“providerClass” value=“org.hibernate.validator.HibernateValidator”/>
<bean id="validatorFactoryBean" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
<property name="providerClass" value="org.hibernate.validator.HibernateValidator"/>
</bean>
- 如果想要在 MVC 中使用,需要 在 Spring MVC 配置的 mvc:annotation-driven/ (启用 Spring MVC 的注解开发模式)中加入属性:
- validator=“validatorFactoryBean”:指定 数据校验器 bean id
<mvc:annotation-driven conversion-service="conversionService" validator="validatorFactoryBean"/>
在 controller 方法参数上增加 @Validated 注解,表示校验规则将会生效。
给参数类增加注解限制,常见的注解可以在 javax.validation.constraints 包中看到。
- 所有注解都能设置 message = “错误信息”
- @Max(10) @Min(1):限制数字的大小
- @PositiveOrZero 自然数, @Positive 正整数
- @NotBlank:限制字符串不能为 null 或 只包含空字符串 “”
- @Size():限制长度 @Size(min = 2, max = 5)
- @Email(regexp = “^\w+([-+.]\w+)@\w+([-.]\w+)\.\w+([-.]\w+)*$”):限制邮箱
- @NotEmpty:限制字符串、集合、map、数组等不能为空
- @NotNull:被注解的元素必须不为null
- @Null:被注解的元素必须为null
- @AssertTrue:被注解的元素必须为true@AssertFalse:被注解的元素必须为 false
- @DecimalMin(value):被注解的元素必须是一个数字,其值必须大于等于指定的最小值
- @DecimalMxa(value):被注解的元素必须是一个数字,其值必须小于等于指定的最大值
- @Degits(integer,fraction):被注解的元素必须是一个数字,其值必须在可接受- 的范围内
- @Past:被注解的元素必须是一个过去的日期
- @Future:被注解的元素必须是一个将来的日期
- @Pattern(regex=, flag=):被注解的元素必须符合指定的正则表达式
- @Length(min=, max=):被注解的字符串的大小必须在指定的范围内
- @Range(min=, max=, message=):被注解的元素必须在合适的范围内
数据回显
- 在校验发现错误时,会有个一个 BindingResult 对象
- 因此就可以判断 BindingResult对象是否为 null,若不为 null 说明未通过校验。
- 并且此时 将已提交的数据 返回给上一个页面,能够实现数据回显。
public String postMapping(@Validated User user, BindingResult res) {
if (res != null) {
List<ObjectError> allError = res.getAllErrors();
for (ObjectError error : allError)
System.out.println(error.getObjectName() + ":" + error.getDefaultMessage());
// 将已提交的数据 返回给上一个页面,能够实现数据回显
}
String ret = user.toString();
return ret;
}
可以在 resource 下定一个 properties 文件 存放 错误信息:
user.id.notnull=user id 不能为空
然后在springmvc.xml 中添加配置:
<bean id="bundleMessageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basenames" value="myMessage"/>
</bean>
在之前的 < bean id=“validatorFactoryBean”> 中添加
<property name="validationMessageSource" ref="bundleMessageSource"/>
RESTful
Fielding(一个超大牛)将他对互联网软件的架构原则,定名为REST,即Representational State Transfer(表现层状态转化)的缩写。如果一个架构符合REST 原则,就称为RESTful架构。
- 访问一个网站,就代表了客户端和服务器的一个互动过程。在这个过程中,势必涉及到数据和状态的变化。
- 互联网通信协议HTTP协议,是一个无状态协议。这意味着,所有的状态都保存在服务器端。因此,如果客户端想要操作服务器,必须通过某种手段,让服务器端发生"状态转化(State Transfer) "。而这种转化是建立在表现层之上的,所以就是"表现层状态转化。
- 客户端用到的手段,只能是HTTP协议。具体来说,就是HTTP协议里面,四个表示操作方式的动词:GET、POST、PUT、它们分别对应四种基本操作:
- GET:用来获取资源
- POST:用来新建资源(也可以用于更新资源)
- PUT:用来更新资源
- DELETE:用来删除资源
Spring MVC对 RESTful提供了非常全面的支持,主要有如下几个注解:
- @RestController:这是一个组合注解,可以用来代替 @Controller
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
public @interface RestController {}
- 请求方法中,提供了常见的请求方法:
- @PostMapping
- @GetMapping
- @PutMapping
- @DeleteMapping
- 默认情况下,Spring MVC 只支持 Get 和 Post,而 Put 和 Delete 请求都是通过 Get 来处理的。
- 解决步骤:
- 1、在 里把 method 改为 post,然后添加一个隐藏域:
- < input type=“hidden” name=“_method” value=“put或delete”/>
- 2、在 web.xml 中添加 HiddenHttpMethodFilter 过滤器
<!-- 使支持 RESTFul 风格,delete转换为标准的http delete方法 -->
<filter>
<filter-name>hiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>hiddenHttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
- @PathVariable(“”):提供请求地址参数,用于 url 传参模式,将 url 中的占位符 匹配至方法参数上(也可不写)
@GetMapping("user/{id}")
public String user(@PathVariable("id") int aa) {}
- 此时 url:http:localhost:8080/test01/1 ;这个 1 就是 id
中文乱码问题
- Tomcat默认使用字符集 ISO-8859-1,属于西欧字符集,不支持中文。
- 解决乱码的核心思路是将 IS0-8859-1 转换为 UTF-8 。
- Controller 中请求与响应 都需要设置 UTF-8 字符集。
解决方法:
- Get 请求乱码: Tomcat 8.0 以前版本 设置 server.xml 增加 URIEncoding 属性。
- 打开 server.xml 文件:E:\Java\apache-tomcat-8.5.59\conf\server.xml
- 大概60几行,找到 的标签。
- 再增加一个属性 :URIEncoding=“UTF-8”
- URI:就是网址部分的编码。
- 再增加一个属性 :URIEncoding=“UTF-8”
<Connector connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443" URIEncoding="UTF-8"/>
不过在 Tomcat 8.0 及以后版本,URIEncoding 默认就是 "UTF-8",因此不用写。
-
Post 请求乱码: web.xml 配置 CharacterEncodingFilter 转换器。
-
Response 响应乱码: Spring 配置 mvc 自带的 StringHttpMessageConverter 转换器。
-
控制台乱码,在Timcat的 VM options 加入中 -Dfile.encoding=UTF-8