Spring MVC 一

  • Spring MVC应用
    • 附 mvc web项目搭建
    • 第⼀部分 Spring MVC 应⽤
      • 第 1 节 Spring MVC 简介
        • 1.1 MVC 体系结构
        • 1.2 Spring MVC 是什么?
      • 第 2 节 Spring Web MVC ⼯作流程
        • 2.1 Spring MVC 请求处理流程
        • 2.2 Spring MVC 九⼤组件
      • 第 3 节 请求参数绑定(串讲)
      • 第 4 节 对 Restful ⻛格请求⽀持
      • 第 5 节 Ajax Json交互
    • 第⼆部分 Spring MVC ⾼级技术
      • 第 1 节 拦截器(Inteceptor)使⽤
        • 1.1 监听器、过滤器和拦截器对⽐
        • 1.2 拦截器的执⾏流程
        • 1.3 多个拦截器的执⾏流程
      • 第 2 节 处理multipart形式的数据
      • 第 3 节 在控制器中处理异常
      • 第 4 节 基于Flash属性的跨重定向请求数据传递
    • 第三部分 ⼿写 MVC 框架

Spring MVC应用

附 mvc web项目搭建

1.创建空工程然后新建新的maven module

2.引入相关jar包

<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.z.pt</groupId><artifactId>mvc-java</artifactId><version>1.0-SNAPSHOT</version><packaging>war</packaging><name>mvc-java Maven Webapp</name><!-- FIXME change it to the project's website --><url>http://www.example.com</url><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><maven.compiler.source>11</maven.compiler.source><maven.compiler.target>11</maven.compiler.target></properties><dependencies><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>test</scope></dependency><!--引入spring webmvc的依赖--><dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>5.1.12.RELEASE</version></dependency><!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api --><dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>3.1.0</version><scope>provided</scope></dependency><!--json数据交互所需jar,start--><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-core</artifactId><version>2.9.0</version></dependency><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.9.0</version></dependency><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-annotations</artifactId><version>2.9.0</version></dependency><!--json数据交互所需jar,end--><!--文件上传所需坐标--><dependency><groupId>commons-fileupload</groupId><artifactId>commons-fileupload</artifactId><version>1.3.1</version></dependency></dependencies><build><plugins><plugin><groupId>org.apache.tomcat.maven</groupId><artifactId>tomcat7-maven-plugin</artifactId><version>2.2</version><configuration><port>8080</port><path>/</path></configuration></plugin></plugins></build></project>

3.修改web.xml

<!DOCTYPE web-app PUBLIC"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN""http://java.sun.com/dtd/web-app_2_3.dtd" ><web-app><display-name>Archetype Created Web Application</display-name><!--springmvc提供的针对post请求的编码过滤器--><filter><filter-name>encoding</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><!--配置springmvc请求方式转换过滤器,会检查请求参数中是否有_method参数,如果有就按照指定的请求方式进行转换--><filter><filter-name>hiddenHttpMethodFilter</filter-name><filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class></filter><filter-mapping><filter-name>encoding</filter-name><url-pattern>/*</url-pattern></filter-mapping><filter-mapping><filter-name>hiddenHttpMethodFilter</filter-name><url-pattern>/*</url-pattern></filter-mapping><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></servlet><servlet-mapping><servlet-name>springmvc</servlet-name><!--方式一:带后缀,比如*.action  *.do *.aaa该种方式比较精确、方便,在以前和现在企业中都有很大的使用比例方式二:/ 不会拦截 .jsp,但是会拦截.html等静态资源(静态资源:除了servlet和jsp之外的js、css、png等)为什么配置为/ 会拦截静态资源???因为tomcat容器中有一个web.xml(父),你的项目中也有一个web.xml(子),是一个继承关系父web.xml中有一个DefaultServlet,  url-pattern 是一个 /此时我们自己的web.xml中也配置了一个 / ,覆写了父web.xml的配置为什么不拦截.jsp呢?因为父web.xml中有一个JspServlet,这个servlet拦截.jsp文件,而我们并没有覆写这个配置,所以springmvc此时不拦截jsp,jsp的处理交给了tomcat如何解决/拦截静态资源这件事?方式三:/* 拦截所有,包括.jsp--><!--拦截匹配规则的url请求,进入springmvc框架处理--><url-pattern>/</url-pattern></servlet-mapping>
</web-app>

4.编写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"xmlns:mvc="http://www.springframework.org/schema/mvc"xsi:schemaLocation="http://www.springframework.org/schema/beanshttps://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttps://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/mvchttps://www.springframework.org/schema/mvc/spring-mvc.xsd
"><!--开启controller扫描--><context:component-scan base-package="com.z.pt"/><!--配置springmvc的视图解析器--><bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"><property name="prefix" value="/WEB-INF/jsp/"/><property name="suffix" value=".jsp"/></bean><!--自动注册最合适的处理器映射器,处理器适配器(调用handler方法)--><mvc:annotation-driven conversion-service="conversionServiceBean"/><!--注册自定义类型转换器--><bean id="conversionServiceBean" class="org.springframework.format.support.FormattingConversionServiceFactoryBean"><property name="converters"><set><bean class="com.z.pt.converter.DateConverter"></bean></set></property></bean><!--静态资源配置,方案一--><!--原理:添加该标签配置之后,会在SpringMVC上下文中定义一个DefaultServletHttpRequestHandler对象这个对象如同一个检查人员,对进入DispatcherServlet的url请求进行过滤筛查,如果发现是一个静态资源请求那么会把请求转由web应用服务器(tomcat)默认的DefaultServlet来处理,如果不是静态资源请求,那么继续由SpringMVC框架处理--><!--<mvc:default-servlet-handler/>--><!--静态资源配置,方案二,SpringMVC框架自己处理静态资源mapping:约定的静态资源的url规则location:指定的静态资源的存放位置--><mvc:resources location="classpath:/"  mapping="/resources/**"/><mvc:resources location="/WEB-INF/js/" mapping="/js/**"/><mvc:interceptors><!--拦截所有handler--><!--<bean class="com.lagou.edu.interceptor.MyIntercepter01"/>--><mvc:interceptor><!--配置当前拦截器的url拦截规则,**代表当前目录下及其子目录下的所有url--><mvc:mapping path="/**"/><!--exclude-mapping可以在mapping的基础上排除一些url拦截--><!--<mvc:exclude-mapping path="/demo/**"/>--><bean class="com.z.pt.interceptor.MyIntercepter01"/></mvc:interceptor><mvc:interceptor><mvc:mapping path="/**"/><bean class="com.z.pt.interceptor.MyIntercepter02"/></mvc:interceptor></mvc:interceptors><!--多元素解析器id固定为multipartResolver--><bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"><!--设置上传文件大小上限,单位是字节,-1代表没有限制也是默认的--><property name="maxUploadSize" value="5000000"/></bean></beans>

5.写控制层代码

package com.z.pt.controller;import com.z.pt.pojo.QueryVo;
import com.z.pt.pojo.User;
import org.springframework.http.RequestEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.FlashMap;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import org.springframework.web.servlet.support.RequestContextUtils;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import java.util.UUID;/*** @author**/
@Controller
@RequestMapping("/demo")
public class DemoController  {// SpringMVC的异常处理机制(异常处理器)// 注意:写在这里只会对当前controller类生效/* @ExceptionHandler(ArithmeticException.class)public void handleException(ArithmeticException exception,HttpServletResponse response) {// 异常处理逻辑try {response.getWriter().write(exception.getMessage());} catch (IOException e) {e.printStackTrace();}}*//*** url: http://localhost:8080/demo/handle01*/@RequestMapping("/handle01")public ModelAndView handle01(@ModelAttribute("name") String name) {//int c = 1/0;Date date = new Date();// 服务器时间// 返回服务器时间到前端页面// 封装了数据和页面信息的 ModelAndViewModelAndView modelAndView = new ModelAndView();// addObject 其实是向请求域中request.setAttribute("date",date);modelAndView.addObject("date",date);// 视图信息(封装跳转的页面信息) 逻辑视图名modelAndView.setViewName("success");return modelAndView;}/*** SpringMVC在handler方法上传入Map、Model和ModelMap参数,并向这些参数中保存数据(放入到请求域),都可以在页面获取到** 它们之间是什么关系?* 运行时的具体类型都是BindingAwareModelMap,相当于给BindingAwareModelMap中保存的数据都会放在请求域中**  Map(jdk中的接口)        Model(spring的接口)**  ModelMap(class,实现Map接口)*****              BindingAwareModelMap继承了ExtendedModelMap,ExtendedModelMap继承了ModelMap,实现了Model接口**//*** 直接声明形参ModelMap,封装数据* url: http://localhost:8080/demo/handle11** =================modelmap:class org.springframework.validation.support.BindingAwareModelMap*/@RequestMapping("/handle11")public String handle11(ModelMap modelMap) {Date date = new Date();// 服务器时间modelMap.addAttribute("date",date);System.out.println("=================modelmap:" + modelMap.getClass());return "success";}/*** 直接声明形参Model,封装数据* url: http://localhost:8080/demo/handle12* =================model:class org.springframework.validation.support.BindingAwareModelMap*/@RequestMapping("/handle12")public String handle12(Model model) {Date date = new Date();model.addAttribute("date",date);System.out.println("=================model:" + model.getClass());return "success";}/*** 直接声明形参Map集合,封装数据* url: http://localhost:8080/demo/handle13* =================map:class org.springframework.validation.support.BindingAwareModelMap*/@RequestMapping("/handle13")public String handle13(Map<String,Object> map) {Date date = new Date();map.put("date",date);System.out.println("=================map:" + map.getClass());return "success";}/**** SpringMVC 对原生servlet api的支持  url:/demo/handle02?id=1** 如果要在SpringMVC中使用servlet原生对象,比如HttpServletRequest\HttpServletResponse\HttpSession,直接在Handler方法形参中声明使用即可**/@RequestMapping("/handle02")public ModelAndView handle02(HttpServletRequest request, HttpServletResponse response,HttpSession session) {String id = request.getParameter("id");Date date = new Date();ModelAndView modelAndView = new ModelAndView();modelAndView.addObject("date",date);modelAndView.setViewName("success");return modelAndView;}/** SpringMVC 接收简单数据类型参数  url:/demo/handle03?id=1** 注意:接收简单数据类型参数,直接在handler方法的形参中声明即可,框架会取出参数值然后绑定到对应参数上* 要求:传递的参数名和声明的形参名称保持一致*/@RequestMapping("/handle03")public ModelAndView handle03(@RequestParam("ids") Integer id,Boolean flag) {Date date = new Date();ModelAndView modelAndView = new ModelAndView();modelAndView.addObject("date",date);modelAndView.setViewName("success");return modelAndView;}/** SpringMVC接收pojo类型参数  url:/demo/handle04?id=1&username=zhangsan** 接收pojo类型参数,直接形参声明即可,类型就是Pojo的类型,形参名无所谓* 但是要求传递的参数名必须和Pojo的属性名保持一致*/@RequestMapping("/handle04")public ModelAndView handle04(User user) {Date date = new Date();ModelAndView modelAndView = new ModelAndView();modelAndView.addObject("date",date);modelAndView.setViewName("success");return modelAndView;}/** SpringMVC接收pojo包装类型参数  url:/demo/handle05?user.id=1&user.username=zhangsan* 不管包装Pojo与否,它首先是一个pojo,那么就可以按照上述pojo的要求来* 1、绑定时候直接形参声明即可* 2、传参参数名和pojo属性保持一致,如果不能够定位数据项,那么通过属性名 + "." 的方式进一步锁定数据**/@RequestMapping("/handle05")public ModelAndView handle05(QueryVo queryVo) {Date date = new Date();ModelAndView modelAndView = new ModelAndView();modelAndView.addObject("date",date);modelAndView.setViewName("success");return modelAndView;}/*** 绑定日期类型参数* 定义一个SpringMVC的类型转换器  接口,扩展实现接口接口,注册你的实现* @param birthday* @return*/@RequestMapping("/handle06")public ModelAndView handle06(Date birthday) {Date date = new Date();ModelAndView modelAndView = new ModelAndView();modelAndView.addObject("date",date);modelAndView.setViewName("success");return modelAndView;}/** restful  get   /demo/handle/15*/@RequestMapping(value = "/handle/{id}",method = {RequestMethod.GET})public ModelAndView handleGet(@PathVariable("id") Integer id) {Date date = new Date();ModelAndView modelAndView = new ModelAndView();modelAndView.addObject("date",date);modelAndView.setViewName("success");return modelAndView;}/** restful  post  /demo/handle*/@RequestMapping(value = "/handle",method = {RequestMethod.POST})public ModelAndView handlePost(String username) {Date date = new Date();ModelAndView modelAndView = new ModelAndView();modelAndView.addObject("date",date);modelAndView.setViewName("success");return modelAndView;}/** restful  put  /demo/handle/15/lisi*/@RequestMapping(value = "/handle/{id}/{name}",method = {RequestMethod.PUT})public ModelAndView handlePut(@PathVariable("id") Integer id,@PathVariable("name") String username) {Date date = new Date();ModelAndView modelAndView = new ModelAndView();modelAndView.addObject("date",date);modelAndView.setViewName("success");return modelAndView;}/** restful  delete  /demo/handle/15*/@RequestMapping(value = "/handle/{id}",method = {RequestMethod.DELETE})public ModelAndView handleDelete(@PathVariable("id") Integer id) {Date date = new Date();ModelAndView modelAndView = new ModelAndView();modelAndView.addObject("date",date);modelAndView.setViewName("success");return modelAndView;}@RequestMapping("/handle07")// 添加@ResponseBody之后,不再走视图解析器那个流程,而是等同于response直接输出数据public @ResponseBody User handle07(@RequestBody User user) {// 业务逻辑处理,修改name为张三丰user.setName("张三丰");return user;}/*** 文件上传* @return*/@RequestMapping(value = "/upload")public ModelAndView upload(MultipartFile uploadFile,HttpSession session) throws IOException {// 处理上传文件// 重命名,原名123.jpg ,获取后缀String originalFilename = uploadFile.getOriginalFilename();// 原始名称// 扩展名  jpgString ext = originalFilename.substring(originalFilename.lastIndexOf(".") + 1, originalFilename.length());String newName = UUID.randomUUID().toString() + "." + ext;// 存储,要存储到指定的文件夹,/uploads/yyyy-MM-dd,考虑文件过多的情况按照日期,生成一个子文件夹String realPath = session.getServletContext().getRealPath("/uploads");String datePath = new SimpleDateFormat("yyyy-MM-dd").format(new Date());File folder = new File(realPath + "/" + datePath);if(!folder.exists()) {folder.mkdirs();}// 存储文件到目录uploadFile.transferTo(new File(folder,newName));// TODO 文件磁盘路径要更新到数据库字段Date date = new Date();ModelAndView modelAndView = new ModelAndView();modelAndView.addObject("date",date);modelAndView.setViewName("success");return modelAndView;}/*** SpringMVC 重定向时参数传递的问题* 转发:A 找 B 借钱400,B没有钱但是悄悄的找到C借了400块钱给A*      url不会变,参数也不会丢失,一个请求* 重定向:A 找 B 借钱400,B 说我没有钱,你找别人借去,那么A 又带着400块的借钱需求找到C*      url会变,参数会丢失需要重新携带参数,两个请求*/@RequestMapping("/handleRedirect")public String handleRedirect(String name,RedirectAttributes redirectAttributes) {//return "redirect:handle01?name=" + name;  // 拼接参数安全性、参数长度都有局限// addFlashAttribute方法设置了一个flash类型属性,该属性会被暂存到session中,在跳转到页面之后该属性销毁redirectAttributes.addFlashAttribute("name",name);return "redirect:handle01";}}

第⼀部分 Spring MVC 应⽤

第 1 节 Spring MVC 简介

1.1 MVC 体系结构

三层架构
我们的开发架构⼀般都是基于两种形式,⼀种是 C/S 架构,也就是客户端/服务器;另⼀种是 B/S 架构,也就是浏览器服务器。在 JavaEE 开发中,⼏乎全都是基于 B/S 架构的开发。那么在 B/S 架构中,系统标准的三层架构包括:表现层、业务层、持久层。三层架构在我们的实际开发中使⽤的⾮常多,所以我们课程中的案例也都是基于三层架构设计的。
三层架构中,每⼀层各司其职,接下来我们就说说每层都负责哪些⽅⾯:
表现层 :
也就是我们常说的web 层。它负责接收客户端请求,向客户端响应结果,通常客户端使⽤http 协议请求web 层,web 需要接收 http 请求,完成 http 响应。
表现层包括展示层和控制层:控制层负责接收请求,展示层负责结果的展示。
表现层依赖业务层,接收到客户端请求⼀般会调⽤业务层进⾏业务处理,并将处理结果响应给客户端。
表现层的设计⼀般都使⽤ MVC 模型。(MVC 是表现层的设计模型,和其他层没有关系)

业务层 :
也就是我们常说的 service 层。它负责业务逻辑处理,和我们开发项⽬的需求息息相关。web 层依赖业务层,但是业务层不依赖 web 层。
业务层在业务处理时可能会依赖持久层,如果要对数据持久化需要保证事务⼀致性。(也就是我们说的, 事务应该放到业务层来控制)
持久层 :
也就是我们是常说的 dao 层。负责数据持久化,包括数据层即数据库和数据访问层,数据库是对数据进⾏持久化的载体,数据访问层是业务层和持久层交互的接⼝,业务层需要通过数据访问层将数据持久化到数据库中。通俗的讲,持久层就是和数据库交互,对数据库表进⾏增删改查的。
MVC设计模式
MVC 全名是 Model View Controller,是 模型(model)-视图(view)-控制器(controller) 的缩写, 是⼀种⽤于设计创建 Web 应⽤程序表现层的模式。MVC 中每个部分各司其职:
Model(模型):模型包含业务模型和数据模型,数据模型⽤于封装数据,业务模型⽤于处理业务。
View(视图): 通常指的就是我们的 jsp 或者 html。作⽤⼀般就是展示数据的。通常视图是依据模型数据创建的。
Controller(控制器): 是应⽤程序中处理⽤户交互的部分。作⽤⼀般就是处理程序逻辑的。MVC提倡:每⼀层只编写⾃⼰的东⻄,不编写任何其他的代码;分层是为了解耦,解耦是为了维护⽅便和分⼯协作。

1.2 Spring MVC 是什么?

SpringMVC 全名叫 Spring Web MVC,是⼀种基于 Java 的实现 MVC 设计模型的请求驱动类型的轻量级Web 框架,属于 SpringFrameWork 的后续产品。

SpringMVC 已经成为 ⽬前最主流的 MVC 框架 之⼀,并且 随着 Spring3.0 的发布,全⾯超越 Struts2,成为最优秀的 MVC 框架。
servlet、struts实现接⼝、springmvc中要让⼀个java类能够处理请求只需要添加注解就ok
它通过⼀套注解,让⼀个简单的 Java 类成为处理请求的控制器,⽽⽆须实现任何接⼝。同时它还⽀持RESTful 编程⻛格的请求。
总之:Spring MVC和Struts2⼀样,都是 为了解决表现层问题 的web框架,它们都是基于 MVC 设计模式的。⽽这些表现层框架的主要职责就是处理前端HTTP请求。
Spring MVC 本质可以认为是对servlet的封装,简化了我们serlvet的开发
作⽤:1)接收请求 2)返回响应,跳转⻚⾯

第 2 节 Spring Web MVC ⼯作流程

需求:前端浏览器请求url:http://localhost:8080/demo/handle01,前端⻚⾯显示后台服务器的时间开发过程
1)配置DispatcherServlet前端控制器
2)开发处理具体业务逻辑的Handler(@Controller、@RequestMapping)
3)xml配置⽂件配置controller扫描,配置springmvc三⼤件
4)将xml⽂件路径告诉springmvc(DispatcherServlet)

2.1 Spring MVC 请求处理流程


流程说明
第⼀步:⽤户发送请求⾄前端控制器DispatcherServlet
第⼆步:DispatcherServlet收到请求调⽤HandlerMapping处理器映射器
第三步:处理器映射器根据请求Url找到具体的Handler(后端控制器),⽣成处理器对象及处理器拦截
器(如果 有则⽣成)⼀并返回DispatcherServlet
第四步:DispatcherServlet调⽤HandlerAdapter处理器适配器去调⽤Handler
第五步:处理器适配器执⾏Handler
第六步:Handler执⾏完成给处理器适配器返回ModelAndView
第七步:处理器适配器向前端控制器返回 ModelAndView,ModelAndView 是SpringMVC 框架的⼀个
底层对 象,包括 Model 和 View
第⼋步:前端控制器请求视图解析器去进⾏视图解析,根据逻辑视图名来解析真正的视图。
第九步:视图解析器向前端控制器返回View
第⼗步:前端控制器进⾏视图渲染,就是将模型数据(在 ModelAndView 对象中)填充到 request 域
第⼗⼀步:前端控制器向⽤户响应结果

2.2 Spring MVC 九⼤组件

  • HandlerMapping(处理器映射器)
    HandlerMapping 是⽤来查找 Handler 的,也就是处理器,具体的表现形式可以是类,也可以是⽅法。⽐如,标注了@RequestMapping的每个⽅法都可以看成是⼀个Handler。Handler负责具体实际的请求处理,在请求到达后,HandlerMapping 的作⽤便是找到请求相应的处理器
    Handler 和 Interceptor.
  • HandlerAdapter(处理器适配器)
    HandlerAdapter 是⼀个适配器。因为 Spring MVC 中 Handler 可以是任意形式的,只要能处理请求即可。但是把请求交给 Servlet 的时候,由于 Servlet 的⽅法结构都是doService(HttpServletRequest req,HttpServletResponse resp)形式的,要让固定的 Servlet 处理⽅法调⽤ Handler 来进⾏处理,便是 HandlerAdapter 的职责。
  • HandlerExceptionResolver
    HandlerExceptionResolver ⽤于处理 Handler 产⽣的异常情况。它的作⽤是根据异常设置ModelAndView,之后交给渲染⽅法进⾏渲染,渲染⽅法会将 ModelAndView 渲染成⻚⾯。
  • ViewResolver
    ViewResolver即视图解析器,⽤于将String类型的视图名和Locale解析为View类型的视图,只有⼀个resolveViewName()⽅法。从⽅法的定义可以看出,Controller层返回的String类型视图名viewName 最终会在这⾥被解析成为View。View是⽤来渲染⻚⾯的,也就是说,它会将程序返回的参数和数据填⼊模板中,⽣成html⽂件。ViewResolver 在这个过程主要完成两件事情:ViewResolver 找到渲染所⽤的模板(第⼀件⼤事)和所⽤的技术(第⼆件⼤事,其实也就是找到视图的类型,如JSP)并填⼊参数。默认情况下,Spring MVC会⾃动为我们配置⼀个
    InternalResourceViewResolver,是针对 JSP 类型视图的。
  • RequestToViewNameTranslator
    RequestToViewNameTranslator 组件的作⽤是从请求中获取 ViewName.因为 ViewResolver 根据ViewName 查找 View,但有的 Handler 处理完成之后,没有设置 View,也没有设置 ViewName,便要通过这个组件从请求中查找 ViewName。
  • LocaleResolver
    ViewResolver 组件的 resolveViewName ⽅法需要两个参数,⼀个是视图名,⼀个是 Locale。LocaleResolver ⽤于从请求中解析出 Locale,⽐如中国 Locale 是 zh-CN,⽤来表示⼀个区域。这个组件也是 i18n 的基础。
  • ThemeResolver
    ThemeResolver 组件是⽤来解析主题的。主题是样式、图⽚及它们所形成的显示效果的集合。Spring MVC 中⼀套主题对应⼀个 properties⽂件,⾥⾯存放着与当前主题相关的所有资源,如图⽚、CSS样式等。创建主题⾮常简单,只需准备好资源,然后新建⼀个“主题名.properties”并将资源设置进去,放在classpath下,之后便可以在⻚⾯中使⽤了。SpringMVC中与主题相关的类有ThemeResolver、ThemeSource和Theme。ThemeResolver负责从请求中解析出主题名,ThemeSource根据主题名找到具体的主题,其抽象也就是Theme,可以通过Theme来获取主题和具体的资源。
  • MultipartResolver
    MultipartResolver ⽤于上传请求,通过将普通的请求包装成 MultipartHttpServletRequest 来实现。MultipartHttpServletRequest 可以通过 getFile() ⽅法 直接获得⽂件。如果上传多个⽂件,还可以调⽤ getFileMap()⽅法得到Map<FileName,File>这样的结构,MultipartResolver 的作⽤就是封装普通的请求,使其拥有⽂件上传的功能。
  • FlashMapManager
    FlashMap ⽤于重定向时的参数传递,⽐如在处理⽤户订单时候,为了避免重复提交,可以处理完post请求之后重定向到⼀个get请求,这个get请求可以⽤来显示订单详情之类的信息。这样做虽然可以规避⽤户重新提交订单的问题,但是在这个⻚⾯上要显示订单的信息,这些数据从哪⾥来获得呢?因为重定向时么有传递参数这⼀功能的,如果不想把参数写进URL(不推荐),那么就可以通过FlashMap来传递。只需要在重定向之前将要传递的数据写⼊请求(可以通过ServletRequestAttributes.getRequest()⽅法获得)的属性OUTPUT_FLASH_MAP_ATTRIBUTE中,这样在重定向之后的Handler中Spring就会⾃动将其设置到Model中,在显示订单信息的⻚⾯上就可以直接从Model中获取数据。FlashMapManager 就是⽤来管理 FalshMap 的。

第 3 节 请求参数绑定(串讲)

第 4 节 对 Restful ⻛格请求⽀持

第 5 节 Ajax Json交互

暂不写

第⼆部分 Spring MVC ⾼级技术

第 1 节 拦截器(Inteceptor)使⽤

1.1 监听器、过滤器和拦截器对⽐

  • Servlet:处理Request请求和Response响应
  • 过滤器(Filter):对Request请求起到过滤的作⽤,作⽤在Servlet之前,如果配置为/*可以对所有的资源访问(servlet、js/css静态资源等)进⾏过滤处理
  • 监听器(Listener):实现了javax.servlet.ServletContextListener 接⼝的服务器端组件,它随Web应⽤的启动⽽启动,只初始化⼀次,然后会⼀直运⾏监视,随Web应⽤的停⽌⽽销毁
    作⽤⼀:做⼀些初始化⼯作,web应⽤中spring容器启动ContextLoaderListener
    作⽤⼆:监听web中的特定事件,⽐如HttpSession,ServletRequest的创建和销毁;变量的创建、销毁和修改等。可以在某些动作前后增加处理,实现监控,⽐如统计在线⼈数,利⽤HttpSessionLisener等。
  • 拦截器(Interceptor):是SpringMVC、Struts等表现层框架⾃⼰的,不会拦截jsp/html/css/image的访问等,只会拦截访问的控制器⽅法(Handler)。
    从配置的⻆度也能够总结发现:serlvet、filter、listener是配置在web.xml中的,⽽interceptor是配置在表现层框架⾃⼰的配置⽂件中的
    在Handler业务逻辑执⾏之前拦截⼀次
    在Handler逻辑执⾏完毕但未跳转⻚⾯之前拦截⼀次
    在跳转⻚⾯之后拦截⼀次

1.2 拦截器的执⾏流程

在运⾏程序时,拦截器的执⾏是有⼀定顺序的,该顺序与配置⽂件中所定义的拦截器的顺序相关。 单个拦截器,在程序中的执⾏流程如下图所示:

1)程序先执⾏preHandle()⽅法,如果该⽅法的返回值为true,则程序会继续向下执⾏处理器中的⽅法,否则将不再向下执⾏。
2)在业务处理器(即控制器Controller类)处理完请求后,会执⾏postHandle()⽅法,然后会通过DispatcherServlet向客户端返回响应。
3)在DispatcherServlet处理完请求后,才会执⾏afterCompletion()⽅法。

1.3 多个拦截器的执⾏流程

多个拦截器(假设有两个拦截器Interceptor1和Interceptor2,并且在配置⽂件中, Interceptor1拦截器配置在前),在程序中的执⾏流程如下图所示:
从图可以看出,当有多个拦截器同时⼯作时,它们的preHandle()⽅法会按照配置⽂件中拦截器的配置顺序执⾏,⽽它们的postHandle()⽅法和afterCompletion()⽅法则会按照配置顺序的反序执⾏。
示例代码
⾃定义SpringMVC拦截器

package com.lagou.edu.interceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* ⾃定义springmvc拦截器
*/
public class MyIntercepter01 implements HandlerInterceptor {
/**
* 会在handler⽅法业务逻辑执⾏之前执⾏
* 往往在这⾥完成权限校验⼯作
* @param request
* @param response
* @param handler
* @return 返回值boolean代表是否放⾏,true代表放⾏,false代表中⽌
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse
response, Object handler) throws Exception {
System.out.println("MyIntercepter01 preHandle......");
return true;
}
/**
* 会在handler⽅法业务逻辑执⾏之后尚未跳转⻚⾯时执⾏
* @param request
* @param response
* @param handler
* @param modelAndView 封装了视图和数据,此时尚未跳转⻚⾯呢,你可以在这⾥针对返回的
数据和视图信息进⾏修改
* @throws Exception
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse
response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("MyIntercepter01 postHandle......");
}
/**
* ⻚⾯已经跳转渲染完毕之后执⾏
* @param request
* @param response
* @param handler
* @param ex 可以在这⾥捕获异常
* @throws Exception
*/
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("MyIntercepter01 afterCompletion......");
}
}

注册SpringMVC拦截器

<mvc:interceptors>
<!--拦截所有handler-->
<!--<bean class="com.lagou.edu.interceptor.MyIntercepter01"/>-->
<mvc:interceptor>
<!--配置当前拦截器的url拦截规则,**代表当前⽬录下及其⼦⽬录下的所有url-->
<mvc:mapping path="/**"/>
<!--exclude-mapping可以在mapping的基础上排除⼀些url拦截-->
<!--<mvc:exclude-mapping path="/demo/**"/>-->
<bean class="com.lagou.edu.interceptor.MyIntercepter01"/>
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.lagou.edu.interceptor.MyIntercepter02"/>
</mvc:interceptor>
</mvc:interceptors>

第 2 节 处理multipart形式的数据

⽂件上传
原⽣servlet处理上传的⽂件数据的,springmvc⼜是对serlvet的封装所需jar包

<!--⽂件上传所需jar坐标-->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>

配置⽂件上传解析器

<!--配置⽂件上传解析器,id是固定的multipartResolver-->
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!--设置上传⼤⼩,单位字节-->
<property name="maxUploadSize" value="1000000000"/>
</bean>

前端Form

<%--
1 method="post"
2 enctype="multipart/form-data"
3 type="file"
--%>
<form method="post" enctype="multipart/form-data" action="/demo/upload">
<input type="file" name="uploadFile"/>
<input type="submit" value="上传"/>
</form>

后台接收Handler

@RequestMapping("upload")
public String upload(MultipartFile uploadFile, HttpServletRequest request)
throws IOException {
// ⽂件原名,如xxx.jpg
String originalFilename = uploadFile.getOriginalFilename();
// 获取⽂件的扩展名,如jpg
String extendName =
originalFilename.substring(originalFilename.lastIndexOf(".") + 1,
originalFilename.length());
String uuid = UUID.randomUUID().toString();
// 新的⽂件名字
String newName = uuid + "." + extendName;
String realPath =
request.getSession().getServletContext().getRealPath("/uploads");
// 解决⽂件夹存放⽂件数量限制,按⽇期存放
String datePath = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
File floder = new File(realPath + "/" + datePath);
if(!floder.exists()) {
floder.mkdirs();
}
uploadFile.transferTo(new File(floder,newName));
return "success";
}

第 3 节 在控制器中处理异常

package com.lagou.edu.controller;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
// 可以让我们优雅的捕获所有Controller对象handler⽅法抛出的异常
@ControllerAdvice
public class GlobalExceptionResolver {
@ExceptionHandler(ArithmeticException.class)
public ModelAndView handleException(ArithmeticException exception,
HttpServletResponse response) {
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("msg",exception.getMessage());
modelAndView.setViewName("error");
return modelAndView;
}
}

第 4 节 基于Flash属性的跨重定向请求数据传递

重定向时请求参数会丢失,我们往往需要重新携带请求参数,我们可以进⾏⼿动参数拼接如下:

return "redirect:handle01?name=" + name;

但是上述拼接参数的⽅法属于get请求,携带参数⻓度有限制,参数安全性也不⾼,此时,我们可以使⽤SpringMVC提供的flash属性机制,向上下⽂中添加flash属性,框架会在session中记录该属性值,当跳转到⻚⾯之后框架会⾃动删除flash属性,不需要我们⼿动删除,通过这种⽅式进⾏重定向参数传递,参数⻓度和安全性都得到了保障,如下:

/**
* SpringMVC 重定向时参数传递的问题
* 转发:A 找 B 借钱400,B没有钱但是悄悄的找到C借了400块钱给A
* url不会变,参数也不会丢失,⼀个请求
* 重定向:A 找 B 借钱400,B 说我没有钱,你找别⼈借去,那么A ⼜带着400块的借钱需求找到
C
* url会变,参数会丢失需要重新携带参数,两个请求
*/
@RequestMapping("/handleRedirect")
public String handleRedirect(String name,RedirectAttributes
redirectAttributes) {
//return "redirect:handle01?name=" + name; // 拼接参数安全性、参数⻓度都有
局限
// addFlashAttribute⽅法设置了⼀个flash类型属性,该属性会被暂存到session中,在
跳转到⻚⾯之后该属性销毁
redirectAttributes.addFlashAttribute("name",name);
return "redirect:handle01";
}

第三部分 ⼿写 MVC 框架

  • 回顾SpringMVC执⾏的⼤致原理,后续根据这个模仿⼿写⾃⼰的mvc框架


创建新module mvc
pom

<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>org.example</groupId><artifactId>mvc-zdy</artifactId><version>1.0-SNAPSHOT</version><packaging>war</packaging><name>mvc-zdy Maven Webapp</name><!-- FIXME change it to the project's website --><url>http://www.example.com</url><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><maven.compiler.source>11</maven.compiler.source><maven.compiler.target>11</maven.compiler.target></properties><dependencies><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>test</scope></dependency><dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>3.1.0</version><scope>provided</scope></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>3.9</version></dependency></dependencies><build><plugins><!--编译插件定义编译细节--><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.1</version><configuration><source>11</source><target>11</target><encoding>utf-8</encoding><!--告诉编译器,编译的时候记录下形参的真实名称--><compilerArgs><arg>-parameters</arg></compilerArgs></configuration></plugin><plugin><groupId>org.apache.tomcat.maven</groupId><artifactId>tomcat7-maven-plugin</artifactId><version>2.2</version><configuration><port>8080</port><path>/</path></configuration></plugin></plugins></build>
</project>
  • ⼿写MVC框架之注解开发
    LagouController

Spring MVC(一)相关推荐

  1. Java之Spring mvc详解(非原创)

    文章大纲 一.Spring mvc介绍 二.Spring mvc代码实战 三.项目源码下载 四.参考文章 一.Spring mvc介绍 1. 什么是springmvc   springmvc是spri ...

  2. spring mvc 关键接口 HandlerMapping HandlerAdapter

    HandlerMapping  Spring mvc 使用HandlerMapping来找到并保存url请求和处理函数间的mapping关系.     以DefaultAnnotationHandle ...

  3. spring mvc 控制器方法传递一些经验对象的数组

    由于该项目必须提交一个表单,其中多个对象,更好的方法是直接通过在控制器方法参数的数组. 因为Spring mvc框架在反射生成控制方法的參数对象的时候会调用这个类的getDeclaredConstru ...

  4. Spring MVC 4

    Spring MVC 4 项目文件结构 pom.xml依赖 <properties><endorsed.dir>${project.build.directory}/endor ...

  5. java注解返回不同消息,Spring MVC Controller中的一个读入和返回都是JSON的方法如何获取javax.validation注解的异常信息...

    Spring MVC Controller中的一个读入和返回都是JSON的方法怎么获取javax.validation注解的错误信息? 本帖最后由 LonelyCoder2012 于 2014-03- ...

  6. Spring MVC前后端的数据传输

    本篇文章主要介绍了Spring MVC中如何在前后端传输数据. 后端 ➡ 前端 在Spring MVC中这主要通过Model将数据从后端传送到前端,一般的写法为: @RequestMapping(va ...

  7. 番外:Spring MVC环境搭建和Mybatis配置避坑篇

    2019独角兽企业重金招聘Python工程师标准>>> web.xml引入对spring mvc的支持: spring-mvc配置spring-mvc: spring-mybatis ...

  8. spring mvc velocity 配置备忘

    2019独角兽企业重金招聘Python工程师标准>>> Spring里面最重要的概念是IOC和AOP,还有两项很重要的模块是事务和MVC,对于IOC和AOP,我们要深究其源码实现,对 ...

  9. Spring MVC配置文件的三个常用配置详解

    2019独角兽企业重金招聘Python工程师标准>>> Spring MVC项目中通常会有二个配置文件,sprng-servlet.xml和applicationContext.xm ...

  10. Spring MVC框架有哪些优点

    Spring MVC是Spring提供的一个实现了Web MVC设计模式的轻量级Web框架.它与Struts2框架一样,都属于MVC框架,但其使用和性能等方面比Struts2更加优异. Spring ...

最新文章

  1. 深入 Python 解释器源码,我终于搞明白了字符串驻留的原理!
  2. iMeta期刊投审稿系统ScholarOne正式上线
  3. 虚拟化数据中心服务器硬件配置建议
  4. sem_我知道的关键词1
  5. 【Linux】一步一步学Linux——lnstat命令(189)
  6. 浅谈ios设计之使用表格UITableVIew设计通讯录的方法
  7. Django的模型类Meta
  8. 【英语学习】【Level 08】U03 My Choice L2 All-time favorite character
  9. 释放摄像头_防爆摄像头在使用过程中会遇到哪些问题,如何处理
  10. SSH/SSH客户端介绍、利用SSH访问linux、SSH跟telnet区别
  11. mybatis 映射问题(mysql date与java Date类型格式错误)
  12. Big Sur MacOS高清动态壁纸
  13. 树形结构 —— 树与二叉树 —— 无根树转有根树
  14. php 抽奖活动_PHP实现活动人选抽奖功能
  15. 数字集成电路 -- 各种计数器简介
  16. Netty 心跳机制及断线重连
  17. C++ Reference: Standard C++ Library reference: C Library: cstdio: fopen
  18. 恒大帝景220平文华东路
  19. 函数CLOI_PUT_SIGN_IN_FRONT:将数字的负号提到前面
  20. 2022-2028全球与中国机器人底盘市场现状及未来发展趋势

热门文章

  1. 5 JInja2模版(适用于Django和Flask)
  2. python绘制直方图横坐标标签竖直显示的方法
  3. PHP打印九九乘法表(让输出内容能够完全对齐)
  4. FLIR Systems获得美国陆军士兵随身传感器项目价值3960万美元的“黑黄蜂”个人侦察系统合同
  5. 可以做mysql题的网站_基于PHP的试题题库管理网站的设计(ThinkPHP,MySQL)
  6. 2022美容师(高级)操作证考试题及答案
  7. 【眇视万里一毫端】全志V853三核异构边缘AI视觉处理芯片全新发布
  8. tensorflow Faster RCNN 缺少 nsync_*.h
  9. SQL优化篇:如何成为一位写优质SQL语句的绝顶高手
  10. java课程设计-音乐播放器_Java程序设计课程设计报告音乐播放器