1、基本简介
1.1、为什么需要异常处理?
先来看看web请求没有异常处理的页面是如何的。(如下图)
显然回答这个问题很容易:
-
首先这不是一个好的UI设计;大多数用户是不懂这些错误代码和报错的,他也不知道它是因为什么原因导致的错误;因此我们有必要在遇到用户不正确使用的时候给予一个很好的答复。
-
其次:对于一些敏感的错误,会泄露一些信息;容易给不法分子有可趁之机。因此非常有必要对异常进行处理!
1.2、SpringMVC框架提供的异常处理机制优势是什么?
-
Java异常处理机制:通过try - catch - finally代码块进行捕捉。但是这会带来一些弊端。大多数异常是运行时异常,或者说一份逻辑代码中可能存在非常多的异常需要进行不同的处理这时候就需要不断地去叠加catch。这肯定不是一个明智的选择!
-
MVC框架异常处理机制:在可能存在的异常的Controller方法上添加异常抛出,不在显示的在逻辑代码中进行捕捉;也就是说不需要try - catch -finally语句,预判异常类型在方法中显示的抛出,然后交给异常处理器进行处理即可!
1.3、注解支持
-
MVC框架的异常处理使用的是@ControllerAdvice注解、@ExceptionHandler注解进行异常的集中式处理。
-
核心原理:AOP,以横切的姿势动态的增强Controller。
-
@ControllerAdvice注解:@ControllerAdvice = @Controller + @Aspect注解。显然加载类上注册一个bean对象,并且该类是一个切面类。
-
@ExceptionHandler注解:加在方法上,内部就一个属性value数组表示可以处理的异常类型;当发生这些异常时交给这个方法处理!
2、准备工作
该项目以用户登录输入姓名、年龄为参数;验证这两个参数的合法性。当出现不合法抛出不同的异常!
2.1、创建页面
在WEB-INF/page/文件夹下放入center.jsp表示成功页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>主页面</title>
</head>
<body>
<h1>状态: ${msg}</h1>
<h1>姓名: ${name}</h1>
<h1>年龄: ${age}</h1>
</body>
</html>
在WEB-INF/page/文件夹下放入error.jsp表示错误页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>错误</title>
</head>
<body>
<h1>错误信息: ${msg}</h1>
<img src="${pageContext.request.contextPath}/${path}" height="500" width="500"/>
</body>
</html>
将webapp/index.jsp改为如下代码用户提交请求,提交一个用户姓名、以及用户年龄。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>首页面</title>
</head>
<body>
<form action="${pageContext.request.contextPath}/Exception/t1" method="post">
姓名: <input type="text" name="name" required>
<hr/>
年龄: <input type="text" name="age" required>
<hr/>
<input type="submit" value="提交"><input type="reset" value="重置">
</form>
</body>
</html>
2.2、其他
-
在webapp下创建一个static文件夹,里面存放各种各样的资源,报错一些错误图之类的。这里放age.jpg、name.jpg、other.jpg表示三种错误图。
-
配置基本的web.xml文件,写个DispatcherServlet前端控制器就可以、防止乱码写个过滤器。
3、逻辑层的编写
逻辑层主要包括三部分:编写异常类、controller层、以及多出来的异常处理handler。
-
手动声明一些异常、不使用系统的异常。(可以抛出系统写好的异常)
-
controller层依旧不变,只是在方法上多了一个异常抛出;内部不在捕获异常,遇到异常直接抛出给框架,框架交给ExceptionHandler集中处理!
-
handler:用于集中式的处理异常、不同的方法处理不同的异常类型。使用@ExceptionHandler注解中的value指定异常处理。
3.1、编写异常类
创建InfoException类并且继承Exception父类。
package com.exception;
public class InfoException extends Exception{
public InfoException() {
}
public InfoException(String message) {
super(message);
}
}
创建AgeException类继承InfoException类。
package com.exception;
public class AgeException extends InfoException{
public AgeException() {
}
public AgeException(String message) {
super(message);
}
}
创建NameException类继承InfoException类。
package com.exception;
public class NameException extends InfoException{
public NameException() {
}
public NameException(String message) {
super(message);
}
}
3.2、Controller
controller处理比较简单:
-
当名字不是admin时会抛出NameException异常。
-
当年龄超过150时抛出AgeException异常
-
当年龄不是合法的数字时会出现其他无法处理的异常。
@Controller
@RequestMapping("/Exception")
public class TestController {
@RequestMapping("/t1")
public ModelAndView test1(String name, Integer age) throws InfoException {
ModelAndView mv = new ModelAndView();
mv.addObject("msg", "Successful!");
if("admin".equals(name) == false){
throw new NameException("姓名不对头!");
}
System.out.println(name);
System.out.println(age.toString());
if(age > 150){
throw new AgeException("年龄不是合法的!");
}
mv.addObject("name", name);
mv.addObject("age", age);
mv.setViewName("center");
return mv;
}
}
3.3、异常处理handler
由于Controller层并没有对异常进行预判、也没有进行处理;当出现异常的时候会抛出异常给MVC框架:
-
这时候MVC框架会去看程序员有没有配置异常处理器。
-
如果没有异常处理器,MVC框架采用默认的异常处理抛出类似开头的一些图,就赤裸裸的暴露一个异常页面。
-
如果有异常处理器并且有适配的异常处理会根据程序员指定的异常处理方式进行处理
-
如果有异常处理器但是没有适配的异常处理一样会抛出MVC框架默认的异常页面。
import com.exception.AgeException;
import com.exception.NameException;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView;
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(value = {AgeException.class})
public ModelAndView AgeExceptionHandler(Exception e){
ModelAndView mv = new ModelAndView();
mv.addObject("msg", e.getMessage());
mv.addObject("path", "static/img/age.jpg");
mv.setViewName("error");
return mv;
}
@ExceptionHandler(value = {NameException.class})
public ModelAndView NameExceptionHandler(Exception e){
ModelAndView mv = new ModelAndView();
mv.addObject("msg", e.getMessage());
mv.addObject("path", "static/img/name.jpg");
mv.setViewName("error");
return mv;
}
@ExceptionHandler
public ModelAndView OtherExceptionHandler(Exception e){
ModelAndView mv = new ModelAndView();
mv.addObject("msg", e.getMessage());
mv.addObject("path", "static/img/other.jpg");
mv.setViewName("error");
return mv;
}
}
-
ControllerAdvice:该注解注册了bean对象、并且定义为一个切面类
-
ExceptionHandler:指定异常类型交给下方方法处理。
-
ExceptionHandler注解中如果value没有指定异常类型那么就表示所有的异常都匹配!
-
显然可以猜测到执行顺序是由一定优先级的,能成功匹配的异常一定交给指定的方法处理;匹配不上才交给所有异常的集中处理方法。
3.4、配置spring-mvc.xml文件
开启注解哪些就不说了,另外需要配置一些资源过滤、以及Exceptionhandler的扫描,将Exceptionhanlder处理器交给IOC容器托管。
<context:component-scan base-package="com.controller"/>
<context:component-scan base-package="com.handler"/>
<context:annotation-config/>
<mvc:annotation-driven>
<mvc:message-converters register-defaults="true">
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<constructor-arg value="UTF-8"/>
</bean>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper">
<bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
<property name="failOnUnknownProperties" value="false"/>
</bean>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
<mvc:resources mapping="/static/**" location="/static/"/>
<bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/page/"/>
<property name="suffix" value=".jsp"/>
</bean>
4、测试
4.1、合法测试
4.2、名字不合法
名字只有admin是对的,其他都是错误的
4.3、年龄不合法测试
当年龄超过150时就是错误的。
4.4、年龄中含有其他字符(其他异常)
当年龄中出现非数字字符时会出现参数接收异常、该异常没有定义处理方式默认情况下会采用全局异常处理来进行处理。
5、总结
SpringMVC框架的异常处理是非常重要的,J2EE开发体系不可缺少的部分;当用户出现异常时看到的"精美"页面其实都是程序员们设定好的,想让你看到的。
-
异常处理主要是两个注解:ControllerAdvice注解、ExceptionHandler注解。
-
原理就是AOP,以横切的姿势切入到逻辑代码中;但是又不影响逻辑代码的正常执行。
-
异常处理不是这么简单随意的处理、一般出现异常时会在异常处理中进行一些日志的记录、进行一些通知… 日志记录有利于后期的升级与维护。