统一异常处理

  • Post author:
  • Post category:其他


项目使用Springboot2.0.6版本

工程的目录结构:

1、引入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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.0.3.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.house</groupId>
	<artifactId>user-service</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>user-service</name>
	<description>用户微服务</description>

	<properties>
		<java.version>1.8</java.version>
		<spring-cloud.version>Finchley.SR4</spring-cloud.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
		</dependency>
		<!-- 健康检查模块 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-actuator</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>druid</artifactId>
			<version>1.1.0</version>
		</dependency>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
		</dependency>

		<dependency>
			<groupId>com.google.guava</groupId>
			<artifactId>guava</artifactId>
			<version>18.0</version>
		</dependency>

		<dependency>
			<groupId>org.apache.commons</groupId>
			<artifactId>commons-lang3</artifactId>
			<version>3.4</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
			<exclusions>
				<exclusion>
					<groupId>org.junit.vintage</groupId>
					<artifactId>junit-vintage-engine</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
	</dependencies>

	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-dependencies</artifactId>
				<version>${spring-cloud.version}</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

2、application.properties配置文件,注册中心搭建及数据库的搭建及相关配置请自行解决

server.port=8083
spring.application.name=user

eureka.client.service-url.defaultZone=http://127.0.0.1:8666/eureka/
eureka.instance.lease-renewal-interval-in-seconds=5
eureka.instance.lease-expiration-duration-in-seconds=10
#健康状态自动上报
eureka.client.healthcheck.enabled=true
management.endpoints.web.exposure.include=*

#datasource config
spring.druid.url=jdbc:mysql://127.0.0.1:3306/user?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull
spring.druid.username=root
spring.druid.password=123456
spring.druid.maxActive=20
spring.druid.minIdle=3
spring.druid.maxWait=10000
spring.druid.validationQuery=SELECT 'x'
spring.druid.timeBetweenEvictionRunsMillis=60000
spring.druid.minEvictableIdleTimeMillis=300000
spring.druid.borrowConnectionTimeout=30000

logbook.write.level=info
logbook.format.style=http

3、创建

GlobalExceptionHanlder





异常处理器,添加注解


@ControllerAdvice


,让Spring识别该类是一个异常处理类;

package com.house.user.common;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;

import javax.servlet.http.HttpServletRequest;

/**
 * 统一异常处理
 */
@ControllerAdvice
public class GlobalExceptionHanlder {
    private static final Logger LOGGER = LoggerFactory.getLogger(GlobalExceptionHanlder.class);
    @ResponseStatus(HttpStatus.OK)             // 状态码200
    @ExceptionHandler(value = Throwable.class) // 定义为处理异常的方法
    @ResponseBody                              // 返回json
    public RestResponse<Object> handler(HttpServletRequest req, Throwable throwable){
        LOGGER.error(throwable.getMessage(),throwable);
        RestCode restCode = Exception2CodeRepo.getCode(throwable);
        RestResponse<Object> response = new RestResponse<Object>(restCode.code,restCode.msg);
        return response;
    }
}

4、创建一个将异常转化成Code的类

Exception2CodeRepo

package com.house.user.common;

import com.google.common.collect.ImmutableMap;
import com.house.user.exception.IllegalParamsException;
import com.house.user.exception.UserException;
import com.house.user.exception.WithTypeException;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.commons.lang3.reflect.FieldUtils;

public class Exception2CodeRepo {
    // 不可变集合
    private static final ImmutableMap<Object, RestCode> MAP = ImmutableMap.<Object,RestCode>builder()
            .put(IllegalParamsException.Type.WRONG_PAGE_NUM,RestCode.WRONG_PAGE)
            .put(IllegalStateException.class,RestCode.UNKNOWN_ERROR)
            .put(UserException.Type.USER_NOT_LOGIN,RestCode.TOKEN_INVALID)
            .put(UserException.Type.USER_NOT_FOUND,RestCode.USER_NOT_EXIST)
            .put(UserException.Type.USER_AUTH_FAIL,RestCode.USER_NOT_EXIST).build();

    public static Object getType(Throwable throwable){
        try {
            return FieldUtils.readDeclaredField(throwable,"type",true);
        } catch (IllegalAccessException e) {
            return null;
        }
    }

    public static RestCode getCode(Throwable throwable){
        if(throwable == null){
            return RestCode.UNKNOWN_ERROR;
        }
        Object target = throwable;
        if(throwable instanceof WithTypeException){
            Object type = getType(throwable);
            if(type!=null){
                target = type;
            }
        }
        RestCode restCode = MAP.get(target);
        if(restCode != null){
            return restCode;
        }
        Throwable rootCause = ExceptionUtils.getRootCause(throwable);
        if(rootCause!=null){
            // 递归调用getCode
            return getCode(rootCause);
        }
        return RestCode.UNKNOWN_ERROR;
    }
}

5、IllegalParamsException类

package com.house.user.exception;

public class IllegalParamsException extends RuntimeException implements WithTypeException {
    private static final long serialVersionUID = 1L;
    private Type type;

    public IllegalParamsException(){

    }
    public IllegalParamsException(Type type,String msg){
        super(msg);
        this.type = type;
    }

    public Type type(){
        return type;
    }
    public enum Type{
        WRONG_PAGE_NUM,WRONG_TYPE
    }
}

6、用户自定义异常类UserException:

package com.house.user.exception;

public class UserException extends RuntimeException implements WithTypeException {
    private static final long serialVersionUID = 1L;
    private Type type;
    public  Type type(){
        return type;
    }
    public enum Type{
        WRONG_PAGE_NUM,LACK_PARAMTER,USER_NOT_LOGIN,USER_NOT_FOUND,USER_AUTH_FAIL;
    }

    public UserException(String message){
        super(message);
        type = Type.LACK_PARAMTER;
    }

    public UserException(Type type,String message){
        super(message);
        this.type = type;
    }
}

7、异常类型接口WithTypeException:

package com.house.user.exception;

/**
 * 包含类型的异常
 */
public interface WithTypeException {

}

7-1】RestCode类:

package com.house.user.common;

public enum RestCode {
    OK(0,"ok"),
    UNKNOWN_ERROR(1,"未知异常"),
    TOKEN_INVALID(2,"TOKEN失效"),
    USER_NOT_EXIST(3,"用户不存在"),
    WRONG_PAGE(10100,"页码不合法"),
    LACK_PARAMS(10101,"缺少参数");
    public int code;
    public String msg;
    private RestCode(int code,String msg){
        this.code = code;
        this.msg = msg;
    }
}

7-2】RestResponse类:

package com.house.user.common;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
@JsonInclude(Include.NON_NULL)
public class RestResponse<T>{
    private int code;
    private String msg;
    private T result;
    public static <T> RestResponse<T> success(){
        return new RestResponse<T>();
    }
    public static <T> RestResponse<T> success(T result){
        RestResponse<T> response = new RestResponse<T>();
        response.setResult(result);
        return response;
    }
    // 错误信息没有result
    public static <T> RestResponse<T> error(RestCode restCode){
        return new RestResponse<T>(restCode.code,restCode.msg);
    }
    // 默认成功
    public RestResponse(){
        this(RestCode.OK.code,RestCode.OK.msg);
    }
    public RestResponse(int code,String msg){
        this.code=code;
        this.msg = msg;
    }
    public int getCode() {
        return code;
    }

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

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public T getResult() {
        return result;
    }

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

8、Controller类:

package com.house.user.controller;

import com.house.user.common.GlobalExceptionHanlder;
import com.house.user.common.RestResponse;
import com.house.user.exception.IllegalParamsException;
import com.house.user.exception.UserException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class UserController {
    private final Logger logger = LoggerFactory.getLogger(UserController.class);
    @Value("${server.port}")
    private Integer port;
    @RequestMapping("/getusername")
    public RestResponse<String> getusername(Long id){
        logger.info("Incoming request...port="+port);
        if(id == null){
            throw new IllegalParamsException(IllegalParamsException.Type.WRONG_PAGE_NUM,"分页参数错误");
        }
        return RestResponse.success("测试user服务"+port);
    }
}

9、测试结果:

a、id不为空

b、id为空

c、打印日志



版权声明:本文为andwey原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。