Spring MVC
简介
- M:Model,模型层,指工程中的JavaBean,作用为处理数据
- V:View,视图层,指html或jsp页面,作用是与用户进行交互
- C:Controller,控制层,指servlet,作用是接收请求和响应浏览器
工作流程
用户通过视图层发送请求到服务器,在服务器中请求被Controller接收,Controller调用响应的Model层处理请求,处理完毕后将结果返回到Controller,Controller再根据请求处理的结果找到响应的View视图,渲染数据后最终响应给浏览器
@RequestMapping注解
@RequestMapping注解功能
将请求和处理请求的控制器方法关联起来,建立映射关系
@RequestMapping注解的位置
- 标识一个类:设置映射请求路径的初始信息
- 标识一个方法:设置映射请求路径的具体信息
@RequestMapping注解的value属性
- value:String[] :设置请求路径,必须设置
- method:RequestMethod[] :设置请求方式(GET或POST),无法匹配报错405
params属性
属性值为字符串数组,请求必须携带所有声明的参数,参数前有!表示不能有该参数,若请求不满足要求报错400
headers属性
同params,报错404
ant风格路径 - 模糊匹配
- ? :表示任意单个字符
- * :表示任意0或多个字符
- ** :表示任意的一层或多层目录
派生注解
- @GetMapping
- @PostMapping
- @PutMapping
- @DeleteMapping
SpringMvc支持路径中的占位符
原始方式:/deleteUser?id=1
rest方式:/deleteUser/1
@RequestMapping("/testPath/{id}")
public String testPath(@PathVarible("id") Integer id){
System.out.println("id:" + id)
return "success";
}
SpringMVC获取请求参数
- 通过ServletAPI获取
将HttpServletRequest作为控制器方法的形参,此时HttpServletRequest类型的参数封装了当前请求的请求对象
- 通过控制器方法的形参获取参数
将请求参数名作为控制器方法的形参,此时会自动为参数赋值
存在多个值时,每个值以,作为分隔符,也可以作为数组
@RequestMapping("/test")
public String test(String username){
System.out.println("username:" + username);
return "success";
}
- @RequestParam
将请求参数和控制器方法的形参创建映射关系
@RequestParam注解一共有三个属性:
value:指定为形参赋值的请求参数名
required:设置是否必须传输此请求参数,默认为true
defaultValue:当value所指定的请求参数没有传输时,指定默认值
@PathVarible
@RequestHeader
将请求头信息和控制器方法的形参创建映射关系,三个属性同@RequestHeader
- @CookieValue
将cookie数据和控制器方法的形参创建映射关系,三个属性同@RequestParam
- 通过POJO获取请求参数
自动装配实体类对象,在控制器方法的形参设置一个实体类类型的形参,若浏览器传输的请求参数的参数名和实体类中的属性名一致,那么请求参数就会为此属性赋值
通过CharacterEncodingFilter处理请求参数乱码
<!-- web.xml -->
<filter>
<filter-name>CharacterEncodingFilter</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>
<!-- 设置响应的编码 -->
<init-param>
<param-name>forceResponseEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
域对象共享数据
- 通过servletAPI向request域对象共享数据
@RequestMapping("/testServletAPI")
public String testServletAPI(HttpServletRequest req){
req.setAttribute("key","value");
return "success";
}
- 使用ModelAndView向request域对象共享数据
@RequestMapping("/testModelAndView")
public ModelAndView testModelAndView(){
/*
* ModelAndView有Model和View功能
* Model用于向请求域共享数据
* View用于设置视图,实现页面跳转
*/
ModelAndView mav = new ModelAndView();
// 处理模型数据,即向请求域request共享数据
mav.addObject("key","value");
// 设置视图名称
mav.setViewName("success");
return mav;
}
- 使用Model向request域对象共享数据
@RequestMapping("/testModel")
public String testModel(Model model){
model.addAttribute("key","value");
return "success";
}
- 使用Map向request域对象共享数据
@RequestMapping("/testMap")
public String testMap(Map<String,Object> map){
map.put("key","value");
return "success";
}
- 使用ModelMap向request域对象共享数据
@RequestMapping("/testModelMap")
public String testModelMap(ModelMap modelMap){
model.addAttribute("key","value");
return "success";
}
- 向session域共享数据
HttpSession 对象来存储和检索来自同一个客户端的多个HTTP请求中的数据
@RequestMapping("/testSession")
public String testSession(HttpSession session){
session.addAttribute("key","value");
value = session.getAttribute("key");
// 清除session
session.invalidate();
return "success";
}
- RedirectAttributes
在两个不同请求之间传参,用于重定向传参
@Controller
public class MyController {
@RequestMapping("/myPage")
public String myPage(RedirectAttributes redirectAttributes) {
String message = "Hello, world!";
redirectAttributes.addFlashAttribute("message", message);
return "redirect:/nextPage";
}
}
- 向application域(ServletContext)共享数据
ServletContext是一个全局对象,可以被Web应用程序中的所有Servlet和JSP页面访问。它提供了一种在应用程序的不同部分之间共享数据和资源的方法,它通常被用于配置和管理任务,被多个用户共享
@RequestMapping("/testApplication")
public String testApplication(HttpSession session){
ServletContext application = session.getServletContext();
application.addAttribute("key","value");
return "success";
}
SpringMVC的视图
SpringMVC的视图是View接口,作用为渲染数据,将模型Model中的数据展示给用户
默认有转发视图InternalResourceView和重定向视图RedirectView
ThymeleafView
当控制器方法中所设置的名称没有任何前缀时,此时的视图名称会被SpirngMvc配置文件中配置的视图解析器解析,视图名称拼接视图前缀和视图后缀得到最终路径,通过转发方式跳转
- 转发视图
SpringMVC默认转发视图为InternalResourceView
return "forward:/path";
- 重定向视图
SpringMVC默认重定向视图RedirectView
return "redirect:/";
转发和重定向的区别
重定向,服务器传递给客户端一个带有url的response,让客户端对这个url发起请求
转发,服务器将这个请求在服务器内部进行传递
- 请求次数:转发请求一次,重定向请求两次
- 地址栏:转发地址栏不变,重定向地址栏改变
- 数据共享:转发共享数据,重定向不共享数据
- 跳转限制:转发只能跳转本站资源,重定向可以跳转到任意url
- 发生行为:转发是服务端行为,重定向是客户端行为
视图控制器 - view-controller
当控制器方法中,仅仅用来实现页面跳转,即只需要设置视图名称时,可以将处理器方法使用view-controller表示
<!--
path: 设置处理的请求地址
view-name: 设置请求地址所对应的视图名称
-->
<mvc:view-controller path="" view-name=""></mvc:view-controller>
RESTFul
RESTFul简介
REST:Representational State Transfer 表现层状态转移
RESTFul的实现
相同请求路径,不同请求方式表示不同操作
- GET:获取资源
- POST:新建资源
- PUT:更新资源
- DELETE:删除资源
HiddenHttpMethodFilter处理PUT和DELETE请求
<from method="post">
<input type="hidden" name="_method" value="put">
</from>
<!--开放对静态资源的访问-->
<mvc:default-servlet-handler />
<!--开启mvc注解驱动-->
<mvc:annotation-driven></mvc:annotation-driven>
HttpMessageConverter
报文信息转换器,将请求报文转换为Java对象,或将Java对象转换为响应报文
HttpMessageConverter提供了两个注解和两个类型:@RequestBody,@ResponseBody,RequestEntity,ResponseEntity
@RequestBody
获取请求体,在控制器方法设置一个形参,使用@RequestBody标注,当前请求体就会为当前注解所标识的形参赋值
@RequestBody String requestBody
RequestEntity
封装请求报文的一种类型,需要在控制器方法的形参中设置该类型的形参,当前请求报文就会赋值给该形参,可以用getHeaders()获取请求头信息,通过getBody()获取请求体信息
RequestEntity<String> requestEntity
@ResponseBody
用于标识一个控制器方法,将该方法的返回值直接作为响应报文的响应体响应到浏览器
// 通过HttpServletResponse响应浏览器数据
@RequestMapping("/testResponse")
public void testResponse(HttpServletResponse resp){
resp.getWriter().print("hello,response");
}
@RequestMapping("/testResponseBody")
@ResponseBody
public String testResponseBody(){
return "success";
}
SpringMvc处理json
- 导入jackson依赖
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
- 开启mvc注解驱动
<mvc:annotation-driven />
- 处理器方法上用@ResponseBody标识
- 将Java对象作为返回值返回
SpringMvc处理Ajax
@RestController
相当于在类上添加@RestController并为每个方法添加@ResponseBody注解
ResponseEntity
用于控制器方法的返回值类型,相当于自定义响应报文
文件上传和下载
文件下载
// 使用ResponseEntity实现下载文件的功能
@RequestMapping("/testDown")
public ResponseEntity<byte[]> testResponseEntity(HttpSession session){
// 获取ServletContext对象
ServletContext servletContext = session.getServletContext();
// 获取服务器种文件的真实路径
String realPath = servletContext.getRealPath("/static/img/1.jpg");
// 创建输入流
InputStream is = new FileInputStream(realPath);
// 创建字节数组
byte[] bytes = new byte[is.available()];
// 将流读到字节数组中
is.read(bytes);
// 创建HttpHeaders对象设置响应头信息
MultiValueMap<String,String> headers = new HttpHeaders();
// 设置下载方式和文件名
headers.add("Content-Disposition","attachment;filename=1.jpg");
// 设置响应状态码
HttpStatus statusCode = HttpStatus.OK;
// 创建ResponseEntity对象
ResponseEntity<byte[]> responseEntity = new ResponseEntity<>(bytes,headers,statusCode);
// 关闭输入流
is.close();
return responseEntity;
}
文件上传
引入依赖
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
</dependency>
配置文件上传解析器
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"></bean>
代码实现
<form th:action="@{/testUp}" method="post" enctype="multipart/form-data">
<input type="file" name="photo">
<input type="submit" value="上传">
</form>
@RequestMapping("/testUp")
public String testUp(MultipartFile photo,HttpSession session){
// 获取上传的文件的文件名
String fileName = photo.getOriginalFilename();
// 获取上传的文件的后缀名
String suffixName = fileName.substring(fileName.lastIndexOf("."));
// 将UUID作为文件名
String uuid = UUID.randomUUID().toString();
// 将uuid和后缀名拼接后的结果作为最终文件名
fileName = uuid + suffixName;
// 通过ServletContext获取服务器中photo目录的路径
ServletContext servletContext = session.getServletContext();
String photoPath = servletContext.getRealPath("photo");
File file = new File(photoPath);
// 判断photoPath对应路径是否存在
if(!file.exists()){
// 若不存在,则创建目录
file.mkdir();
}
String finalPath = photoPath + File.separator + fileName;
photo.transferTo(new File(finalPath));
return "success";
}
拦截器
SpringMVC拦截器用于拦截控制器方法的执行,拦截器需要实现HandlerInterceptor或者继承HandlerIntercepterAdapter类
拦截器的配置
SpiringMVC的拦截器必须在SpirngMVC的配置文件中配置
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="" />
<mvc:exclude-mapping path="" />
<!-- 第一种方式 -->
<!--<bean class="com.xxx.interceptor"></bean>-->
<!-- 第二种方式 -->
<!--<ref bean="interceptor"></ref>-->
</mvc:interceptor>
</mvc:interceptors>
拦截器的抽象方法
preHandle:
控制器方法执行之前执行,boolean返回值表示拦截或放行,true为放行
postHandle:
控制器方法执行后执行
afterComplation:
处理完视图和模型数据,渲染完毕后执行
拦截器执行顺序
- 若每个拦截器的preHandle()都返回true 此时preHandle()按照配置的顺序执行,而postHandle()和afterComplation()反序执行
- 若每个拦截器的preHandle()都返回false preHandle()返回false和它之前的拦截器的preHandle()都会执行,postHandle()都不执行,返回false之前的拦截器的afterComplation()会执行
SpringMVC执行流程
SpringMVC常用组件
- DispatcherServlet:前端控制器,由框架提供
- 作用:统一处理请求和响应,整个流程控制的中心,由它调用其他组件处理用户的请求
- HandlerMapping:处理映射器,框架提供
- 作用:根据请求的url,method查找Handler,即控制器方法
- Handler:处理器,工程师开发
- 作用:在DispatcherServlet的控制下,对具体的用户请求进行处理
- HandlerAdapter:处理器适配器,框架提供
- 作用:通过HandlerAdapter()对处理器进行执行
- ViewResolver:视图解析器,框架提供
- 作用:进行视图解析,得到相应的视图,如ThymeleafView等
- View:视图,框架提供
- 作用:将模型数据通过页面展示给用户 ###DispatcherServlet初始化流程
SpringMVC的执行流程
- 用户向服务器发送请求,请求被SpringMVC前端控制器DispatcherServlet捕获
- DispatcherServlet对请求url解析,得到请求资源标识符(uri),判断请求uri对应的映射
- 不存在
- 判断是否配置了mvc:default-servlet-handler
- 如果没配置,客户端报错404
- 如果有配置,则访问目标资源,找不到报错404
- 存在
- 根据URI,调用HandlerMapping获得该Handler配置的所有相关的对象(Handler对象以及相关拦截器),最后以HandlerExecutionChain执行链对象的形式返回
- DispatcherServlet根据Handler,选择一个合适的HandlerAdapter
- 如果成功获得HandlerAdapter,执行拦截器的preHandler()
- 提取Request中的模型数据,填充Handler形参,开始执行Handler(Controller)方法,处理请求
- 填充过程中,根据配置,Spring将做一些额外工作
- HttpMessageConveter:将请求消息(如Json,xml等数据)转换成一个对象,将对象转换为指定的响应信息
- 数据转换:对请求消息进行数据转换
- 数据格式化:对请求消息进行数据格式化
- 数据验证:验证数据的有效性(长度,格式),验证结果存储到BindingResult或Error中
- 填充过程中,根据配置,Spring将做一些额外工作
- Handler执行完成后,向DispatcherServlet返回一个ModelAndView对象
- 此时开始执行拦截器的postHandle()方法
- 根据返回的ModelAndView(此时会判断异常,并进行处理),选择一个合适的ViewResolver进行视图解析,根据Model和View渲染视图
- 渲染视图完毕后执行拦截器的afterCompletion()方法
- 将渲染结果返回给客户端
- 不存在