在代码的编写的过程中,异常(bug)无处不在,有些使我们能明显意识到需要我们提前避免的,有些确实我们意料之外的。比如我们在做一个新增操作时,为了防止插入失败我们有时会这么做:
try {
checkgroupService.add(checkGroup,checkitemIds);
setmealService.generaStaticSetmealDetailHtml(setmealService.findAll());
} catch (Exception e) {
e.printStackTrace();
return new Result(false, MessageConstant.ADD_CHECKGROUP_FAIL);
}
但每一个增删改查操作都进行这样的try...catch操作就产生了大量的冗余代码,同时返回的格式也并不统一。
或者有些我们意识不到的,比如角标越界,IO异常,或者空指针。这些报错就比较尴尬了,容易返回前端一面一个大大地500错误。这对我们的用户来说是非常不友好的。
那么今天我就给大家讲一下,相对来说更加合理的一种解决方案:
通过springMvc实现自定义全局异常捕获器。
本文所用的pom,pojo,log4j均在文末提供。
好了开始进入正文:
一、自定义异常流程
1、自定义异常类型。
2、自定义错误代码及错误信息。
3、对于可预知的异常由程序员在代码中主动抛出,由SpringMVC统一捕获。
可预知异常是程序员在代码中手动抛出本系统定义的特定异常类型,由于是程序员抛出的异常,通常异常信息比较齐全,程序员在抛出时会指定错误代码及错误信息,获取异常信息也比较方便。
4、对于不可预知的异常(运行时异常)由SpringMVC统一捕获Exception类型的异常。
不可预知异常通常是由于系统出现bug、或一些不要抗拒的错误(比如网络中断、服务器宕机等),异常类型为RuntimeException类型(运行时异常)。
5、可预知的异常及不可预知的运行时异常最终会采用统一的信息格式(错误代码+错误信息)来表示,最终也会随请求响应给客户端。
看不懂?
那就来张图或许更容易懂一些:
二、自定义一个异常类用来抛出我们自定的异常
package com.huawei.exception;
import com.huawei.domain.ResultCode;
public class ExceptionCoustm extends RuntimeException {
private ResultCode resultCode;
public ResultCode getResultCode() {
return resultCode;
}
public ExceptionCoustm(ResultCode resultCode) {
// super必须在构造的第一行,this也一样,所以两者不能同时出现
super("错误代码:"+resultCode.code()+"错误信息:"+resultCode.message());
this.resultCode = resultCode;
}
}
先看这一段代码,很多小伙伴或许会疑惑:为什么要继承RuntimeException。答案很简单,为了在需要抛出异常的可以编译通过(不用try…catch)。同时需要在继承父类构造(message参数)来完成存储我们的错误信息。
三、定义静态调用类(可以省略)
package com.huawei.exception;
import com.huawei.domain.ResultCode;
public class ExceptionCast {
private static ResultCode resultCode;
public static void cast(){
throw new ExceptionCoustm(resultCode);
}
}
这个类只是单纯为了抛出异常方便,可以直接在需要抛出异常的地方throw new ExceptionCoustm(参数)。
我们抛出异常可以这样抛了:
if (id==null&& id.length()==0){
ExceptionCast.cast(CommonCode.EXCEPTIONCOUSTM);
}
四、定义捕获类(重点都在这了)
package com.huawei.exception;
import com.google.common.collect.ImmutableMap;
import com.huawei.domain.CommonCode;
import com.huawei.domain.ResultCode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseBody;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
@ControllerAdvice
@ResponseBody
public class ExceptionCatch {
// log4j打印日志
private static final Logger LOGGER = LoggerFactory.getLogger(ExceptionCatch.class);
//使用EXCEPTIONS存放异常类型和错误代码的映射,
private static ImmutableMap<Class<? extends Throwable>,ResultCode> EXCEPTIONS;
//使用builder来构建一个异常类型和错误代码的异常集合
protected static ImmutableMap.Builder<Class<? extends Throwable>,ResultCode> builder = ImmutableMap.builder();
// 加载异常类型,可以加载n个
static {
builder.put(IOException.class,CommonCode.IO_EXCEPTION);
}
//捕获自定义异常类型
@ExceptionHandler(ExceptionCoustm.class)
public ResultCode exceptionCoustm(ExceptionCoustm exceptionCoustm){
ResultCode resultCode = exceptionCoustm.getResultCode();
LOGGER.error("catch exception:{}",exceptionCoustm.getMessage());
exceptionCoustm.printStackTrace();
return CommonCode.EXCEPTIONCOUSTM;
}
@ExceptionHandler(Exception.class)
public ResultCode exception(Exception exception){
// 打印记录日志
LOGGER.error("catch exception:{}",exception.getMessage());
exception.printStackTrace();
if (EXCEPTIONS==null){
// ImmutableMap创建完成,不可在更改数据
EXCEPTIONS=builder.build();
}
// 获取异常类型,与定义相符的存入(resultCode)多态
ResultCode resultCode = EXCEPTIONS.get(exception.getClass());
//
if (resultCode!=null){
return resultCode;
}else {
// 如果没有匹配的类型返回其他类型
return CommonCode.OTHER_EXCEPTION;
}
}
}
@ControllerAdvice :这里我们用的一种用途
可以实现三种功能:
1.实现全局异常捕获 结合 @ExceptionHandler(CustomException.class) 来指定要捕获的异常类型
2.实现全局数据绑定 结合@ModelAttribute 来实现全局绑定
3.全局数据预处理 比较鸡肋
private static ImmutableMap<Class<? extends Throwable>,ResultCode> EXCEPTIONS;
说一下这个集合吧。这是一个goole的产品,ImmutableMap的特点的一旦创建不可改变,并且线程安全。同时这个集合是一个虚拟类(被abstract修饰),这段代码并没有进行实例化。
如果上面关于为什么用这个集合不明白的话可以参考这篇文章:https://blog.csdn.net/qq_27093465/article/details/53212577
protected static ImmutableMap.Builder<Class<? extends Throwable>,ResultCode> builder = ImmutableMap.builder();
定义ImmutableMap.Builder对象,方便存放异常类型。那么有的小伙伴又会问既然是Map集合为何不直接put,那岂不更省事?
当然不能!看下面这段ImmutableMap的源码:
ImmutableMap() {
}
public final V put(K k, V v) {
throw new UnsupportedOperationException();
}
public final V remove(Object o) {
throw new UnsupportedOperationException();
}
public final void putAll(Map<? extends K, ? extends V> map) {
throw new UnsupportedOperationException();
}
public final void clear() {
throw new UnsupportedOperationException();
}
put方法都抛出了异常,禁止了直接添加以保证集合的安全性,这也就说明了:ImmutableMap的特点的一旦创建不可改变,并且线程安全。
static{
// 还可以放更多的映射关系
builder.put(IOException.class,CommonCode.IO_EXCEPTION);
}
使用静态代码块在编译时就将映射关系,保证在集合创建前,映射关系注入集合。当然也可以使用:
Map<Class<? extends Throwable>ResultCode> EXCEPTIONS =
new ImmutableMap.Builder<Class<? extends Throwable> ResultCode>().
put(IOException.class,CommonCode.IO_EXCEPTION);
.build();
把上边的三段代码去掉也可以。ImmutableMap就讲到这了。
@ExceptionHandler(ExceptionCoustm.class) 这个注解是用来捕获指定的异常类型(这里只捕获自定义的异常类型)。
ResultCode resultCode = EXCEPTIONS.get(exception.getClass());
将捕获的异常放入ImmutableMap判断是否有映射关系,根据是否有映射关系来返回不同的响应信息,集合中没有的就统一返回其他异常(一般定义为系统繁忙)
pojo文件:
package com.huawei.domain;
//自定义枚举类
public enum CommonCode implements ResultCode {
EXCEPTIONCOUSTM(false,10000,"自定义异常"),
OTHER_EXCEPTION(false,99999,"不可预测异常"),
IO_EXCEPTION(false,20000,"IO异常");
private Boolean success;
private Integer code;
private String message;
CommonCode(Boolean success, Integer code, String message) {
this.success = success;
this.code = code;
this.message = message;
}
@Override
public boolean success() {
return success;
}
@Override
public int code() {
return code;
}
@Override
public String message() {
return message;
}
}
package com.huawei.domain;
public interface ResultCode {
//操作是否成功,true为成功,false操作失败
boolean success();
//操作代码
int code();
//提示信息
String message();
}
五、pom文件
<?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>com.huawei</groupId>
<artifactId>ssm</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<name>ssm Maven Webapp</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.32</version>
</dependency>
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.2</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
<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>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.3</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.7</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.7</version>
</dependency>
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>com.google.collections</groupId>
<artifactId>google-collections</artifactId>
<version>1.0</version>
</dependency>
</dependencies>
<build>
<!--maven插件-->
<plugins>
<!--jdk编译插件-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<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>8080</port>
<!-- 项目访问路径 本例:localhost:9090, 如果配置的aa, 则访问路径为localhost:9090/aa-->
<path>/</path>
</configuration>
</plugin>
</plugins>
</build>
</project>
六、log4j配置
### direct log messages to stdout ###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %l [%30.30m]%d %n
### direct messages to file mylog.log ###
#log4j.appender.A=org.apache.log4j.FileAppender
#log4j.appender.A.File=D:\\mylog.log
#log4j.appender.A.append=false
#log4j.appender.A.layout=org.apache.log4j.PatternLayout
#log4j.appender.A.layout.ConversionPattern=%d{ABSOLUTE}%5p %c{1}:%L -%m%n
### set log levels -for more verbose logging change 'info' to 'debug' ###
log4j.rootLogger=debug, stdout
##log4j.rootLogger=warn, stdout,A
springMvc实现自定义全局异常捕获器就先讲到这了,如有不对之处请指正,谢谢。
原文链接:https://betheme.net/news/txtlist_i46108v.html?action=onClick