Элегантная глобальная обработка исключений SpringBoot

Spring Boot

предисловие

В этой статье в основном представлен проект SpringBoot для глобальной обработки исключений.

Подготовка глобального исключения SpringBoot

Примечание. Если вы хотите получить проект напрямую, вы можете сразу перейти к нижней части и загрузить код проекта по ссылке.

подготовка к разработке

Требования к окружающей среде JDK: 1,8SpringBoot: 1.5.17.РЕЛИЗ

Прежде всего, связанные зависимости Maven:


  <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <java.version>1.8</java.version>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.17.RELEASE</version>
        <relativePath />
    </parent>
    <dependencies>
        <!-- Spring Boot Web 依赖 核心 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- Spring Boot Test 依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.41</version>
        </dependency>
    </dependencies>
    

Конфигурационный файл в принципе менять не нужно, а обработку глобальных исключений нужно только реализовать в коде.

написание кода

Проект SpringBoot уже обрабатывал определенные исключения, но он может не подойти нам, разработчикам, поэтому нам необходимо единообразно перехватывать и обрабатывать эти исключения. В SpringBoot есть одинControllerAdviceАннотация, используйте эту аннотацию, чтобы указать, что захват глобальных исключений включен, нам нужно использовать ее только в пользовательском методе.ExceptionHandlerАннотируйте, а затем определите тип захваченных исключений для единообразной обработки этих захваченных исключений.

Давайте посмотрим, как используется аннотация, согласно следующему примеру.

Образец кода:


@ControllerAdvice
public class MyExceptionHandler {

    @ExceptionHandler(value =Exception.class)
	public String exceptionHandler(Exception e){
		System.out.println("未知异常!原因是:"+e);
       	return e.getMessage();
    }
}

В приведенном выше примере мы выполняем простую вторичную обработку захваченного исключения и возвращаем информацию об исключении.Хотя это позволяет нам узнать причину исключения, во многих случаях это может быть недостаточно удобным для человека, чтобы удовлетворить наши потребности. . Затем мы можем получить данные, которые нам нужны, с помощью пользовательских классов исключений и классов перечисления.

Пользовательский базовый класс интерфейса

Сначала определите базовый класс интерфейса, а пользовательский класс перечисления описания ошибки должен реализовать этот интерфейс.код показывает, как показано ниже:


public interface BaseErrorInfoInterface {
    /** 错误码*/
	 String getResultCode();
	
	/** 错误描述*/
	 String getResultMsg();
}

пользовательский класс перечисления

Затем мы настраиваем здесь класс перечисления и реализуем интерфейс.код показывает, как показано ниже:


public enum CommonEnum implements BaseErrorInfoInterface {
	// 数据操作错误定义
	SUCCESS("200", "成功!"), 
	BODY_NOT_MATCH("400","请求的数据格式不符!"),
	SIGNATURE_NOT_MATCH("401","请求的数字签名不匹配!"),
	NOT_FOUND("404", "未找到该资源!"), 
	INTERNAL_SERVER_ERROR("500", "服务器内部错误!"),
	SERVER_BUSY("503","服务器正忙,请稍后再试!")
	;

	/** 错误码 */
	private String resultCode;

	/** 错误描述 */
	private String resultMsg;

	CommonEnum(String resultCode, String resultMsg) {
		this.resultCode = resultCode;
		this.resultMsg = resultMsg;
	}

	@Override
	public String getResultCode() {
		return resultCode;
	}

	@Override
	public String getResultMsg() {
		return resultMsg;
	}

}

пользовательский класс исключений

Затем мы определяем класс исключений для обработки наших бизнес-исключений.код показывает, как показано ниже:


public class BizException extends RuntimeException {

	private static final long serialVersionUID = 1L;

	/**
	 * 错误码
	 */
	protected String errorCode;
	/**
	 * 错误信息
	 */
	protected String errorMsg;

	public BizException() {
		super();
	}

	public BizException(BaseErrorInfoInterface errorInfoInterface) {
		super(errorInfoInterface.getResultCode());
		this.errorCode = errorInfoInterface.getResultCode();
		this.errorMsg = errorInfoInterface.getResultMsg();
	}
	
	public BizException(BaseErrorInfoInterface errorInfoInterface, Throwable cause) {
		super(errorInfoInterface.getResultCode(), cause);
		this.errorCode = errorInfoInterface.getResultCode();
		this.errorMsg = errorInfoInterface.getResultMsg();
	}
	
	public BizException(String errorMsg) {
		super(errorMsg);
		this.errorMsg = errorMsg;
	}
	
	public BizException(String errorCode, String errorMsg) {
		super(errorCode);
		this.errorCode = errorCode;
		this.errorMsg = errorMsg;
	}

	public BizException(String errorCode, String errorMsg, Throwable cause) {
		super(errorCode, cause);
		this.errorCode = errorCode;
		this.errorMsg = errorMsg;
	}
	

	public String getErrorCode() {
		return errorCode;
	}

	public void setErrorCode(String errorCode) {
		this.errorCode = errorCode;
	}

	public String getErrorMsg() {
		return errorMsg;
	}

	public void setErrorMsg(String errorMsg) {
		this.errorMsg = errorMsg;
	}

	public String getMessage() {
		return errorMsg;
	}

	@Override
	public Throwable fillInStackTrace() {
		return this;
	}

}

пользовательский формат данных

Кстати, здесь мы определяем формат передачи данных.код показывает, как показано ниже:


public class ResultBody {
	/**
	 * 响应代码
	 */
	private String code;

	/**
	 * 响应消息
	 */
	private String message;

	/**
	 * 响应结果
	 */
	private Object result;

	public ResultBody() {
	}

	public ResultBody(BaseErrorInfoInterface errorInfo) {
		this.code = errorInfo.getResultCode();
		this.message = errorInfo.getResultMsg();
	}

	public String getCode() {
		return code;
	}

	public void setCode(String code) {
		this.code = code;
	}

	public String getMessage() {
		return message;
	}

	public void setMessage(String message) {
		this.message = message;
	}

	public Object getResult() {
		return result;
	}

	public void setResult(Object result) {
		this.result = result;
	}

	/**
	 * 成功
	 * 
	 * @return
	 */
	public static ResultBody success() {
		return success(null);
	}

	/**
	 * 成功
	 * @param data
	 * @return
	 */
	public static ResultBody success(Object data) {
		ResultBody rb = new ResultBody();
		rb.setCode(CommonEnum.SUCCESS.getResultCode());
		rb.setMessage(CommonEnum.SUCCESS.getResultMsg());
		rb.setResult(data);
		return rb;
	}

	/**
	 * 失败
	 */
	public static ResultBody error(BaseErrorInfoInterface errorInfo) {
		ResultBody rb = new ResultBody();
		rb.setCode(errorInfo.getResultCode());
		rb.setMessage(errorInfo.getResultMsg());
		rb.setResult(null);
		return rb;
	}

	/**
	 * 失败
	 */
	public static ResultBody error(String code, String message) {
		ResultBody rb = new ResultBody();
		rb.setCode(code);
		rb.setMessage(message);
		rb.setResult(null);
		return rb;
	}

	/**
	 * 失败
	 */
	public static ResultBody error( String message) {
		ResultBody rb = new ResultBody();
		rb.setCode("-1");
		rb.setMessage(message);
		rb.setResult(null);
		return rb;
	}

	@Override
	public String toString() {
		return JSONObject.toJSONString(this);
	}

}

Пользовательский глобальный класс обработки исключений

Наконец, мы пишем собственный глобальный класс обработки исключений.код показывает, как показано ниже:


@ControllerAdvice
public class GlobalExceptionHandler {
	private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
	
	/**
	 * 处理自定义的业务异常
	 * @param req
	 * @param e
	 * @return
	 */
    @ExceptionHandler(value = BizException.class)  
    @ResponseBody  
	public  ResultBody bizExceptionHandler(HttpServletRequest req, BizException e){
    	logger.error("发生业务异常!原因是:{}",e.getErrorMsg());
    	return ResultBody.error(e.getErrorCode(),e.getErrorMsg());
    }

	/**
	 * 处理空指针的异常
	 * @param req
	 * @param e
	 * @return
	 */
	@ExceptionHandler(value =NullPointerException.class)
	@ResponseBody
	public ResultBody exceptionHandler(HttpServletRequest req, NullPointerException e){
		logger.error("发生空指针异常!原因是:",e);
		return ResultBody.error(CommonEnum.BODY_NOT_MATCH);
	}


    /**
        * 处理其他异常
     * @param req
     * @param e
     * @return
     */
    @ExceptionHandler(value =Exception.class)
	@ResponseBody
	public ResultBody exceptionHandler(HttpServletRequest req, Exception e){
    	logger.error("未知异常!原因是:",e);
       	return ResultBody.error(CommonEnum.INTERNAL_SERVER_ERROR);
    }
}

Потому что здесь мы используем только для функциональной реализации и тестирования глобальной обработки исключений, поэтому здесь нам нужно только добавить класс сущности и класс уровня управления.

класс сущности

Еще одна универсальная пользовательская таблица (^▽^)

код показывает, как показано ниже:


public class User implements Serializable{
	private static final long serialVersionUID = 1L;
	/** 编号 */
	 private int id;
	 /** 姓名 */
	 private String name;
	 /** 年龄 */
	 private int age;
	 
	 public User(){
	 }

	public int getId() {
		return id;
	}
	
	public void setId(int id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public String toString() {
		return JSONObject.toJSONString(this);
	}
}

Уровень управления контроллером

Слой управления также относительно прост, используется CRUD-функция, реализованная в стиле Restful, разница в том, что я намеренно сделал некоторые исключения, чтобы эти исключения можно было отловить и обработать. Среди этих исключений есть настраиваемые исключения, исключения нулевого указателя и, конечно же, непредсказуемые исключения (здесь вместо этого я использую исключения преобразования типов), затем давайте посмотрим на эти исключения после того, как мы закончим писать код. Можно ли их захватить и обработать. успешно!

код показывает, как показано ниже:


@RestController
@RequestMapping(value = "/api")
public class UserRestController {

	@PostMapping("/user")
    public boolean insert(@RequestBody User user) {
    	System.out.println("开始新增...");
    	//如果姓名为空就手动抛出一个自定义的异常!
        if(user.getName()==null){
            throw  new BizException("-1","用户姓名不能为空!");
        }
        return true;
    }

    @PutMapping("/user")
    public boolean update(@RequestBody User user) {
    	System.out.println("开始更新...");
       //这里故意造成一个空指针的异常,并且不进行处理
        String str=null;
        str.equals("111");
        return true;
    }

    @DeleteMapping("/user")
    public boolean delete(@RequestBody User user)  {
        System.out.println("开始删除...");
        //这里故意造成一个异常,并且不进行处理
        Integer.parseInt("abc123");
        return true;
    }

    @GetMapping("/user")
    public List<User> findByUser(User user) {
    	System.out.println("开始查询...");
        List<User> userList =new ArrayList<>();
        User user2=new User();
        user2.setId(1L);
        user2.setName("xuwujing");
        user2.setAge(18);
        userList.add(user2);
        return userList;
    }
    
}

Запись приложения

Это в основном то же самое, что и обычный проект SpringBoot.

код показывает, как показано ниже:


@SpringBootApplication
public class App 
{
    public static void main( String[] args )
    {
		SpringApplication.run(App.class, args);
		System.out.println("程序正在运行...");
    }
}

функциональный тест

После того, как мы успешно запустили программу, мы используем инструмент Postman для тестирования интерфейса.

Сначала сделайте запрос, чтобы узнать, работает ли программа нормально или нет, и используйте метод GET, чтобы сделать запрос.

GET http://localhost:8181/api/user

Возвращаемый параметр:

{"id":1,"name":"xuwujing","age":18}

пример графика:

在这里插入图片描述
Видно, что программа возвращается нормально и на нее не влияет пользовательское глобальное исключение.

Затем мы проверим, может ли пользовательское исключение быть правильно перехвачено и обработано.

Сделать запрос методом POST

POST http://localhost:8181/api/user

Параметры кузова:

{"id":1,"age":18}

Возвращаемый параметр:

{"code":"-1","message":"Имя пользователя не может быть пустым!","result":null}

пример графика:

在这里插入图片描述
Видно, что выбрасываемое нами исключение инкапсулируется в данные, а затем возвращается исключение.

Затем мы проверим, можно ли правильно перехватить и обработать исключение нулевого указателя. В пользовательском глобальном исключении, в дополнение к определению обработки исключений нулевых указателей, мы также определяем один из самых высоких уровней исключений Exception.После того, как здесь возникает исключение нулевого указателя, какой из них используется первым? Вот и тестируем.

Используйте метод PUT, чтобы сделать запрос.

PUT http://localhost:8181/api/user

Параметры кузова:

{"id":1,"age":18}

Возвращаемый параметр:

{"code":"400","message":"Запрошенный формат данных не соответствует!","result":null}

пример графика:

Мы видим, что именно обработка исключений действительно возвращает нулевой указатель, и можно сделать вывод, что глобальная обработка исключений отдает приоритет исключениям подклассов.

Затем давайте попробуем обработать неуказанные исключения, чтобы увидеть, можно ли их перехватить.

Используйте метод DELETE, чтобы сделать запрос.

DELETE http://localhost:8181/api/user

Параметры кузова:

{"id":1}

Возвращаемый параметр:

{"code":"500","message":"Внутренняя ошибка сервера!","result":null}

在这里插入图片描述

Здесь вы можете видеть, что он использует метод обработки исключений Exception в нашем пользовательском глобальном классе обработки исключений. На этом испытание закончено. Кстати, в дополнение к вышеуказанному формату данных, пользовательская глобальная обработка исключений также может обрабатывать переходы по страницам, просто заполните путь перехода в обработке возврата вновь добавленного метода исключения и не используйте его.ResponseBodyПросто аннотируйте. Внимательные одноклассники могут обнаружить, что вGlobalExceptionHandlerИспользуемый классControllerAdviceаннотации, неRestControllerAdviceАннотация, если используетсяRestControllerAdviceаннотации, он автоматически преобразует данные в формат JSON, который используется вControllerа такжеRestControllerАналогично, поэтому мы можем выбрать гибкую обработку после использования глобальной обработки исключений.

разное

Статья об элегантной глобальной обработке исключений SpringBoot объясняется здесь Если что-то не так, поправьте меня!

адрес проекта

Адрес проекта глобальной обработки исключений SpringBoot:GitHub.com/nihilty/tickets…

Адрес всей коллекции SpringBoot:GitHub.com/nihilty/tickets…

Статьи из серии SpringBoot Integration

музыкальная рекомендация

Оригинал не просто, если вы чувствуете себя хорошо, я надеюсь дать рекомендацию! Ваша поддержка - самая большая мотивация для моего письма! Уведомление об авторских правах: Автор: ничтожество Источник блога сада:www.cnblogs.com/xuwujingИсточник CSDN:blog.csdn.net/qazwsxpcm     Источник личного блога:www.panchengming.com