项目使用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、打印日志