Spring MVC(一)
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(一)相关推荐
- Java之Spring mvc详解(非原创)
文章大纲 一.Spring mvc介绍 二.Spring mvc代码实战 三.项目源码下载 四.参考文章 一.Spring mvc介绍 1. 什么是springmvc springmvc是spri ...
- spring mvc 关键接口 HandlerMapping HandlerAdapter
HandlerMapping Spring mvc 使用HandlerMapping来找到并保存url请求和处理函数间的mapping关系. 以DefaultAnnotationHandle ...
- spring mvc 控制器方法传递一些经验对象的数组
由于该项目必须提交一个表单,其中多个对象,更好的方法是直接通过在控制器方法参数的数组. 因为Spring mvc框架在反射生成控制方法的參数对象的时候会调用这个类的getDeclaredConstru ...
- Spring MVC 4
Spring MVC 4 项目文件结构 pom.xml依赖 <properties><endorsed.dir>${project.build.directory}/endor ...
- java注解返回不同消息,Spring MVC Controller中的一个读入和返回都是JSON的方法如何获取javax.validation注解的异常信息...
Spring MVC Controller中的一个读入和返回都是JSON的方法怎么获取javax.validation注解的错误信息? 本帖最后由 LonelyCoder2012 于 2014-03- ...
- Spring MVC前后端的数据传输
本篇文章主要介绍了Spring MVC中如何在前后端传输数据. 后端 ➡ 前端 在Spring MVC中这主要通过Model将数据从后端传送到前端,一般的写法为: @RequestMapping(va ...
- 番外:Spring MVC环境搭建和Mybatis配置避坑篇
2019独角兽企业重金招聘Python工程师标准>>> web.xml引入对spring mvc的支持: spring-mvc配置spring-mvc: spring-mybatis ...
- spring mvc velocity 配置备忘
2019独角兽企业重金招聘Python工程师标准>>> Spring里面最重要的概念是IOC和AOP,还有两项很重要的模块是事务和MVC,对于IOC和AOP,我们要深究其源码实现,对 ...
- Spring MVC配置文件的三个常用配置详解
2019独角兽企业重金招聘Python工程师标准>>> Spring MVC项目中通常会有二个配置文件,sprng-servlet.xml和applicationContext.xm ...
- Spring MVC框架有哪些优点
Spring MVC是Spring提供的一个实现了Web MVC设计模式的轻量级Web框架.它与Struts2框架一样,都属于MVC框架,但其使用和性能等方面比Struts2更加优异. Spring ...
最新文章
- 深入 Python 解释器源码,我终于搞明白了字符串驻留的原理!
- iMeta期刊投审稿系统ScholarOne正式上线
- 虚拟化数据中心服务器硬件配置建议
- sem_我知道的关键词1
- 【Linux】一步一步学Linux——lnstat命令(189)
- 浅谈ios设计之使用表格UITableVIew设计通讯录的方法
- Django的模型类Meta
- 【英语学习】【Level 08】U03 My Choice L2 All-time favorite character
- 释放摄像头_防爆摄像头在使用过程中会遇到哪些问题,如何处理
- SSH/SSH客户端介绍、利用SSH访问linux、SSH跟telnet区别
- mybatis 映射问题(mysql date与java Date类型格式错误)
- Big Sur MacOS高清动态壁纸
- 树形结构 —— 树与二叉树 —— 无根树转有根树
- php 抽奖活动_PHP实现活动人选抽奖功能
- 数字集成电路 -- 各种计数器简介
- Netty 心跳机制及断线重连
- C++ Reference: Standard C++ Library reference: C Library: cstdio: fopen
- 恒大帝景220平文华东路
- 函数CLOI_PUT_SIGN_IN_FRONT:将数字的负号提到前面
- 2022-2028全球与中国机器人底盘市场现状及未来发展趋势
热门文章
- 5 JInja2模版(适用于Django和Flask)
- python绘制直方图横坐标标签竖直显示的方法
- PHP打印九九乘法表(让输出内容能够完全对齐)
- FLIR Systems获得美国陆军士兵随身传感器项目价值3960万美元的“黑黄蜂”个人侦察系统合同
- 可以做mysql题的网站_基于PHP的试题题库管理网站的设计(ThinkPHP,MySQL)
- 2022美容师(高级)操作证考试题及答案
- 【眇视万里一毫端】全志V853三核异构边缘AI视觉处理芯片全新发布
- tensorflow Faster RCNN 缺少 nsync_*.h
- SQL优化篇:如何成为一位写优质SQL语句的绝顶高手
- java课程设计-音乐播放器_Java程序设计课程设计报告音乐播放器