统一异常处理和拦截器
1.全局异常处理机制
1.异常处理两种方式
开发过程中是不可避免地会出现各种异常情况的,例如网络连接异常、数据格式异常、空指针异常等等。异常的出现可能导致程序的运行出现问题,甚至直接导致程序崩溃。因此,在开发过程中,合理处理异常、避免异常产生、以及对异常进行有效的调试是非常重要的。
对于异常的处理,一般分为两种方式:
编程式异常处理:是指在代码中显式地编写处理异常的逻辑。它通常涉及到对异常类型的检测及其处理,例如使用 try-catch 块来捕获异常,然后在 catch 块中编写特定的处理代码,或者在 finally 块中执行一些清理操作。在编程式异常处理中,开发人员需要显式地进行异常处理,异常处理代码混杂在业务代码中,导致代码可读性较差。
声明式异常处理:则是将异常处理的逻辑从具体的业务逻辑中分离出来,通过配置等方式进行统一的管理和处理。在声明式异常处理中,开发人员只需要为方法或类标注相应的注解(如 `@Throws` 或 `@ExceptionHandler`),就可以处理特定类型的异常。相较于编程式异常处理,声明式异常处理可以使代码更加简洁、易于维护和扩展。
2.基于注解异常声明异常处理 (Springmvc异常处理机制)
概念:配置出现什么异常显示对应的错误页面。异常类型和错误页面映射关系
1.声明异常处理控制器类,统一定义异常处理handler方法!
异常处理handler方法和普通的handler方法参数接收和响应都一致!
只不过异常处理handler方法要映射异常,发生对应的异常会调用!
普通的handler方法要使用@RequestMapping注解映射路径,发生对应的路径调用!
package com.bdqn.exceptionhandler;/*** 全局异常处理器,内部可以定义异常处理Handler!* @ControllerAdvice 代表当前类的异常处理controller!*/@ControllerAdvicepublic class GlobalExceptionHandler {/*** @ExceptionHandler(NullPointerException.class)* 该注解标记异常处理Handler,并且指定发生异常调用该方法!* 异常处理handler,当发生空指针异常会触发此方法!* @param e 获取异常对象!* @return 返回handler处理结果!*/@ExceptionHandler(NullPointerException.class)public Object handlerNullException(NullPointerException e){return "null_error";}/*** 所有异常都会触发此方法!但是如果有具体的异常处理Handler!* 具体异常处理Handler优先级更高!* 例如: 发生NullPointerException异常!会触发handlerNullException方法,不会触发handlerException方法!*/@ExceptionHandler(Exception.class)public Object handlerException(Exception e){return "error";}} |
2.配置文件扫描控制器类配置
确保异常处理控制类被扫描
@ComponentScan({"com.bdqn.controller","com.bdqn.exceptionhandler"})public class SpringMvcConfig implements WebMvcConfigurer { } |
3.创建错误页面error.jsp 、null_error.jsp
<body><h1>出现xxx异常!</h1><img src="imgs/error1.jpg" /></body> |
3.Jsp页面异常处理
<%@page contentType="text/html; UTF-8" pageEncoding="UTF-8" errorPage="error.jsp" %> <html><body><% int num=10/0;%><h2>index.jsp</h2></body></html> |
4.拦截器 Interceptor
在程序中,使用拦截器在请求到达具体 控制器handler 方法前,统一执行检测
![]() |
4.1拦截器和过滤器区别
1.过滤器:工作在 Servlet 容器中,可以过滤所有资源,包括静态和jsp 2.拦截器:工作在 SpringMVC 的基础上,只针对拦截控制器Controller 选择:功能需要如果用 SpringMVC 的拦截器能够实现,就不使用过滤器 ![]() |
4.2 实现步骤
准备控制器的类
@Controllerpublic class TestController {@RequestMapping("/demo1")public String demo1() {System.out.println("执行demo1"); return "index";}@RequestMapping("/demo2")public String demo2() {System.out.println("demo2...");return "index";}@RequestMapping("/demo3")public String demo3() {System.out.println("demo3...");return "index";}} |
- 编写拦截器的java类,实现HandlerInterceptor接口
package com.bdqn.interceptor;public class MyInterceptor implements HandlerInterceptor {//在控制器之前执行,并且返回是否放行。true:放行,false:不放行public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println("控制器之前执行...");return true;}//在控制器执行之后,页面显示之前执行。控制器报错不执行!public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception{System.out.println("控制器之后,页面之前执行...");}//页面显示完成之后,执行public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {System.out.println("页面之后执行...");}} |
2. 修改SpringMvcConfig 配置类添加拦截器
@Configuration@EnableWebMvc@ComponentScan({"com.bdqn.controller"})public class SpringMvcConfig implements WebMvcConfigurer {/*** 配置拦截器*/@Overridepublic void addInterceptors(InterceptorRegistry registry) {//将拦截器添加到Springmvc环境,默认拦截所有Springmvc分发的请求registry.addInterceptor(new MyInterceptor());}} |
3. 配置详解
1. 默认拦截全部
@Override |
2.精准配置
@Overridepublic void addInterceptors(InterceptorRegistry registry) { //精准匹配,设置拦截器处理指定请求 路径可以设置一个或者多个,为项目下路径即可//addPathPatterns("/path") 添加拦截路径//也支持 /* 和 /** 模糊路径。 * 任意一层字符串 ** 任意层 任意字符串registry.addInterceptor(new MyInterceptor()).addPathPatterns("/demo01","/demo02");} |
3.排除配置
@Overridepublic void addInterceptors(InterceptorRegistry registry) { //排除匹配,排除应该在匹配的范围内排除//excludePathPatterns("/path"); 排除路径,排除应该在拦截的范围内registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**").excludePathPatterns("/demo01");} |
4.多个拦截器执行顺序
1. preHandle() 方法:SpringMVC 会把所有拦截器收集到一起,然后按照配置顺序调用各个 preHandle() 方法。 2. postHandle() 方法:SpringMVC 会把所有拦截器收集到一起,然后按照配置相反的顺序调用各个 postHandle() 方法。 3. afterCompletion() 方法:SpringMVC 会把所有拦截器收集到一起,然后按照配置相反的顺序调用各个 afterCompletion() 方法。 |
后台管理系统搭建:
1.MVC三层架构层
Pojo Dao Service Controllor interceptor Jsp |
2.jsp页面要求
1. 除了login.jsp以外的所有页面都不能直接访问。必须放到/WEB-INF/pages/文件夹下。 /WEB-INF/文件下内容都不能直接访问。 2. 要想访问/WEB-INF/下的页面必须通过控制器才能访问。 控制器编写访问页面的控制器方法 @Controller public class PageController { /*根据控制方法,显示指定页面*/ @RequestMapping("{page}") public String page(@PathVariable String page) { return page; } } 3. 配置视图解析器 /*** 配置视图解析器*/@Overridepublic void configureViewResolvers(ViewResolverRegistry registry) {//加上前缀和后缀registry.jsp("/WEB-INF/pages/",".jsp");} |
3.登录拦截器 LoginInterceptor
/*进行登录验证*/ public class LoginInterceptor implements HandlerInterceptor {/*** 进入控制器之前执行,先验证用户是否登录*/public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {HttpSession session=request.getSession();User user=(User)session.getAttribute("user");//判断用户是否登录,如果没有登录,跳转登录页面,不放行if(user==null){response.sendRedirect("login.jsp");return false;}return true;}} |
4. SpringMvc中配置登录的拦截器
/*** 配置拦截器*/public void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new LoginInterceptor()).excludePathPatterns("/user/login");} |
注意:会出现资源访问的路径问题
- Jsp中静态资源从项目名开始写路径
Jsp获取当前项目名称
${pageContext.request.contextPath} |
2.Controller中的跳转到控制器请加上/,代表项目名称
return "redirect:/getAll"; |