0 今日案例需要的依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
| <?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>springmvc021</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> <dependencies> <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> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>jsp-api</artifactId> <version>2.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.1.6.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>5.1.6.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.1.6.RELEASE</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.16.6</version> </dependency> </dependencies>
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.1</version> <configuration> <source>1.8</source> <target>1.8</target> <encoding>utf-8</encoding> </configuration> </plugin>
<plugin> <groupId>org.apache.tomcat.maven</groupId> <artifactId>tomcat7-maven-plugin</artifactId> <version>2.1</version> <configuration> <port>80</port> <path>/</path> </configuration> </plugin> </plugins> </build> </project>
|
1 异步调用@RequestBody
1.1 发送异步请求
前端index.jsp代码
两个注意事项
1.前端需要通过contentType告诉后端是JSON数据,并且数据在请求体中
2.前端需要把数据转换成字符串再发送
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <body> <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script> <button id="btn1">点击发送请求</button>
<script> $(function () { $("#btn1").on('click',function () { var users = [ { name:"jack", age:34 }, { name:"tom", age:32 },{ name:"张三", age:3 }
]; $.ajax({ type:"post", url:"/user/ajax", data:JSON.stringify(users), contentType:"application/json", success:function (data) { console.log(data) } }); }); }); </script> </body> </html>
|
java代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| package cn.itcast.controller;
import cn.itcast.domain.User; import org.springframework.format.annotation.DateTimeFormat; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody;
import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map;
@Controller @RequestMapping("/user") public class UserController {
@RequestMapping("/ajax") @ResponseBody public Map<Object, Object> ajax(@RequestBody List<User> users){ System.out.println(users);
HashMap<Object, Object> map = new HashMap<>(); map.put("status","success"); return map; } }
|
总结 @RequestBody的作用
用来获取请求体中的数据,前端请求头的类型Content-Type:applicaiton/json,还要求前段传到后端的数据必须是string类型的json数据
如果前端正常的发送表单数据(比如前端使用了form表单,又或者是正常调用$.get,$.post,$.ajax,并没有指定contnetType),那么后端获取数据的时候不需要加@RequestBody
2 异步请求-跨域访问
2.1 跨域访问介绍
- 当通过域名A下的操作访问域名B下的资源时,称为跨域访问
- 跨域访问时,会出现无法访问的现象
(https://gitee.com/haoyongliang/resources/raw/master/images/springmvc/day02/image-20200427162623591.png)
2.2 跨域环境搭建
当前电脑启动两台服务器会比较麻烦。
可以为当前电脑设置两个host域名,访问页面用A域名,页面访问后台用B域名
修改C:\Windows\System32\drivers\etc 目录下的 host文件,将该文件拷贝到其他盘,修改后替换原文件即可
添加如下内容
1 2
| 127.0.0.1 a.com 127.0.0.1 b.com
|
前端index.jsp代码 修改访问的服务的地址
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <body> <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script> <button id="btn1">点击发送请求</button>
<script> $(function () { $("#btn1").on('click',function () { var users = [ { name:"jack", age:34 }, { name:"tom", age:32 },{ name:"张三", age:3 }
]; $.ajax({ type:"post", url:"http://b.com/user/ajax", data:JSON.stringify(users), contentType:"application/json", success:function (data) { console.log(data) } }); }); }); </script> </body> </html>
|
在浏览器中访问a.com ,因为要发请求给http://b.com服务器,所以会报跨域错误
(https://gitee.com/haoyongliang/resources/raw/master/images/springmvc/day02/1593099295180.png)
2.3 跨域访问支持
在接口所在的方法或者类上加@CrossOrigin注解
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
|
package cn.itcast.controller;
import cn.itcast.domain.User; import org.springframework.format.annotation.DateTimeFormat; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody;
import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map;
@Controller @RequestMapping("/user") @CrossOrigin public class UserController {
@RequestMapping("/ajax") @ResponseBody public Map<Object, Object> ajax(@RequestBody List<User> users){ System.out.println(users);
HashMap<Object, Object> map = new HashMap<>(); map.put("status","success"); return map; } }
|
3 拦截器
3.1 拦截器Interceptor
请求处理过程解析
(https://gitee.com/haoyongliang/resources/raw/master/images/springmvc/day02/image-20200427164038103.png)
概念和作用
是一种动态拦截方法调用的机制
作用
1.在指定的方法调用前后执行预先设定后的的代码
2.阻止原始方法的执行
核心原理:AOP思想
拦截器链:多个拦截器按照一定的顺序,对原始被调用功能进行增强
拦截器VS过滤器
归属不同: Filter属于Servlet技术, Interceptor属于SpringMVC技术
拦截内容不同: Filter对所有访问进行增强, Interceptor仅针对SpringMVC的访问进行增强
(https://gitee.com/haoyongliang/resources/raw/master/images/springmvc/day02/image-20200427164512745.png)
3.2 自定义拦截器开发过程
第一步:实现HandlerInterceptor接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| public class MyInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("前置运行----a1"); return true; }
@Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("后置运行----b1"); }
@Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("完成运行----c1"); }
}
|
第二步:配置拦截器
1 2 3 4 5 6
| <mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/showPage"/> <bean class="com.itheima.interceptor.MyInterceptor"/> </mvc:interceptor> </mvc:interceptors>
|
注意:配置顺序为先配置执行位置,后配置执行类
3.3 拦截器执行流程
(https://gitee.com/haoyongliang/resources/raw/master/images/springmvc/day02/image-20200427164840131.png)
3.4 拦截器配置与方法参数
3.4.1 前置处理方法
原始方法之前运行
1 2 3 4 5 6
| public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("preHandle"); return true; }
|
- 参数
request:请求对象
response:响应对象
handler:被调用的处理器对象,本质上是一个方法对象,对反射中的Method对象进行了再包装 - 返回值
返回值为false,被拦截的处理器将不执行
3.4.2 后置处理方法
原始方法运行后运行,如果原始方法被拦截,则不执行
1 2 3 4 5 6
| public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("postHandle"); }
|
参数
modelAndView:如果处理器执行完成具有返回结果,可以读取到对应数据与页面信息,并进行调整
3.4.3 完成处理方法
拦截器最后执行的方法,无论原始方法是否执行
1 2 3 4 5 6
| public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("afterCompletion"); }
|
参数
ex:如果处理器执行过程中出现异常对象,可以针对异常情况进行单独处理
3.5 拦截器配置项
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/*"/> <mvc:mapping path="/**"/> <mvc:mapping path="/handleRun*"/> <mvc:exclude-mapping path="/b*"/> <bean class="MyInterceptor"/> </mvc:interceptor> </mvc:interceptors>
|
3.6 多拦截器配置
(https://gitee.com/haoyongliang/resources/raw/master/images/springmvc/day02/image-20200427171422781.png)
责任链模式
责任链模式是一种行为模式
特征:
沿着一条预先设定的任务链顺序执行,每个节点具有独立的工作任务
优势:
独立性:只关注当前节点的任务,对其他任务直接放行到下一节点
隔离性:具备链式传递特征,无需知晓整体链路结构,只需等待请求到达后进行处理即可
灵活性:可以任意修改链路结构动态新增或删减整体链路责任
解耦:将动态任务与原始任务解耦
弊端:
链路过长时,处理效率低下
可能存在节点上的循环引用现象,造成死循环,导致系统崩溃
4 异常处理
系统的Dao、Service、Controller出现都通过throws Exception向上抛出,最后由SpringMVC前端控制器交由异常处理器进行异常处理,如下图:
(https://gitee.com/haoyongliang/resources/raw/master/images/springmvc/day02/1593101330925.png)
4.0 异常处理的方式
方式1(了解)
使用Spring MVC提供的简单异常处理器SimpleMappingExceptionResolver
(https://gitee.com/haoyongliang/resources/raw/master/images/springmvc/day02/1593101397999.png)
方式2(XML了解)
第一步 创建异常处理器类实现HandlerExceptionResolver
(https://gitee.com/haoyongliang/resources/raw/master/images/springmvc/day02/1593101437662.png)
第二步 编写异常页面error.jsp
(https://gitee.com/haoyongliang/resources/raw/master/images/springmvc/day02/1593101464016.png)
第三步:把自定义异常处理类注册到springmvc框架中
(https://gitee.com/haoyongliang/resources/raw/master/images/springmvc/day02/1593101488122.png)
第四步:控制器中编写会发生异常的代码
(https://gitee.com/haoyongliang/resources/raw/master/images/springmvc/day02/1593101511200.png)
测试异常跳转
(https://gitee.com/haoyongliang/resources/raw/master/images/springmvc/day02/1593101520636.png)
方式3(注解)掌握
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| package cn.itcast.exception;
import org.springframework.stereotype.Component; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;
@ControllerAdvice public class ExceptionAdvice { @ExceptionHandler(RuntimeException.class) public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
ModelAndView modelAndView = new ModelAndView(); modelAndView.setViewName("/error.jsp"); modelAndView.addObject("msg","系统出错"); return modelAndView; } }
|
(https://gitee.com/haoyongliang/resources/raw/master/images/springmvc/day02/1593153998744.png)
4.1 异常处理解决方案
- 异常处理方案
- 业务异常:
发送对应消息传递给用户,提醒规范操作 - 系统异常:
发送固定消息传递给用户,安抚用户
发送特定消息给运维人员,提醒维护
记录日志 - 其他异常:
发送固定消息传递给用户,安抚用户
发送特定消息给编程人员,提醒维护
纳入预期范围内
记录日志
(https://gitee.com/haoyongliang/resources/raw/master/images/springmvc/day02/1593156991219.png)
4.2 自定义异常
(https://gitee.com/haoyongliang/resources/raw/master/images/springmvc/day02/1593102161185.png)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public class BusinessException extends RuntimeException { public BusinessException() { }
public BusinessException(String message) { super(message); }
public BusinessException(String message, Throwable cause) { super(message, cause); }
public BusinessException(Throwable cause) { super(cause); }
public BusinessException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { super(message, cause, enableSuppression, writableStackTrace); } }
|
5 实用技术
5.1 文件上传下载
前端页面
必表单中的三个注意事项
- input的type必须是file类型
- 表单的提交方式必须是post
- 表单的enctype属性的值必须是”multipart/form-data”
页面表单代码
(https://gitee.com/haoyongliang/resources/raw/master/images/springmvc/day02/1593102313554.png)
(https://gitee.com/haoyongliang/resources/raw/master/images/springmvc/day02/1593102324376.png)
单文件上传
1.导入fileupload和common-io依赖
1 2 3 4 5 6 7 8 9 10
| <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.3.1</version> </dependency> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.3</version> </dependency>
|
2.配置文件上传解析器
(https://gitee.com/haoyongliang/resources/raw/master/images/springmvc/day02/1593102410802.png)
B表示Byte
1M=1024*1024
注意事项:bean的id值必须和我的保持一致,不然后台获取不到表单数据
3.编写控制器
(https://gitee.com/haoyongliang/resources/raw/master/images/springmvc/day02/1593102437307.png)
(https://gitee.com/haoyongliang/resources/raw/master/images/springmvc/day02/1593102446579.png)
多文件上传
页面代码
(https://gitee.com/haoyongliang/resources/raw/master/images/springmvc/day02/1593102484899.png)
页面效果
(https://gitee.com/haoyongliang/resources/raw/master/images/springmvc/day02/1593102497607.png)
控制器
(https://gitee.com/haoyongliang/resources/raw/master/images/springmvc/day02/1593102515834.png)
(https://gitee.com/haoyongliang/resources/raw/master/images/springmvc/day02/1593102523368.png)
总结
1.页面中的多个上传选项name要一致
2.后端接受文件上传数据时使用数组接收
5.2 文件上传注意事项
- 文件命名问题, 获取上传文件名,并解析文件名与扩展名
- 文件名过长问题
- 文件保存路径
- 重名问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| @RequestMapping(value = "/fileupload")
public String fileupload(MultipartFile file,MultipartFile file1,MultipartFile file2, HttpServletRequest request) throws IOException { System.out.println("file upload is running ..."+file); if(!file.isEmpty()){ String fileName = file.getOriginalFilename(); String realPath = request.getServletContext().getRealPath("/images"); file.transferTo(new File(realPath,file.getOriginalFilename())); } if(!file1.isEmpty()){ String fileName = file1.getOriginalFilename(); String realPath = request.getServletContext().getRealPath("/images"); file1.transferTo(new File(realPath,file1.getOriginalFilename())); } if(!file2.isEmpty()){ String fileName = file2.getOriginalFilename(); String realPath = request.getServletContext().getRealPath("/images"); file2.transferTo(new File(realPath,file2.getOriginalFilename())); } return "page.jsp"; }
|
5.4 Restful风格配置
5.4.1 Rest
- Rest( REpresentational State Transfer) 一种网络资源的访问风格,定义了网络资源的访问方式
- Restful是按照Rest风格访问网络资源
- 优点
隐藏资源的访问行为,通过地址无法得知做的是何种操作
书写简化
5.4.2 Rest行为约定方式
GET(查询) http://localhost/user/1 GET
POST(保存) http://localhost/user POST
PUT(更新) http://localhost/user PUT
DELETE(删除) http://localhost/user DELETE
注意:上述行为是约定方式,约定不是规范,可以打破,所以称Rest风格,而不是Rest规范
5.4.3 Restful开发入门
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| @RestController
@RequestMapping("/user/") public class UserController {
@RequestMapping("/user/{id}") public String restLocation(@PathVariable Integer id){ System.out.println("restful is running ...."); return "success.jsp"; }
@RequestMapping("{id}") public String restLocation2(@PathVariable Integer id){ System.out.println("restful is running ....get:"+id); return "success.jsp"; }
@RequestMapping(value = "{id}",method = RequestMethod.GET) @GetMapping("{id}") public String get(@PathVariable Integer id){ System.out.println("restful is running ....get:"+id); return "success.jsp"; }
@RequestMapping(value = "{id}",method = RequestMethod.POST) @PostMapping("{id}") public String post(@PathVariable Integer id){ System.out.println("restful is running ....post:"+id); return "success.jsp"; }
@RequestMapping(value = "{id}",method = RequestMethod.PUT) @PutMapping("{id}") public String put(@PathVariable Integer id){ System.out.println("restful is running ....put:"+id); return "success.jsp"; }
@RequestMapping(value = "{id}",method = RequestMethod.DELETE) @DeleteMapping("{id}") public String delete(@PathVariable Integer id){ System.out.println("restful is running ....delete:"+id); return "success.jsp"; } }
|
1 2 3 4 5 6 7 8 9
| <filter> <filter-name>HiddenHttpMethodFilter</filter-name> <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class> </filter> <filter-mapping> <filter-name>HiddenHttpMethodFilter</filter-name> <servlet-name>DispatcherServlet</servlet-name> </filter-mapping>
|
开启SpringMVC对Restful风格的访问支持过滤器,即可通过页面表单提交PUT与DELETE请求
页面表单使用隐藏域提交请求类型,参数名称固定为_method,必须配合提交类型method=post使用
1 2 3 4
| <form action="/user/1" method="post"> <input type="hidden" name="_method" value="PUT"/> <input type="submit"/> </form>
|
Restful请求路径简化配置方式
1 2 3 4 5 6 7 8
| @RestController public class UserController { @RequestMapping(value = "/user/{id}",method = RequestMethod.DELETE) public String restDelete(@PathVariable String id){ System.out.println("restful is running ....delete:"+id); return "success.jsp"; } }
|
5.5 postman工具安装与使用
postman 是 一款可以发送Restful风格请求的工具,方便开发调试。首次运行需要联网注册
(https://gitee.com/haoyongliang/resources/raw/master/images/springmvc/day02/image-20200427180851880.png)
面试题
spring里面用到了哪些设计模式?
代理模式
工厂模式
单例模式
责任链模式
模板模式: 模版方法模式定义了一个操作中的算法骨架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构,就可以重定义该算法的某些特定步骤
https://www.cnblogs.com/Adam-Ye/p/13638784.html
拦截器和过滤器的区别
过滤器是web核心三大组件之一,可以增强所有的请求和响应。
拦截器是springmvc的一个特有功能,理论上只能增强处理器(处理器:controller中的方法),底层是AOP技术。