案例需要的依赖
<?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> <!--jackson--> <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> <!-- servlet3.1规范的坐标 --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency> <!--jsp坐标--> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>jsp-api</artifactId> <version>2.0</version> <scope>provided</scope> </dependency> <!--spring的坐标--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.1.6.RELEASE</version> </dependency> <!--spring web的坐标--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>5.1.6.RELEASE</version> </dependency> <!--springmvc的坐标--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.1.6.RELEASE</version> </dependency> <!--Lombok--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.16.6</version> </dependency> </dependencies> <build> <plugins> <!--jdk编译插件--> <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> <!--tomcat插件--> <plugin> <groupId>org.apache.tomcat.maven</groupId> <!-- tomcat7的插件, 不同tomcat版本这个也不一样 --> <artifactId>tomcat7-maven-plugin</artifactId> <version>2.1</version> <configuration> <!-- 通过maven tomcat7:run运行项目时,访问项目的端口号 --> <port>80</port> <!-- 项目虚拟路径 如果配置的aa, 则访问路径为localhost:8080/aa--> <path>/</path> </configuration> </plugin> </plugins> </build> </project>
1 异步调用
1.1 发送异步请求
前端index.jsp代码
两个注意事项
1.前端需要通过contentType告诉后端是JSON数据,并且数据在请求体中
2.前端需要把数据转换成字符串再发送
<%@ 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",//用json,text后端封装复杂数据会失败报415,自行测试 success:function (data) { console.log(data) } }); }); }); </script> </body> </html>
java代码
package cn.oldlu.controller; import cn.oldlu.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); //不要直接返回字符串,返回对象,测试前端success接收到的是字符串还是对象 HashMap<Object, Object> map = new HashMap<>(); map.put("status","success"); return map; } }
2 异步请求-跨域访问
2.1 跨域访问介绍
- 当通过域名A下的操作访问域名B下的资源时,称为跨域访问
- 跨域访问时,会出现无法访问的现象
2.2 跨域环境搭建
当前电脑启动两台服务器会比较麻烦。
可以为当前电脑设置两个host域名,访问页面用A域名,页面访问后台用B域名
修改C:\Windows\System32\drivers\etc 目录下的 host文件,将该文件拷贝到其他盘,修改后替换原文件即可
添加如下内容
127.0.0.1 a.com 127.0.0.1 b.com
前端index.jsp代码 修改访问的服务的地址
<%@ 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",//用json,text后端封装复杂数据会失败报415,自行测试 success:function (data) { console.log(data) } }); }); }); </script> </body> </html>
在浏览器中访问a.com ,因为要发请求给http://b.com服务器,所以会报跨域错误
2.3 跨域访问支持
在接口所在的方法或者类上加@CrossOrigin注解
//使用@CrossOrigin开启跨域访问 //标注在处理器方法上方表示该方法支持跨域访问 //标注在处理器类上方表示该处理器类中的所有处理器方法均支持跨域访问 package cn.oldlu.controller; import cn.oldlu.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
请求处理过程解析
概念和作用
是一种动态拦截方法调用的机制
作用
1.在指定的方法调用前后执行预先设定后的的代码
2.阻止原始方法的执行
核心原理:AOP思想
拦截器链:多个拦截器按照一定的顺序,对原始被调用功能进行增强
拦截器VS过滤器
归属不同: Filter属于Servlet技术, Interceptor属于SpringMVC技术
拦截内容不同: Filter对所有访问进行增强, Interceptor仅针对SpringMVC的访问进行增强
3.2 自定义拦截器开发过程
第一步:实现HandlerInterceptor接口
//自定义拦截器需要实现HandleInterceptor接口 public class MyInterceptor implements HandlerInterceptor { //处理器运行之前执行 @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("前置运行----a1"); //返回值为false将拦截原始处理器的运行 //如果配置多拦截器,返回值为false将终止当前拦截器后面配置的拦截器的运行 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"); } //三个方法的运行顺序为 preHandle -> postHandle -> afterCompletion //如果preHandle返回值为false,三个方法仅运行preHandle }
第二步:配置拦截器
<mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/showPage"/> <bean class="com.itheima.interceptor.MyInterceptor"/> </mvc:interceptor> </mvc:interceptors>
注意:配置顺序为先配置执行位置,后配置执行类
3.3 拦截器执行流程
3.4 拦截器配置与方法参数
3.4.1 前置处理方法
原始方法之前运行
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 后置处理方法
原始方法运行后运行,如果原始方法被拦截,则不执行
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("postHandle"); }
参数
modelAndView:如果处理器执行完成具有返回结果,可以读取到对应数据与页面信息,并进行调整
3.4.3 完成处理方法
拦截器最后执行的方法,无论原始方法是否执行
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("afterCompletion"); }
参数
ex:如果处理器执行过程中出现异常对象,可以针对异常情况进行单独处理
3.5 拦截器配置项
<mvc:interceptors> <!--开启具体的拦截器的使用,可以配置多个--> <mvc:interceptor> <!--设置拦截器的拦截路径,支持*通配--> <!--/** 表示拦截所有映射--> <!--/* 表示拦截所有/开头的映射--> <!--/user/* 表示拦截所有/user/开头的映射--> <!--/user/add* 表示拦截所有/user/开头,且具体映射名称以add开头的映射--> <!--/user/*All 表示拦截所有/user/开头,且具体映射名称以All结尾的映射--> <mvc:mapping path="/*"/> <mvc:mapping path="/**"/> <mvc:mapping path="/handleRun*"/> <!--设置拦截排除的路径,配置/**或/*,达到快速配置的目的--> <mvc:exclude-mapping path="/b*"/> <!--指定具体的拦截器类--> <bean class="MyInterceptor"/> </mvc:interceptor> </mvc:interceptors>
责任链模式
责任链模式是一种行为模式
特征:着一条预先设定的任务链顺序执行,每个节点具有独立的工作任务
优势:
独立性:只关注当前节点的任务,对其他任务直接放行到下一节点
隔离性:具备链式传递特征,无需知晓整体链路结构,只需等待请求到达后进行处理即可
灵活性:可以任意修改链路结构动态新增或删减整体链路责任
解耦:将动态任务与原始任务解耦
弊端:
链路过长时,处理效率低下
可能存在节点上的循环引用现象,造成死循环,导致系统崩溃
4 异常处理
系统的Dao、Service、Controller出现都通过throws Exception向上抛出,最后由SpringMVC前端控制器交由异常处理器进行异常处理,如下图:
4.0 异常处理的方式
方式1(了解)
方式2(XML了解)
第一步 创建异常处理器类实现HandlerExceptionResolver
第二步 编写异常页面error.jsp
第三步:把自定义异常处理类注册到springmvc框架中
第四步:控制器中编写会发生异常的代码
测试异常跳转
方式3(注解)掌握
package cn.oldlu.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; }
4.2 自定义异常
//自定义异常继承RuntimeException,覆盖父类所有的构造方法 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); } }
异常触发方式
if(user.getName().trim().length()<4) { throw new BusinessException("用户名长度必须在2-4位之间,请重新输入! "); }
- 通过自定义异常将所有的异常现象进行分类管理,以统一的格式对外呈现异常消息