SpringMVC记录
原视频
MVC 模型
SpringMVC 核心组件
DispatcherServlet
:前置控制器,是整个流程控制的核心,控制其他组件的执行,进行统一调度,降低组件之间的耦合性Handler
: 处理器,完成具体的业务逻辑,相当于 Servlet 或 ActionHandlerMapping
:DispatcherServlet
接收到请求之后,通过HandlerMappin
g 将不同的请求映射到不同的Handler
HandlerInterceptor
:处理器拦截器,是一个接口,如果需要完成一些拦截处理,可通过实现该接口来完成HandlerExecutionChain
:处理器执行链,包括两部分内容:Handler
和HandlerInterceptor
(系统会有一个默认的Handlerlnterceptor
,如果需要额外设置拦截,可以添加拦截器)HandlerAdapter
:处理器适配器,Handler
执行业务方法之前,需要进行一系列的操作,包括表单数据的验证、数据类型的转换、将表单数据封装到 JavaBean 等,这些操作都是由HandlerApater
来完成ModelAndView
:装载了模型数据和视图信息,作为 Handler 的处理结果,返回给 DispatcherServletViewResolver
:视图解析器,DispatcheServlet
通过它将逻辑视图解析为物理视图,最终将渲染结果响应给客户端
SpringMVC 工作流程
1)用户发送请求至前端控制器 DispatcherServlet。
2)DispatcherServlet 收到请求调用 HandlerMapping 处理器映射器。
3) 处理器映射器根据请求 url 找到具体的 Handler,生成处理器对象(handler)及处理器拦截器(handlerInterceptor)(如果有则生成)一并返回给 DispatcherServlet。
4) DispatcherServlet 通过 HandlerAdapter 处理器适配器调用处理器。
5) HandlerAdapter 执行handler(也叫后端控制器Controller)的方法。
6) Handler执行完成后返回 ModelAndView。
7) HandlerAdapter 将 handler 执行结果 ModelAndView 返回给 DispatcherServlet。
8) DispatcherServlet 将 ModelAndView 传给 ViewReslover 视图解析器。
9) ViewReslover 解析后返回具体 View 对象。
10) DispatcherServlet 对 View 进行渲染视图(即将模型数据填充至视图中)。
11) DispatcherServlet 响应用户。
创建 SpringMVC 的 maven 项目
1.添加依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>6.0.6</version>
</dependency>
2.在web.xml
中配置DispatcherServlet
,在resources
下新建一个springmvc.xml
文件用来注入 Bean
<servlet>
<servlet-name>dispatcherServlet</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>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
springmvc.xml
配置如下
<?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: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/context https://www.springframework.org/schema/context/spring-context.xsd">
<!--自动扫描-->
<!--扫描的包名和自己项目保持一致-->
<context:component-scan base-package="cc.bnblogs"></context:component-scan>
<!--配置视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
</beans>
3.配置 tomcat
SpringMVC 中文乱码问题
为了使用index.jsp
中的中文在网页中不乱码,需要在该文件开头添加
<%@ page contentType="text/html;charset=utf-8" %>
为了使Handler
输出到控制台的中文不乱码,需要在web.xml
中添加过滤器
<!--添加一个过滤器,避免终端乱码-->
<filter>
<filter-name>encodingFilter</filter-name>
<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-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
为了使得RestController
返回数据中的中文不乱码,在springmvc.xml
中添加消息转换器配置
<mvc:annotation-driven>
<!--消息转换器配置-->
<!--用于解决controller返回给前端的数据乱码的问题-->
<mvc:message-converters register-defaults="true">
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<property name="supportedMediaTypes" value="text/html;charset=UTF-8"/>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
集成fastJson
先导入依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.25</version>
</dependency>
在springMVC.xml
中进行配置
<mvc:annotation-driven>
<!--消息转换器配置-->
<!--用于解决controller返回给前端的数据乱码的问题-->
<mvc:message-converters register-defaults="true">
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<property name="supportedMediaTypes" value="text/html;charset=UTF-8"/>
</bean>
<bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>application/json;charset=UTF-8</value>
</list>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
编写 Controller 进行测试
/**
* 接收一个json对象,返回User对象
*/
@RequestMapping(value = "json",method = RequestMethod.POST)
public User getJson(@RequestBody User user) {
System.out.println("user = " + user);
return user;
}
SpringMVC 添加模型数据
常见内置对象
对象 | 类名 | 作用 |
---|---|---|
PageContext | javax.servlet.jsp.PageContext | jsp 的页面容器 |
request | javax.servlet.http.HttpServletrequest | 获取用户的请求信息 |
response | javax.servlet.http.HttpServletResponse | 服务器向客户端的响应信息 |
session | javax.servlet.http.HttpSession | 用来保存每一个用户的信息 |
application | javax.servlet.ServletContext | 保存当前项目的所有信息 |
将模型数据绑定到 request 对象
Map 类型
Handler
@RequestMapping("view")
public class ViewHandler {
@RequestMapping("map")
public String map(Map<String, User> userMap) {
User user = new User();
user.setId(18);
user.setName("张三");
userMap.put("user",user);
return "view";
}
}
view.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@page isELIgnored="false" %>
<%--注意添加上面这一行配置--%>
<html>
<head>
<title>Title</title>
</head>
<body>
${requestScope.user}
</body>
</html>
Model 类型
Handler
@RequestMapping("model")
public String model(Model model) {
User user = new User();
user.setId(18);
user.setName("张三");
model.addAttribute("user",user);
return "view";
}
view.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@page isELIgnored="false" %>
<%--注意添加上面这一行配置--%>
<html>
<head>
<title>Title</title>
</head>
<body>
${requestScope.user}
</body>
</html>
ModelAndView 类型
上面的都是直接将模型数据返回给 view,这里是两者同时返回
有多种方式可以设置 model 和 view,下面提供了 4 种实现方式
@RequestMapping("modelAndView")
public ModelAndView modelAndView() {
User user = new User();
user.setId(18);
user.setName("张三");
ModelAndView modelAndView = new ModelAndView();
// 添加model数据
modelAndView.addObject("user",user);
// 添加view
modelAndView.setViewName("view");
return modelAndView;
}
@RequestMapping("modelAndView1")
public ModelAndView modelAndView1() {
User user = new User();
user.setId(18);
user.setName("张三");
ModelAndView modelAndView = new ModelAndView();
// 添加model数据
modelAndView.addObject("user",user);
View view = new InternalResourceView("/view.jsp");
modelAndView.setView(view);
return modelAndView;
}
@RequestMapping("modelAndView2")
public ModelAndView modelAndView2() {
User user = new User();
user.setId(18);
user.setName("张三");
// 直接提供一个视图的名字
ModelAndView modelAndView = new ModelAndView("view");
modelAndView.addObject("user",user);
return modelAndView;
}
@RequestMapping("modelAndView3")
public ModelAndView modelAndView3() {
Map<String,User> userMap = new HashMap<String, User>();
User user = new User();
user.setId(18);
user.setName("张三");
userMap.put("user",user);
// 直接提供一个视图的名字
return new ModelAndView("view",userMap);
}
基于 Request 的方式
上面的方式本质上都是将模型数据添加到 request,也可以直接使用 request 来实现
添加依赖
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
Handler
@RequestMapping("request")
public String request(HttpServletRequest request) {
User user = new User();
user.setId(18);
user.setName("张三");
request.setAttribute("user",user);
return "view";
}
使用@ModelAttribute 注解
- 定义一个方法,该方法专门用来返回要填充到模型数据中的对象。
- 注意:无论执行哪个具体的业务方法,都会先执行有
@ModelAttribute
注解的方法
@ModelAttribute
public User getUser() {
User user = new User();
user.setId(18);
user.setName("李四");
return user;
}
- 业务方法中无需再处理模型数据,只需返回视图即可。
@RequestMapping("modelAttr")
public String getUserAttribute(){
return "view";
}
将模型数据绑定到 session 对象
- 基于session的方式
@RequestMapping("session")
public String session(HttpServletRequest request) {
HttpSession httpSession = request.getSession();
User user = new User();
user.setId(18);
user.setName("李四");
httpSession.setAttribute("user",user);
return "view";
}
将模型数据绑定到 application 对象
@RequestMapping("application")
public String application(HttpServletRequest request) {
ServletContext application = request.getServletContext();
User user = new User();
user.setId(18);
user.setName("李四");
application.setAttribute("user",user);
return "view";
}
SpringMVC 自定义数据转换器
HandlerAdapter 默认并不提供 String 到 Date 类型的数据转换
但是可以自己创建一个String
到Date
的数据转换器,需要实现 Converter 接口
public class DateConverter implements Converter<String, Date> {
private final String pattern;
public DateConverter(String pattern) {
this.pattern = pattern;
}
public Date convert(String s) {
SimpleDateFormat format = new SimpleDateFormat(pattern);
Date date = null;
try {
date = format.parse(s);
} catch (ParseException e) {
throw new RuntimeException(e);
}
return date;
}
}
注入 Bean
<!--配置自定义转换器-->
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<list>
<!--配置日期转换器-->
<bean class="cc.bnblogs.converter.DateConverter">
<constructor-arg type="java.lang.String" value="yyyy-MM-dd"/>
</bean>
</list>
</property>
</bean>
<mvc:annotation-driven conversion-service="conversionService">
<!--消息转换器配置-->
<!--用于解决controller返回给前端的数据乱码的问题-->
<mvc:message-converters register-defaults="true">
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<property name="supportedMediaTypes" value="text/html;charset=UTF-8"/>
</bean>
<bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>application/json;charset=UTF-8</value>
</list>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
ConvertHandler
@RestController
@RequestMapping("convert")
public class ConvertHandler {
@RequestMapping(value = "date",method = RequestMethod.POST)
public String getDate(Date date) {
return date.toString();
}
}
SpringMVC 文件上传下载
单文件上传
导入依赖
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.11.0</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.5</version>
</dependency>
springmvc.xml 配置
<!--配置文件组件-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
</bean>
web.xml
放行图片资源
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.png</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.jpg</url-pattern>
</servlet-mapping>
upload.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page isELIgnored="false" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="/file/upload" method="post" enctype="multipart/form-data">
<input type="file" name="img" />
<hr/>
<input type="submit" value="上传">
<img src="${requestScope.path}">
</form>
</body>
</html>
fileHandler
@PostMapping("upload")
public String imgUpload(MultipartFile img, HttpServletRequest request) throws IOException {
if (!img.isEmpty()) {
// 保存路径: D:\apache-tomcat-9.0.73\webapps\ROOT\files
// 每次重启tomcat后会删掉
String path = request.getServletContext().getRealPath("files");
// 获取上传文件的文件名
String name= img.getOriginalFilename();
// 存到tomcat安装目录下的bin文件夹下的files文件夹
File file = new File(path,name);
// 保存内容到文件中
img.transferTo(file);
// 保存到request中用于回显
request.setAttribute("path","/files/" + name);
}
return "upload";
}
多文件上传
添加依赖
<!--jstl-->
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!--taglibs-->
<dependency>
<groupId>org.apache.taglibs</groupId>
<artifactId>taglibs-standard-impl</artifactId>
<version>1.2.5</version>
<scope>runtime</scope>
</dependency>
fileHandler
@PostMapping("uploads")
public String uploadImages(MultipartFile[] images,HttpServletRequest request) throws IOException {
List<String> paths = new ArrayList<String>();
for (MultipartFile img: images) {
if (!img.isEmpty()) {
// 保存路径: D:\apache-tomcat-9.0.73\webapps\ROOT\files
// 每次重启tomcat后会删掉
String path = request.getServletContext().getRealPath("files");
// 获取上传文件的文件名
String name= img.getOriginalFilename();
// 存到tomcat安装目录下的bin文件夹下的files文件夹
File file = new File(path,name);
// 保存内容到文件中
img.transferTo(file);
// 保存到request中用于回显
paths.add("/files/"+name);
}
}
request.setAttribute("path",paths);
return "uploads";
}
uploads.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page isELIgnored="false" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="/file/uploads" method="post" enctype="multipart/form-data">
file1: <input type="file" name="images" /> <br/>
file2: <input type="file" name="images" /> <br/>
file3: <input type="file" name="images" /> <br/>
<input type="submit" value="上传">
</form>
<c:forEach items="${requestScope.path}" var="file">
<img src="${file}" width="300px">
</c:forEach>
</body>
</html>
SpringMVC数据校验
基于validator接口
创建一个实体类Account
@Data
public class Account {
private String username;
private String password;
}
创建一个自定义验证器AccountValidator
,实现Validator
接口
这里简单校验字段是否为空或者全是空格
public class AccountValidator implements Validator {
/**
* 是否支持对该数据类型进行验证
* @param aClass
* @return
*/
public boolean supports(Class<?> aClass) {
return aClass.equals(Account.class);
}
/**
* 验证的具体逻辑
* @param o
* @param errors
*/
public void validate(Object o, Errors errors) {
// 校验用户名
ValidationUtils.rejectIfEmptyOrWhitespace(errors,"username",null,"用户名不能为空或空格");
// 校验密码
ValidationUtils.rejectIfEmptyOrWhitespace(errors,"password",null,"密码不能为空或空格");
}
}
springmvc.xml
配置
<!--注入自定义验证器到IOC容器-->
<bean id="accountValidator" class="cc.bnblogs.validator.AccountValidator" />
<!--在MVC注册自定义验证器-->
<mvc:annotation-driven validator="accountValidator"/>
ValidatorHandler
@Controller
@RequestMapping("validator")
public class ValidatorHandler {
@GetMapping("login")
public String login(Model model) {
// 绑定一个空数据,便于测试jsp
model.addAttribute("account",new Account());
return "login";
}
@PostMapping("/login")
public String login(@Validated Account account, BindingResult bindingResult) {
// 表单校验失败,返回登录页面
if (bindingResult.hasErrors()) {
return "login";
}
return "index";
}
}
login.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page isELIgnored="false" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form:form modelAttribute="account" action="/validator/login" method="post">
姓名: <form:input path="username"/> <form:errors path="username"/><br />
密码: <form:input path="password"/> <form:errors path="password"/><br />
<input type="submit" value="登录">
</form:form>
</body>
</html>
表单验证
基于Annotation JSR303
导入依赖
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.23.Final</version>
</dependency>
springmvc.xml
添加配置
<mvc:annotation-driven></mvc:annotation-driven>
通过注解的方式直接在实体类中添加相关的验证规则。
@Data
public class Person {
@NotEmpty(message = "用户名不能为空")
private String username;
@Size(min = 6,max=12,message = "密码必须为6-12位")
private String password;
@Email(regexp = "^[A-Za-z0-9\u4e00-\u9fa5]+@[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(?:\\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+$",message = "请输入正确的邮箱")
private String email;
@Pattern(regexp = "^(?:(?:\\+|00)86)?1(?:(?:3[\\d])|(?:4[5-79])|\n" +
"(?:5[0-35-9])|(?:6[5-7])|(?:7[0-8])|\n" +
"(?:8[\\d])|(?:9[189]))\\d{8}$",message = "请输入正确的电话")
private String phone;
}
ValidatorHandler
@GetMapping("person")
public String person(Model model) {
model.addAttribute("person",new Person());
return "person";
}
@PostMapping("/person")
public String person(@Valid Person person, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
return "person";
}
return "index";
}
person.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page isELIgnored="false" %>
<%@taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form:form modelAttribute="person" action="/validator/person" method="post">
姓名: <form:input path="username"/> <form:errors path="username"/><br />
密码: <form:password path="password"/> <form:errors path="password"/><br />
邮箱: <form:input path="email"/> <form:errors path="email"/><br />
电话: <form:input path="phone"/> <form:errors path="phone"/><br />
<input type="submit" value="提交">
</form:form>
</body>
</html>
表单验证