二、异常处理
2.1.为什么要全局异常处理
在开发中,不管是dao层、service层还是controller层,都有可能抛出异常,在springmvc中,能将所有类型的异常处理从各处理过程解耦出来,既保证了相关处理过程的功能较单一,也实现了异常信息的统一处理和维护 ,全局异常处理是指在应用程序中对所有异常进行捕获和处理,而不是仅仅对特定的异常进行处理。
以下是一些原因说明为什么要全局异常处理:
- 统一处理异常:全局异常处理可以确保在应用程序的所有部分都使用相同的异常处理逻辑,从而使代码更加一致和易于维护。
- 简化代码:通过将异常处理逻辑集中在一个地方,可以减少代码冗余,并使代码更易于阅读和维护。
- 提高安全性:全局异常处理可以帮助防止未处理的异常导致的潜在安全问题,例如泄露敏感信息或允许攻击者执行恶意代码。
- 更好的用户体验:通过全局异常处理,应用程序可以在出现异常时给出更有用和友好的错误消息,从而提高用户体验。
综上所述,全局异常处理可以使应用程序更加健壮、一致、易于维护和安全,同时提供更好的用户体验。
小贴士:运行时异常和编译时异常的区别?
- 编译时异常(Checked Exception): 编译时异常是在编译阶段被检查出来的异常,必须进行处理,否则编译器会报错。常见的编译时异常有IOException、SQLException等。处理方式可以使用try-catch语句块来捕获和处理这些异常。
- 运行时异常(Runtime Exception): 运行时异常是在程序运行期间抛出的异常,如果不进行处理,程序会崩溃。常见的运行时异常有NullPointerException、ArrayIndexOutOfBoundsException等。这些异常通常是由程序逻辑错误引起的,因此无法在编译时进行检测。
2.2.异常处理思路
系统的dao、service、controller出现异常都通过throws Exception向上抛出,最后由springmvc前端控制器交由异常处理器进行异常处理。springmvc提供全局异常处理器(一个系统只有一个异常处理器)进行统一异常处理。
具体来说,异常处理的思路包括以下几个方面:
- 确定异常类型:在进行异常处理之前,需要先确定可能会出现哪些异常情况,以及这些异常情况对应的异常类型。
- 添加异常处理代码:在程序中添加相应的异常处理代码,用于捕获可能出现的异常,并进行相应的处理。
- 处理异常:根据不同的异常类型,采取不同的处理方式。例如,对于运行时异常,可以采取打印错误信息等方式进行处理;对于受检异常,则需要在方法声明中添加throws关键字,并在调用该方法时进行try-catch处理。
- 优化异常处理:在实际应用中,需要根据具体情况对异常处理进行优化。例如,可以使用多线程机制来提高程序的性能;或者使用日志系统来记录程序运行过程中出现的异常情况等。
2.3.SpringMVC异常分类
SpringMVC中的异常处理方式有三种:
- 使用Spring MVC提供的简单异常处理器SimpleMappingExceptionResolver;
- 实现Spring的异常处理接口HandlerExceptionResolver自定义自己的异常处理器;
- 使用@ControllerAdvice + @ExceptionHandler
2.4.案例实战
2.4.1.异常处理方式①
SpringMVC中自带了一个异常处理器叫SimpleMappingExceptionResolver,该处理器实现了HandlerExceptionResolver 接口,全局异常处理器都需要实现该接口。
spring-mvc.xml
<!-- springmvc提供的简单异常处理器 --> <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"> <!-- 定义默认的异常处理页面 --> <property name="defaultErrorView" value="error"/> <!-- 定义异常处理页面用来获取异常信息的变量名,也可不定义,默认名为exception --> <property name="exceptionAttribute" value="ex"/> <!-- 定义需要特殊处理的异常,这是重要点 --> <property name="exceptionMappings"> <props> <prop key="java.lang.RuntimeException">error</prop> </props> <!-- 还可以定义其他的自定义异常 --> </property> </bean>
error.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>系统繁忙</title> </head> <body> ${ex}<br> <img src="${pageContext.request.contextPath }/static/1.jpg" style="height: 1000px;width: 1550px;"> </body> </html>
注:页面跳转由SpringMVC来接管了,所以此处的定义默认的异常处理页面都应该配置成逻辑视图名。
我们没有配置这段代码之前,以下的页面是我们不想看到的,看看配置后是怎么样的吧?
配置异常处理:
明显后面做了异常处理的看起来更为舒服。
2.4.2.异常处理方式②
创建一个名为exception的包将我们的GlobalException类放入其中。
GlobalException.java
public class GlobalException extends RuntimeException { public GlobalException() { } public GlobalException(String message) { super(message); } public GlobalException(String message, Throwable cause) { super(message, cause); } public GlobalException(Throwable cause) { super(cause); } public GlobalException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { super(message, cause, enableSuppression, writableStackTrace); } }
创建一个名为Component的包将我们的GlobalExceptionHandler类放入其中。
GlobalExceptionHandler.java
@Component public class GlobalExceptionHandler implements HandlerExceptionResolver { // 跳转错误页面 @Override public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) { ModelAndView mv = new ModelAndView(); mv.setViewName("error"); if (e instanceof GlobalException){ GlobalException globalException = (GlobalException) e; mv.addObject("ex",globalException.getMessage()); mv.addObject("msg","全局异常...."); }else if (e instanceof RuntimeException){ RuntimeException runtimeException = (RuntimeException) e; mv.addObject("ex",runtimeException.getMessage()); mv.addObject("msg","运行时异常...."); }else{ mv.addObject("ex",e.getMessage()); mv.addObject("msg","其他异常...."); } return mv; } }
1.通过instanceof判断异常类型
2.通过设置mv.setView(new MappingJackson2JsonView())方式返回JSON数据
这时候我们来访问以下http://localhost:8080/xwzyssm/stu/json/jsonStr
因为异常处理会根据我们的异常问题进行判断map保存输出到前端,所以在JSP页面上用EL表达式捕捉msg信息就可以知道问题是什么更为直观。
2.4.3.异常处理方式③
GlobalExceptionResolver.java
@ControllerAdvice public class GlobalExceptionResolver { // 返回错误json数据 @ResponseBody @ExceptionHandler public Map handler(Exception e){ Map map = new HashMap(); if (e instanceof GlobalException){ GlobalException globalException = (GlobalException) e; map.put("ex",globalException.getMessage()); map.put("msg","全局异常...."); }else if (e instanceof RuntimeException){ RuntimeException runtimeException = (RuntimeException) e; map.put("ex",runtimeException.getMessage()); map.put("msg","运行时异常...."); }else { map.put("ex",e.getMessage()); map.put("msg","其它异常...."); } return map; } }
这种方式是将我们的错误信息进行map保存然后转换为JSON格式输出在页面上。
这时候我们来访问以下 http://localhost:8080/xwzyssm/stu/json/jsonStr
2.5.响应封装类
通过我刚刚的解释想必大家对异常处理有了一定的理解,但是大家有没有发现,异常处理类中反复的需要定义Map,随后.put添加数据,我们能否对以上代码进行优化呢?能!!下面请欣赏小编所需的R工具类。
R.java
public class R extends HashMap { public R data(String key, Object value) { this.put(key, value); return this; } public static R ok(int code, String msg) { R r = new R(); r.data("success", true).data("code", code).data("msg", msg); return r; } public static R error(int code, String msg) { R r = new R(); r.data("success", false).data("code", code).data("msg", msg); return r; } public static R ok(int code, String msg,Object data) { R r = new R(); r.data("success", true).data("code", code).data("msg", msg).data("data", data); return r; } public static R ok(int code, String msg, long count, Object data) { R r = new R(); r.data("success", true).data("code", code).data("msg", msg).data("count", count).data("data", data); return r; } }
GlobalExceptionResolver.java
@ControllerAdvice public class GlobalExceptionResolver { // 响应封装类 @ResponseBody @ExceptionHandler public Map handler(Exception e){ if (e instanceof GlobalException){ GlobalException globalException = (GlobalException) e; return R.ok(500,"全局异常....",globalException.getMessage()); }else if (e instanceof RuntimeException){ RuntimeException runtimeException = (RuntimeException) e; return R.ok(500,"运行时异常....",runtimeException.getMessage()); }else { return R.ok(500,"其他异常....",e.getMessage()); } } }
这时候我们来访问以下 http://localhost:8080/xwzyssm/stu/json/jsonStr
到这里我的分享就结束了,欢迎到评论区探讨交流!!
💖如果觉得有用的话还请点个赞吧 💖