- 一、Sprig MVC简介
- 1.1介绍
- 1.2MVC是什么
- 二、Spring MVC实现原理
- 2.1核心组件
- 2.2工作流程
- 三、第一个Spring MVC
- 四、常用注解
- 五、参数绑定
- 5.1URL风格参数绑定
- 5.2RESTful风格的URL参数获取
- 5.3映射cookie
- 5.4使用POJO绑定参数
- 5.5JSP页面的转发和重定向
- 六、Spring MVC数据绑定
- 6.1基本数据类型
- 6.2包装类
- 6.3数组类型
- 6.4POJO(java对象)
- 6.5List
- 6.6JSON
- 七、Spring MVC视图层解析
- 7.1业务数据绑定到request域对象
- 7.1.1Map
- 7.1.2 Model
- 7.1.3ModelAndView
- 7.1.4 Servlet的API
- 7.1.5@ModelAttribute
- 7.2业务数据绑定到Session域对象
- 7.2.1使用原生的Servlet API
- 7.2.2@SessionAttribute
- 八、 Spring MVC自定义数据类型转换器
- 九、Spring MVC和RESTful的集成
- 9.1初识RESTful
- 9.2RESTful的特点
- 9.3HiddenHttpMethodFilter的实现原理
- 9.4Spring MVC和RESTful整合
- 十、文件的上传下载
- 10.1文件上传
- 10.1.1单文件上传
- 10.1.2多文件上传
- 10.2文件下载
- 十一、拦截器
- 11.1过滤器、监听器、拦截器的对比
- 11.2拦截器基本概念
- 11.3拦截器的实现
- 11.4多个拦截器的实现
文章是看楠哥的视频做的总结,自己还查阅了一些资料,文章也加了自己的总结,这篇博客可以帮助大家入门,还有一些知识大家如果用到的话可以到时候再去学习一下,我写这篇主要是为了方便后面复习。
一、Sprig MVC简介
1.1介绍
Spring MVC是Spring framework提供的Web组件,全称是Spring Web MVC,是目前主流的实现MVC设计模式的框架,提供前端路由映射、视图解析等功能
Java Web开发者必须要掌握的技术框架
1.2MVC是什么
MVC是一种软件架构思想,把软件按照模型,视图,控制器来划分
Model:模型层,指工程中的JavaBean,用来处理数据
JavaBean分成两类:
- 一类称为实体类Bean:专门用来存储业务数据,比如Student,User
- 一类称为业务处理Bean:指Servlet或Dao对象,专门用来处理业务逻辑和数据访问
View:视图层,指工程中的html,jsp等页面,作用是和用户进行交互,展示数据
Controler:控制层,指工程中的Servlet,作用是接收请求和响应浏览器 - 流程:
- 用户通过视图层发送请求到服务器,在服务器中请求被Controller接收
- Controller调用相应的Model层处理请求,处理完毕后结果返回到Controller
- Controller再根据请求处理的结果找到对应的View视图,渲染数据后最终响应给浏览器
Spring MVC对这套MVC流程进行封装,帮助开发者屏蔽底层细节,并且开放出相关接口供开发者调用,让MVC开发更简单方便
二、Spring MVC实现原理
2.1核心组件
- DispatcherServlet:前置控制器,负责调度其他组件的执行,可以降低不同组件之间的耦合性,是整个Spring MVC的核心模块
- Handler:处理器,完成具体的业务逻辑,相当于Servlet
- HandlerMapping:DispatcherServlet是通过 HandlerMapping把请求映射到不同的Handler
- HandlerInterceptor:处理器拦截器,是一个接口,如果我们需要进行一些拦截处理,可以通过实现该接口完成
- HandlerExecutionChain:处理器执行链,包括两部分内容:Handler和HandlerInterceptor(系统会有一个默认的HandlerInterceptor,如果有额外拦截处理,可以添加拦截器进行设置)
- HandlerAdapter:处理器适配器,Handler执行业务方法之前,需要进行一系列的操作包括表单的数据验证、数据类型转换、把表单数据封装到POJO等,这些一系列的操作都是由HandlerAdapter完成,DispatcherServlet通过HandlerAdapter执行不同的Handler
- ModelAndView:封装了模型数据和视图信息,作为Handler的处理结果,返回给DispatcherServlet
- ViewResolver:视图解析器,DispatcherServlet通过它把逻辑视图解析为物理视图,最终把渲染的结果响应给客户端
2.2工作流程
- 客户端请求被DispatcherServlet接收
- 根据HandlerMapping映射到Handler
- 生成Handler和HandlerInterceptor
- Handler和HandlerInterceptor以HandlerExecutionChain的形式一并返回给DispatcherServlet
- DispatcherServlet通过HandlerAdapter调用Handler的方法完成业务逻辑处理
- 返回一个ModelAndView对象给DispatcherServlet
- DispatcherServlet把获取的ModelAndView对象传给ViewResolver视图解析器,把逻辑视图解析成物理视图
- ViewResolver返回一个View进行视图渲染(把模型填充到视图中)
- DispatcherServlet把渲染后的视图响应给客户端
三、第一个Spring MVC
- 创建maven改成工程,pom.xml加入Spring MVC的依赖
pom.xml
<?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>SpringMVC</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> <name>SpringMVC 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>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.3.19</version> </dependency> </dependencies></project>
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.3.19</version> </dependency>
- 在web.xml中配置Spring MVC的DispatcherServlet
首先在项目中创建java和resources的目录
在resources目录中添加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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"></beans>
然后在web.xml 配置Spring MVC的DispatcherServlet
<!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> <!-- 配置核心控制器 --> <servlet> <servlet-name>dispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!-- springmvc配置文件加载路径 1)默认情况下,读取WEB-INF下面的文件 2)可以改为加载类路径下(resources目录),加上classpath: --> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:springmvc.xml</param-value> </init-param> <!-- DispatcherServlet对象创建时间问题 1)默认情况下,第一次访问该Servlet的创建对象,意味着在这个时间才去加载springMVC.xml 2)可以改变为在项目启动时候就创建该Servlet,提高用户访问体验。 <load-on-startup>1</load-on-startup> 数值越大,对象创建优先级越低! (数值越低,越先创建) --> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dispatcherServlet</servlet-name> <!--/ 匹配所有的请求;(不包括.jsp)--> <!-- @RequestMapping("/index") public String index(){ System.out.println("接收到了请求"); //返回逻辑视图 逻辑视图相当于视图的别名 通过这个找到物理视图,也就是真正的视图 //这里返回的只是页面的名称,不是完整的页面访问路径 return "index"; }}
@Controller注解是为了让Spring IOC容器初始化时自动扫描到该Controller类;@RequestMapping是为了映射请求路径,这里因为类与方法上都有映射所以访问时应该是/;方法返回的结果是视图的名称index,该名称不是完整页面路径,最终会经过视图解析器解析为完整页面路径并跳转。
配置Tomcat
5. 测试
流程梳理
- DispatcherServlet接收到URL请求index,结合@RequestMapping(“/index”)注解把该请求交给index业务方法进行处理
- 执行index业务方法,控制台打印日志,并且返回"index"字符串(逻辑视图).
- 结合springmvc.xml中的视图解析器配置,找到目标资源:/index.jsp,即根目录下的index.jsp文件,把该JSP资源返回给客户端完成响应。
Spring MVC搭建成功
四、常用注解
@RequestMapping
Spring MVC通过@RequestMapping注解把URL请求和业务方法进行映射,在控制器的类定义处以及方法定义处都可以添加@RequestMapping,在类定义处添加相当于多了一层访问路径
@RequestMapping常用参数
- value:指定URL请求的实际地址,是@RequestMapping的默认值
- method:指定请求的method类型,包括GET、POST、PUT、DELETE等
@RequestMapping(value = "/index",method = RequestMethod.POST) public String index(){ System.out.println("接收到了请求"); //返回逻辑视图 逻辑视图相当于视图的别名 通过这个找到物理视图,也就是真正的视图 //注意:这里返回的只是页面名称,不是完整的页面访问路径 return "index"; }
上述代码表示只有POST请求可以访问该方法,如果使用其他请求访问的话,直接抛出异常,比如GET请求
- params:指定request请求中必须包含的参数值,如果不包含的话,就无法调用该方法
五、参数绑定
5.1URL风格参数绑定
params是对URL请求参数进行限制,不满足条件的URL无法访问该方法,需要在业务方法中获取URL的参数值。
- 在业务方法定义时声明参数列表
- 给参数列表添加@RequestParam注解进行绑定
Spring MVC可以自动完成数据类型转换,该工作是由HandlerAdapter来完成的
5.2RESTful风格的URL参数获取
- 传统的URL:localhost:8080/hello/index?id=1&name=tom
- RESTful URL:localhost:8080/hello/index/1/tom
@RequestMapping("/restful/{id}/{name}") public String restful(@PathVariable("id") Integer num, @PathVariable("name") String name){ System.out.println(num+"-"+name); return "index"; }
5.3映射cookie
@RequestMapping("/cookie") public String getcookie(@cookievalue("JSESSIONID") String sessionId){ System.out.println(sessionId); return "index"; }
5.4使用POJO绑定参数
Spring MVC会根据请求参数名和POJO属性名进行匹配,自动为该对象填充属性值,并且支持属性级联
首先创建实体类
为了方便测试,写一个addUser.jsp页面
<%-- Created by IntelliJ IDEA. User: 17614 Date: 2022-07-04 Time: 21:01 To change this template use File | Settings | File Templates.--%><%@ page contentType="text/html;charset=UTF-8" language="java" %><html><head> <title>Title</title></head><body><form action="/hello/add" method="post"> <table> <tr> <td>编号:</td> <td> <input type="text" name="id"> </td> </tr> <tr> <td>姓名:</td> <td> <input type="text" name="name"> </td> </tr> <tr> <td> <input type="submit" value="提交"> </td> </tr> </table></form></body></html>
然后在Handler中,编写相关方法
启动Tomcat服务器
结果发现出现乱码问题
为了解决这个问题,我们只需要在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>@Controller@RequestMapping("/course")public class CourseController { @Autowired private CourseRepository courseRepository; @GetMapping("/findAll") public ModelAndView findAll() { ModelAndView modelAndView = new ModelAndView(); modelAndView.setViewName("courseList"); modelAndView.addObject("list", courseRepository.findAll()); return modelAndView; } @DeleteMapping("/deleteById/{id}") public String deleteById(@PathVariable("id") Integer id){ courseRepository.deleteById(id); return "redirect:/course/findAll"; } @PostMapping("/save") public String save(Course course) { courseRepository.saveOrUpdate(course); return "redirect:/course/findAll"; } @GetMapping("/findById/{id}") public ModelAndView findById(@PathVariable("id") Integer id) { ModelAndView modelAndView = new ModelAndView(); modelAndView.setViewName("edit"); modelAndView.addObject("courser", courseRepository.findById(id)); return modelAndView; } @PutMapping("/update") public String update(Course course){ courseRepository.saveOrUpdate(course); return "redirect:/course/findAll"; }}
JSP
courseList.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %><%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %><%@page isELIgnored="false" %><html><head> <title>Title</title></head><body> <table> <tr> <td>编号:</td> <td>名称:</td> <td>价格:</td> <td>操作:</td> </tr> <c:forEach items="${list}" var="course"> <tr> <td>${course.id}</td> <td>${course.name}</td> <td>${course.price}</td> <td> <form action="/course/deleteById/${course.id}" method="post"> <input type="hidden" name="_method" value="DELETE"> <input type="submit" value="删除"> </form> <a href="/course/findById/${course.id}">修改</a> </td> </tr> </c:forEach> </table></body></html>
save.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %><html><head> <title>Title</title></head><body><form action="/course/save" method="post"> <table> <tr> <td>课程编号:</td> <td> <input type="text" name="id"> </td> </tr> <tr> <td>课程名称:</td> <td><input type="text" name="name"></td> </tr> <tr> <td>课程价格:</td> <td> <input type="text" name="price"></td> </tr> <tr> <td> <input type="submit" value="提交"> </td> <td> <input type="reset" value="重置"> </td> </tr> </table></form></body></html>
edit.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %><%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %><%@page isELIgnored="false" %><html><head> <title>Title</title></head><body> <form action="/course/update" method="post"> <input type="hidden" name="_method" value="PUT"/> <table> <tr> <td>课程编号:</td> <td> <input type="text" name="id" value="${courser.id}"> </td> </tr> <tr> <td>课程名称:</td> <td> <input type="text" name="name" value="${courser.name}"> </td> </tr> <tr> <td>课程价格:</td> <td> <input type="text" name="price" value="${courser.price}"> </td> </tr> <tr> <td> <input type="submit" value="提交"> </td> </tr> </table> </form></body></html>
点击删除
点击修改
点提交
十、文件的上传下载
10.1文件上传
10.1.1单文件上传
1.底层使用的是Apache fileupload 组件完成上传功能,Spring MVC只是对其进行了封装,简化开发,pom.xml
<dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.8.0</version> </dependency> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.4</version> </dependency>
springmvc.xml
为了把二进制数据解析成对象
<!-- 文件的上传--> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"></bean>
2.JSP页面
- input的type属性设置为file
- form表单的method设置为post
- form表单的enctype设置为multipart/form-data
<%@ 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"/> <input type="submit" value="提交"> <!-- 加上/代表从根目录也就是8080开始找 --> </form> <img src="${src}"/></body></html>
@Component@RequestMapping("/file")public class FileHandler { @PostMapping("/upload") public String upload(@RequestParam("img") MultipartFile img, HttpServletRequest request){ if (img.getSize()>0){ String path = request.getSession().getServletContext().getRealPath("file"); String filename = img.getOriginalFilename(); File descFile=new File(path, filename); try { img.transferTo(descFile); request.setAttribute("src", "/file/"+filename); } catch (IOException e) { e.printStackTrace(); } } return "upload"; }}
然后选择文件
提交
10.1.2多文件上传
<%@ 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></head><body> <form action="/file/uploads" method="post" enctype="multipart/form-data"> file1:<input type="file" name="imgs"/><br> file2:<input type="file" name="imgs"/><br> file3:<input type="file" name="imgs"/><br> <input type="submit" value="提交"/> </form><c:forEach items="${pathList}" var="path"> <img src="${path}" width="300px"></c:forEach>
@PostMapping("/uploads") public String uploads(@RequestParam("imgs") MultipartFile[] imgs,HttpServletRequest request){ List<String> pathList=new ArrayList<>(); for (MultipartFile img:imgs){ if (img.getSize()>0){ String path = request.getSession().getServletContext().getRealPath("file"); String filename = img.getOriginalFilename(); File descFile=new File(path, filename); try { img.transferTo(descFile); pathList.add("/file/"+filename); } catch (IOException e) { e.printStackTrace(); } } } request.setAttribute("pathList", pathList); return "uploads"; }
10.2文件下载
JSP页面通过超链接点击进行下载
<%@ page contentType="text/html;charset=UTF-8" language="java" %><html><head> <title>文件下载</title></head><body> <a href="/file/download?fileName=皮卡丘.jpg">皮卡丘.jpg</a> <a href="/file/download?fileName=柯南.png">柯南.png</a> <a href="/file/download?fileName=springmvc.png">springmvc.png</a></body></html>
Handler
@GetMapping("/download") public void download(String fileName, HttpServletRequest request, HttpServletResponse response) { if (fileName!=null){ String path = request.getSession().getServletContext().getRealPath("file"); File file=new File(path,fileName); OutputStream out=null; if (file.exists()){ //设置下载文件 response.setContentType("applicationContext/force-download"); //设置文件名 response.setHeader("Context-Disposition", "attachment;filename="+fileName); try { out=response.getOutputStream(); out.write(FileUtils.readFileToByteArray(file)); out.flush(); } catch (IOException e) { e.printStackTrace(); }finally { if (out!=null){ try { out.close(); } catch (IOException e) { e.printStackTrace(); } } } } } }
十一、拦截器
11.1过滤器、监听器、拦截器的对比
- Servlet:处理Reequest请求和Response响应
- 过滤器(Filter):对Request请求起到过滤作用,作用在Servlet之前,如果配置为 @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("目标Handler执行前执行MyInterceptor---->preHandle方法..."); return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("目标Handler执行后,视图执行前执行MyInterceptor---->postHandle方法..."); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("目标Handler执行后,视图执行后执行MyInterceptor---->afterCompletion方法..."); }}
拦截器配置1
<mvc:interceptors> <!-- 拦截器配置 --> <!-- 使用bean定义一个Interceptor 直接定义在mvc:interceptors根下面的Interceptor将拦截所有的请求 --> <bean class="com.zyh.interceptor.MyInterceptor"></bean> </mvc:interceptors>
拦截器配置方式2
<!-- 拦截器配置2 --> <mvc:interceptors> <!--定义在mvc:interceptor下面,可以自定义需要拦截的请求 如果有多个拦截器满足拦截处理的要求,则依据配置的先后顺序来执行 --> <mvc:interceptor> <!--通过mvc:mapping配置需要拦截的资源。支持通配符,可以配置多个 --> <mvc:mapping path=" @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("2.目标Handler执行前执行MyInterceptor2---->preHandle方法..."); return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("3.目标Handler执行后,视图执行前执行MyInterceptor2---->postHandle方法..."); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("5.目标Handler执行后,视图执行后执行MyInterceptor2---->afterCompletion方法..."); }}
Handler
@RequestMapping("/hello")@Controllerpublic class HelloContro @RequestMapping("/packageType") @ResponseBody public String packageType(@RequestParam(value = "id", required = true) Integer id) { System.out.println("拦截的方法..."); return "id=" + id; }}