1 SpringMVC拦截器
SpringMVC中的拦截器主要用于拦截用户请求并做出相应的处理。比如:权限验证、记录请求信息的日志、判断用户是否登录等。
1.1 实现拦截器的两种方法
①实现HandlerInterceptor接口或继承该接口的实现类(如HandlerInterceptorAdapter)
MyInterceptor.java
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandler....");
return true;//返回true表示放行,可以继续到达handler*
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
/*handler处理单元返回ModelAndView时候进行拦截*/
System.out.println("postHandler...");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
/*页面渲染完毕,但是还没有给浏览器响应数据*/
System.out.println("afterCompletion..");;
}
}
在springmvc.xml中注册拦截器:
<!--注册拦截器-->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/login"/>
<bean class="com.zi.interceptor.MyInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>
②通过实现WebRequestInterceptor接口或继承该接口的实现类
1.2 拦截器、过滤器区别
1.拦截器是SpringMVC的,过滤器是java的servlet的
2.拦截器不依赖servlet,由Spring容器初始化的,过滤器依赖于servlet容器,由servlet容器初始化
3.拦截器只能对.action、.do这类似的请求起作用,而过滤器可以对几乎所有请求起作用(包括对静态资源的访问)
4.拦截器可以访问action上下文、值栈里面的对象,而过滤器不能
5.在action的生命周期中,拦截器可以被多次调用,而过滤器只能在容器初始化时被调用一次
6.拦截器可以获取IOC容器中的各个bean,而过滤器不方便获取;在拦截器中注入一个service,可以调用业务逻辑
1.3 拦截器的三个方法及作用
1.3.1 preHandle
/**
*
* @param request 请求对象
* @param response 响应对象
* @param handler 目标要调用的Handler
* @return 返回true表示放行
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//在请求到达我们定义的handler之前执行的
System.out.println("preHandler....");
//设置请求和响应的编码【防止乱码】
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
//判断是否登录,如果没有登录直接拦截
// User user = (User) request.getSession().getAttribute("user");
// if(null == user){
// response.sendRedirect("index.jsp");
// }
// return false;
return true;//返回true表示放行,可以继续到达handler*
}
1.3.2 postHandle
/**
*
* @param request
* @param response
* @param handler
* @param modelAndView controller响应的结果,视图和数据
* @throws Exception
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
/*handler处理单元返回ModelAndView时候进行拦截*/
System.out.println("postHandler...");
//控制数据
Map<String, Object> map = modelAndView.getModel();
String msg = (String) map.get("msg");
String newMsg = msg.replaceAll("脏话", "**");
map.put("msg", newMsg);
// modelAndView.setViewName("success");
//控制视图
// modelAndView.setViewName("/testDemo");
}
1.3.3 afterCompletion
/**
* 无论controller是否出现异常都会执行的操作,类似于finally
* 通常用来完成资源释放等操作
* @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("afterCompletion..");;
}
拦截器执行顺序
多个拦截器同时存在时,执行的顺序由配置顺序决定. 先配置谁, 谁就先执行.多个拦截器想象成"反弹"
2 SpringMVC异常
在Java中异常包括两类:预期异常(检查型异常)和运行时异常
系统的dao、service、controller出现都向上throws Exception,最后由springmvc前端控制器交由异常处理器进行异常处理,如下图:
2.1 SpringMVC异常处理的具体实现
2.1.1 使用@ExceptionHandler注解(只能处理当前controller中异常)
DemoExceptionController02.java:
@Controller
public class DemoExceptionController02 {
@RequestMapping("/test01")
public String test01(){
int i = 1 / 0;
return "success";
}
@RequestMapping("/test02")
public String test02(){
String str = null;
int length = str.length();
return "success";
}
@ExceptionHandler(value = {ArithmeticException.class})//处理数学异常
public ModelAndView handlerException(){
ModelAndView mv = new ModelAndView();
mv.setViewName("fail");
return mv;
}
}
2.1.2 使用@ControllerAdvice+@ExceptionHandler(优先级低于局部)
@ControllerAdvice
public class ExceptionHandler01 {
@ExceptionHandler(value = ArithmeticException.class)
public ModelAndView handlerException(){
ModelAndView mv = new ModelAndView();
mv.setViewName("index");
return mv;
}
}
注意:还需要再applicationContext.xml中配置扫描:
<context:component-scan base-package="com.zi.exceptionhandler"></context:component-scan>
2.1.3 使用SimpleMappingExceptionResolver
①XML方式
需要再springmvc.xml中配置:
<!--自定义异常解析器-->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver" id="exceptionResolver">
<property name="exceptionMappings">
<props>
<prop key="java.lang.ArithmeticException">redirect://error1.jsp</prop>
<prop key="java.lang.NullPointerException">redirect://error2.jsp</prop>
</props>
</property>
</bean>
②配置类方式【零XML配置】
ExceptionConfig.java:
@Configuration
public class ExceptionConfig {
@Bean
public SimpleMappingExceptionResolver getSimpleMappingExceptionResolver(){
SimpleMappingExceptionResolver resolver = new SimpleMappingExceptionResolver();
Properties prop = new Properties();
prop.put("java.lang.NullPointerException", "error1.jsp");
prop.put("java.lang.ArithmeticException", "error2.jsp");
resolver.setExceptionMappings(prop);
return resolver;
}
}
注意:还需在applicationContext.xml中配置包扫描
2.1.4 自定义类HandlerExceptionResolver【实现接口】
ExceptionHandler02.java:
/**
* 全局异常
*/
@Configuration
public class ExceptionHandler02 implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
ModelAndView mv = new ModelAndView();
if(ex instanceof NullPointerException){
mv.setViewName("error1");
}
if(ex instanceof ArithmeticException){
mv.setViewName("error2");
}
mv.addObject("msg", ex);
return mv;
}
}
3 SpringMVC其他注解
① @GetMapping、@PostMapping
例:@GetMapping = @RequestMapping + method = GET
②@RestController【相当于类中所有方法都加上@ResponseBody】
如果我们在一个controller中,想要返回json格式的数据,而不是通过返回String,从而对应视图,就需要添加@ResponseBody
③@JsonFormat
作用:处理响应json数据的处理
属性:
pattern:指定响应时间日期的格式
TImezone:执行响应的时区
testJson.jsp:
【注意:ajax中的type与method区别:method是jQuery1.9之后推出的新属性】
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
<script src="js/jquery-3.4.1.js"></script>
<script>
$(function(){
$.ajax({
url:"demo1",
type:"get",
data:"name=zhangsan&hiredate=2022-7-28",
success:function (result){
console.log(result)
}
})
})
</script>
</head>
<body>
</body>
</html>
EmpController.java:
@Controller
public class EmpController {
@RequestMapping("/demo1")
@ResponseBody
public Emp demo1(Emp emp){
System.out.println(emp);
return emp;
}
}
最终结果:
④@RequestBody
用于获取请求体json格式的字符串内容。直接使用得到是 key=value&key=value…结构的数据,get 请求方式不适用。
testJson.jsp
【注意加上contenType,并且方式为post】
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
<script src="js/jquery-3.4.1.js"></script>
<script>
var jsonObj = {name:"lisi", hiredate:"2035-9-5"}
var jsonStr = JSON.stringify(jsonObj);//'{"name":"lisi", "hiredate":"2035-9-5"}'
$(function(){
$.ajax({
url:"demo1",
method:"post",
data:jsonStr,
contentType:"application/json",//需要指定否则会报415【Unsupported Media Type】
success:function (result){
console.log(result)
}
})
})
</script>
</head>
<body>
</body>
</html>
EmpController
@Controller
public class EmpController {
@RequestMapping(value = "/demo1", method = RequestMethod.POST)
@ResponseBody
public Emp demo1(@RequestBody(required = false) Emp emp){//required = false 表明emp参数可以没有
System.out.println(emp);
return emp;
}
}
⑤@CrossOrigin
作用:解决ajax请求之间的跨域问题
浏览器有一个同源策略的约定。同源(同一个域):两个页面具有相同的协议protocol、主机host、端口号port
http://127.0.0.1:8080/zi/index.jsp 基础
https://127.0.0.1:8080/zi/index.jsp 协议不同
http://192.168.3.34:8080/zi/index.jsp IP不同
http://127.0.0.1:8080/zi/index.jsp IP不同
http://127.0.0.1:9090/zi/index.jsp 端口不同
http://localhost:8080/zi/index.jsp IP不同
属性:
orgins:允许可访问的域列表IP
maxAge:准备响应前的缓存持续的最大时间(秒为单位)
代码:
@Controller
@CrossOrigin(value = "http:domain.com", maxAge = 6000)
public class EmpController {
@RequestMapping(value = "/demo1", method = RequestMethod.POST)
@ResponseBody
public Emp demo1(@RequestBody(required = false) Emp emp){//required = false 表明emp参数可以没有
System.out.println(emp);
return emp;
}
}
拓展:
解决跨域问题的三种方案:
- 前端解决jsonp
- 后端解决:@CrossOrigin
- 后端解决:通过过滤器
第一种方案jsonp:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>JSONP 实例</title>
<script src="https://cdn.static.runoob.com/libs/jquery/1.8.3/jquery.js"></script>
</head>
<body>
<div id="divCustomers"></div>
<script>
$.getJSON("https://www.runoob.com/try/ajax/jsonp.php?jsoncallback=?", function(data) {
var html = '<ul>';
for(var i = 0; i < data.length; i++)
{
html += '<li>' + data[i] + '</li>';
}
html += '</ul>';
$('#divCustomers').html(html);
});
</script>
</body>
</html>
第三种方案:通过过滤器
/*请求地址白名单 *代表所有*/
resp.setHeader("Access-Control-Allow-Origin", "*")
/*请求方式白名单*/
resp.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE")
resp.setHeader("Access-Control-Max-Age", "3600")
resp.setHeader("Access-Control-Allow-Header", "x-requried-with")