简化配置
我新建一个类,但是我不用 @Service
注解,也就是说,它是个普通的类,那么我们如何使它也成为一个 Bean 让 Spring 去管理呢?只需要 @Configuration
和 @Bean
两个注解即可,如下:
public class TestService {
public String sayHello () {
return "Hello Spring Boot!";
}
}
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class JavaConfig {
@Bean
public TestService getTestService() {
return new TestService();
}
}
@Configuration
表示该类是个配置类,@Bean
表示该方法返回一个 Bean。这样就把 TestService 作为 Bean 让 Spring 去管理了,在其他地方,我们如果需要使用该 Bean,和原来一样,直接使用 @Resource
注解注入进来即可使用,非常方便。
fastjson 依赖导入
使用 fastjson 需要导入依赖,本课程使用 1.2.35 版本,依赖如下:
com.alibaba
fastjson
1.2.35
使用 fastjson 处理 null
使用 fastjson 时,对 null 的处理和 Jackson 有些不同,需要继承 WebMvcConfigurationSupport 类,然后覆盖 configureMessageConverters 方法。在方法中,我们可以选择要实现 null 转换的场景,配置好即可。如下:
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.support.config.FastJsonConfig;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
@Configuration
public class fastJsonConfig extends WebMvcConfigurationSupport {
/**
* 使用阿里 fastjson 作为JSON MessageConverter
* @param converters
*/
@Override
public void configureMessageConverters(List> converters) {
FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
FastJsonConfig config = new FastJsonConfig();
config.setSerializerFeatures(
// 保留map空的字段
SerializerFeature.WriteMapNullValue,
// 将String类型的null转成""
SerializerFeature.WriteNullStringAsEmpty,
// 将Number类型的null转成0
SerializerFeature.WriteNullNumberAsZero,
// 将List类型的null转成[]
SerializerFeature.WriteNullListAsEmpty,
// 将Boolean类型的null转成false
SerializerFeature.WriteNullBooleanAsFalse,
// 避免循环引用
SerializerFeature.DisableCircularReferenceDetect);
converter.setFastJsonConfig(config);
converter.setDefaultCharset(Charset.forName("UTF-8"));
List mediaTypeList = new ArrayList<>();
// 解决中文乱码问题,相当于在Controller上的@RequestMapping中加了个属性produces = "application/json"
mediaTypeList.add(MediaType.APPLICATION_JSON);
converter.setSupportedMediaTypes(mediaTypeList);
converters.add(converter);
}
}
定义统一的 JSON 结构
public class JsonResult {
private T data;
private String code;
private String msg;
/**
* 若没有数据返回,默认状态码为0,提示信息为:操作成功!
*/
public JsonResult() {
this.code = "0";
this.msg = "操作成功!";
}
/**
* 若没有数据返回,可以人为指定状态码和提示信息
* @param code
* @param msg
*/
public JsonResult(String code, String msg) {
this.code = code;
this.msg = msg;
}
/**
* 有数据返回时,状态码为0,默认提示信息为:操作成功!
* @param data
*/
public JsonResult(T data) {
this.data = data;
this.code = "0";
this.msg = "操作成功!";
}
/**
* 有数据返回,状态码为0,人为指定提示信息
* @param data
* @param msg
*/
public JsonResult(T data, String msg) {
this.data = data;
this.code = "0";
this.msg = msg;
}
// 省略get和set方法
}
修改 Controller 中的返回值类型及测试
@RestController
@RequestMapping("/jsonresult")
public class JsonResultController {
@RequestMapping("/user")
public JsonResult getUser() {
User user = new User(1, "倪升武", "123456");
return new JsonResult<>(user);
}
@RequestMapping("/list")
public JsonResult getUserList() {
List userList = new ArrayList<>();
User user1 = new User(1, "倪升武", "123456");
User user2 = new User(2, "达人课", "123456");
userList.add(user1);
userList.add(user2);
return new JsonResult<>(userList, "获取用户列表成功");
}
@RequestMapping("/map")
public JsonResult
我们重新在浏览器中输入:localhost:8080/jsonresult/user
返回 JSON 如下:
{"code":"0","data":{"id":1,"password":"123456","username":"倪升武"},"msg":"操作成功!"}
输入:localhost:8080/jsonresult/list
,返回 JSON 如下:
{"code":"0","data":[{"id":1,"password":"123456","username":"倪升武"},{"id":2,"password":"123456","username":"达人课"}],"msg":"获取用户列表成功"}
输入:localhost:8080/jsonresult/map
,返回 JSON 如下:
{"code":"0","data":{"作者信息":{"id":1,"password":"","username":"倪升武"},"CSDN地址":null,"粉丝数量":4153,"博客地址":"http://blog.itcodai.com"},"msg":"操作成功!"}
通过封装,我们不但将数据通过 JSON 传给前端或者其他接口,还带上了状态码和提示信息,这在实际项目场景中应用得非常广泛。
阿里巴巴已经将 SLF4J 作为他们的日志框架了。在《阿里巴巴 Java 开发手册(正式版)》中,日志规约一项第一条就强制要求使用 SLF4J:
1.【强制】应用中不可直接使用日志系统(Log4j、Logback)中的 API,而应依赖使用日志框架 SLF4J 中的API,使用门面模式的日志框架,有利于维护和各个类的日志处理方式统一。
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Test {
private static final Logger logger = LoggerFactory.getLogger(Test.class);
// ……
}
我们看一下 application.yml 文件中对日志的配置:
logging:
config: logback.xml
level:
com.itcodai.course03.dao: trace
logging.config 用来指定项目启动的时候,读取哪个配置文件,这里指定的日志配置文件是根路径下的 logback.xml 文件。关于日志的相关配置信息,都放在了 logback.xml 文件中。
logging.level 用来指定具体的 Mapper 中日志的输出级别,上面的配置表示 com.itcodai.course03.dao
包下的所有 Mapper 日志输出级别为 Trace,会将操作数据库的 SQL 打印出来。开发时设置成 trace 方便定位问题,在生产环境上,将这个日志级别再设置成 error 级别即可(本文不讨论 Mapper 层,在后面 Spring Boot 集成 MyBatis 时再详细讨论)。
常用的日志级别按照从高到低依次为:ERROR、WARN、INFO、DEBUG。
在上面的 application.yml 文件中,我们指定了日志配置文件 logback.xml。logback.xml 文件中主要用来做日志的相关配置。在 logback.xml 中,我们可以定义日志输出的格式、路径、控制台输出格式、文件大小、保存时长等等。下面来分析一下。
定义日志输出格式和存储路径
我们来看一下这个定义的含义:首先定义一个格式,命名为 “LOG_PATTERN”,该格式中 %date
表示日期,%thread
表示线程名,%-5level
表示级别从左显示5个字符宽度,%logger{36}
表示 Logger 名字最长36个字符,%msg
表示日志消息,%n
是换行符。
然后再定义名为 FILE_PATH
的文件路径,日志都会存储在该路径下。%i
表示第 i 个文件,当日志文件达到指定大小时,会将日志生成到新的文件里,这里的 i 就是文件索引,日志文件允许的大小可以设置,下面会讲解。这里需要注意的是,不管是 Windows 系统还是 Linux 系统,日志存储的路径必须是绝对路径。
定义控制台输出
${LOG_PATTERN}
使用
节点设置控制台输出(class="ch.qos.logback.core.ConsoleAppender"
)的配置,定义为 CONSOLE。使用上面定义好的输出格式(LOG_PATTERN
)来输出,使用 ${}
引用进来即可。
定义日志文件的相关参数
${FILE_PATH}
15
10MB
${LOG_PATTERN}
使用
定义一个名为 “FILE” 的文件配置,主要是配置日志文件保存的时间、单个日志文件存储的大小,以及文件保存的路径和日志的输出格式。
定义日志输出级别
有了上面那些定义后,最后我们使用
来定义一下项目中默认的日志输出级别,这里定义级别为 INFO,然后针对 INFO 级别的日志,使用
引用上面定义好的控制台日志输出和日志文件的参数。这样 logback.xml 文件中的配置就设置完了。
在代码中,我们一般使用 Logger 对象打印 log 信息,可以指定打印出的日志级别,也支持占位符,很方便。
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestController {
private final static Logger logger = LoggerFactory.getLogger(TestController.class);
@RequestMapping("/log")
public String testLog() {
logger.debug("=====测试日志debug级别打印====");
logger.info("======测试日志info级别打印=====");
logger.error("=====测试日志error级别打印====");
logger.warn("======测试日志warn级别打印=====");
// 可以使用占位符打印出一些参数信息
String str1 = "blog.itcodai.com";
String str2 = "blog.csdn.net/eson_15";
logger.info("======倪升武的个人博客:{};倪升武的CSDN博客:{}", str1, str2);
return "success";
}
}
启动该项目,在浏览器中输入 localhost:8080/test/log
后可以看到控制台的日志记录:
======测试日志info级别打印=====
=====测试日志error级别打印====
======测试日志warn级别打印=====
======倪升武的个人博客:blog.itcodai.com;倪升武的CSDN博客:blog.csdn.net/eson_15
因为 INFO 级别比 DEBUG 级别高,所以 debug 这条没有打印出来,如果将 logback.xml 中的日志级别设置成 DEBUG,那么四条语句都会打印出来,这个大家自己去测试了。同时可以打开 D:\logs\course03\
目录,里面有项目刚刚启动时以及后面生成的所有日志记录。项目部署后,我们大部分都通过查看日志文件来定位问题。
# 配置多个微服务的地址
url:
# 订单微服务的地址
orderUrl: http://localhost:8002
# 用户微服务的地址
userUrl: http://localhost:8003
# 购物车微服务的地址
shoppingUrl: http://localhost:8004
也许实际业务中,远远不止这三个微服务,甚至十几个都有可能。对于这种情况,我们可以先定义一个 MicroServiceUrl 类来专门保存微服务的 URL,如下:
@Component
@ConfigurationProperties(prefix = "url")
public class MicroServiceUrl {
private String orderUrl;
private String userUrl;
private String shoppingUrl;
// 省去get和set方法
}
需要注意的是,使用 @ConfigurationProperties
注解需要导入它的依赖:
org.springframework.boot
spring-boot-configuration-processor
true
@RestController
@RequestMapping("/test")
public class TestController {
private static final Logger LOGGER = LoggerFactory.getLogger(TestController.class);
@Resource
private MicroServiceUrl microServiceUrl;
@RequestMapping("/config")
public String testConfig() {
LOGGER.info("=====获取的订单服务地址为:{}", microServiceUrl.getOrderUrl());
LOGGER.info("=====获取的用户服务地址为:{}", microServiceUrl.getUserUrl());
LOGGER.info("=====获取的购物车服务地址为:{}", microServiceUrl.getShoppingUrl());
return "success";
}
}
再次启动项目,请求一下可以看到,控制台打印出如下信息,说明配置文件生效,同时正确获取配置文件内容:
=====获取的订单服务地址为:http://localhost:8002
=====获取的订单服务地址为:http://localhost:8002
=====获取的用户服务地址为:http://localhost:8003
=====获取的购物车服务地址为:http://localhost:8004
我们新建两个配置文件: application-dev.yml
和 application-pro.yml
,分别用来对开发环境和生产环境进行相关配置。这里为了方便,我们分别设置两个访问端口号,开发环境用 8001,生产环境用 8002。
# 开发环境配置文件
server:
port: 8001
# 开发环境配置文件
server:
port: 8002
然后在 application.yml
文件中指定读取哪个配置文件即可。比如我们在开发环境下,指定读取 applicationn-dev.yml
文件,如下:
spring:
profiles:
active:
- dev
这样就可以在开发的时候,指定读取 application-dev.yml
文件,访问的时候使用 8001 端口,部署到服务器后,只需要将 application.yml
中指定的文件改成 application-pro.yml
即可,然后使用 8002 端口访问,非常方便。
@PathVariable
@GetMapping("/user/{id}")
public String testPathVariable(@PathVariable Integer id) {
System.out.println("获取到的id为:" + id);
return "success";
}
那么问题来了,如果表单数据很多,我们不可能在后台方法中写上很多参数,每个参数还要 @RequestParam
注解。针对这种情况,我们需要封装一个实体类来接收这些参数,实体中的属性名和表单中的参数名一致即可。
public class User {
private String username;
private String password;
// set get
}
使用实体接收的话,我们不必在前面加 @RequestParam
注解,直接使用即可。(表单)
@PostMapping("/form2")
public String testForm(User user) {
System.out.println("获取到的username为:" + user.getUsername());
System.out.println("获取到的password为:" + user.getPassword());
return "success";
}
json实体:(上面是表单提交)
@PostMapping("/user")
public String testRequestBody(@RequestBody User user) {
System.out.println("获取到的username为:" + user.getUsername());
System.out.println("获取到的password为:" + user.getPassword());
return "success";
}
使用 Swagger2 工具,必须要导入 Maven 依赖,当前官方最高版本是 2.8.0,我尝试了一下,个人感觉页面展示的效果不太好,而且不够紧凑,不利于操作。另外,最新版本并不一定是最稳定版本,当前我们实际项目中使用的是 2.2.2 版本,该版本稳定,界面友好,所以本节课主要围绕着 2.2.2 版本来展开,依赖如下:
io.springfox
springfox-swagger2
2.2.2
io.springfox
springfox-swagger-ui
2.2.2
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
/**
* @author shengwu ni
*/
@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
// 指定构建api文档的详细信息的方法:apiInfo()
.apiInfo(apiInfo())
.select()
// 指定要生成api接口的包路径,这里把controller作为包路径,生成controller中的所有接口
.apis(RequestHandlerSelectors.basePackage("com.itcodai.course06.controller"))
.paths(PathSelectors.any())
.build();
}
/**
* 构建api文档的详细信息
* @return
*/
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
// 设置页面标题
.title("Spring Boot集成Swagger2接口总览")
// 设置接口描述
.description("跟武哥一起学Spring Boot第06课")
// 设置联系方式
.contact("倪升武," + "CSDN:http://blog.csdn.net/eson_15")
// 设置版本
.version("1.0")
// 构建
.build();
}
}
访问localhost:8080/swagger-ui.html
友情提示: 可能有很多朋友在配置 Swagger 的时候会遇到下面的情况,而且还关不掉,这是由浏览器缓存引起的,清空一下浏览器缓存即可解决问题。
实体类注解
本节我们建一个 User 实体类,主要介绍 Swagger2 中的 @ApiModel
和 @ApiModelProperty
注解,同时为后面的测试做准备。
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
@ApiModel(value = "用户实体类")
public class User {
@ApiModelProperty(value = "用户唯一标识")
private Long id;
@ApiModelProperty(value = "用户姓名")
private String username;
@ApiModelProperty(value = "用户密码")
private String password;
// 省略set和get方法
}
Controller 类中相关注解
我们写一个 TestController,再写几个接口,然后学习一下 Controller 中和 Swagger2 相关的注解。
import com.itcodai.course06.entiy.JsonResult;
import com.itcodai.course06.entiy.User;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/swagger")
@Api(value = "Swagger2 在线接口文档")
public class TestController {
@GetMapping("/get/{id}")
@ApiOperation(value = "根据用户唯一标识获取用户信息")
public JsonResult getUserInfo(@PathVariable @ApiParam(value = "用户唯一标识") Long id) {
// 模拟数据库中根据id获取User信息
User user = new User(id, "倪升武", "123456");
return new JsonResult(user);
}
可以看出,Swagger 页面对该接口的信息展示得非常全面,每个注解的作用以及展示的地方在上图中已经标明,通过页面即可知道该接口的所有信息,那么我们直接在线测试一下该接口返回的信息,输入 ID 为1,看一下返回数据:
可以看出,直接在页面返回了 JSON 格式的数据,开发人员可以直接使用该在线接口来测试数据的正确与否,非常方便。上面是针对单个参数的输入,如果输入参数为某个对象时,Swagger 是什么样子呢?我们再写一个接口。
@PostMapping("/insert")
@ApiOperation(value = "添加用户信息")
public JsonResult insertUser(@RequestBody @ApiParam(value = "用户信息") User user) {
// 处理添加逻辑
return new JsonResult<>();
}
重启项目,在浏览器中输入:localhost:8080/swagger-ui.html
,看一下效果:
org.springframework.boot
spring-boot-starter-thymeleaf
另外,在 HTML 页面上如果要使用 Thymeleaf 模板,需要在页面标签中引入如下代码:
spring:
thymeleaf:
cache: false #关闭缓存
这个和 Thymeleaf 没啥关系,应该说是通用的。我把它一并写到这里,是因为一般我们做网站的时候,都会做一个 404 页面和 500 页面,为了出错时给用户一个友好的展示,而不至于一堆异常信息抛出来。Spring Boot 会自动识别模板目录(templates/
)下的 404.html 和 500.html 文件。我们在 templates/
目录下新建一个 error 文件夹,专门放置错误的 HTML 页面,然后分别打印些信息。以 404.html 为例,代码如下:
Title
这是404页面
我们再写一个 controller 来测试一下 404 和 500 页面:
@Controller
@RequestMapping("/thymeleaf")
public class ThymeleafController {
@RequestMapping("/test404")
public String test404() {
return "index";
}
@RequestMapping("/test500")
public String test500() {
int i = 1 / 0;
return "index";
}
}
当我们在浏览器中输入:
localhost:8080/thymeleaf/test400
时,故意输入错误,找不到对应的方法,就会跳转到 404.html 显示。当我们在浏览器中输入:
localhost:8088/thymeleaf/test505
时,会抛出异常,然后会自动跳转到 500.html 显示。
public class Blogger {
private Long id;
private String name;
private String pass;
// 省去set和get
}
然后在 controller 层中初始化:
@GetMapping("/getBlogger")
public String getBlogger(Model model) {
Blogger blogger = new Blogger(1L, "倪升武", "123456");
model.addAttribute("blogger", blogger);
return "blogger";
}
我们先初始化一个 Blogger 对象,然后将该对象放到 Model 中,再返回到 blogger.html 页面去渲染。接下来我们再写一个 blogger.html 来渲染 blogger 信息:
博主信息
处理 List 的话,和处理上面介绍的对象差不多,但是需要在 Thymeleaf 中进行遍历。我们先在 Controller 中模拟一个 List。
@GetMapping("/getList")
public String getList(Model model) {
Blogger blogger1 = new Blogger(1L, "倪升武", "123456");
Blogger blogger2 = new Blogger(2L, "达人课", "123456");
List list = new ArrayList<>();
list.add(blogger1);
list.add(blogger2);
model.addAttribute("list", list);
return "list";
}
接下来我们写一个 list.html 来获取该 List 信息,然后在 list.html 中遍历这个 List。如下代码:
博主信息
public class JsonResult {
/**
* 异常码
*/
protected String code;
/**
* 异常信息
*/
protected String msg;
public JsonResult() {
this.code = "200";
this.msg = "操作成功";
}
public JsonResult(String code, String msg) {
this.code = code;
this.msg = msg;
}
// get set
}
@ControllerAdvice
@ResponseBody
public class GlobalExceptionHandler {
// 打印log
private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
// ……
}
/**
* 缺少请求参数异常
* @param ex HttpMessageNotReadableException
* @return
*/
@ExceptionHandler(MissingServletRequestParameterException.class)
@ResponseStatus(value = HttpStatus.BAD_REQUEST)
public JsonResult handleHttpMessageNotReadableException(
MissingServletRequestParameterException ex) {
logger.error("缺少请求参数,{}", ex.getMessage());
return new JsonResult("400", "缺少必要的请求参数");
}
我们来写个简单的 Controller 测试一下该异常,通过 POST 请求方式接收两个参数:姓名和密码。
@RestController
@RequestMapping("/exception")
public class ExceptionController {
private static final Logger logger = LoggerFactory.getLogger(ExceptionController.class);
@PostMapping("/test")
public JsonResult test(@RequestParam("name") String name,
@RequestParam("pass") String pass) {
logger.info("name:{}", name);
logger.info("pass:{}", pass);
return new JsonResult();
}
}
.
然后使用 Postman 来调用一下该接口,调用的时候,只传姓名,不传密码,就会抛缺少参数异常,该异常被捕获之后,就会进入我们写好的逻辑,给调用方返回一个友好信息,如下:
@ControllerAdvice
@ResponseBody
public class GlobalExceptionHandler {
private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
/**
* 空指针异常
* @param ex NullPointerException
* @return
*/
@ExceptionHandler(NullPointerException.class)
@ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
public JsonResult handleTypeMismatchException(NullPointerException ex) {
logger.error("空指针异常,{}", ex.getMessage());
return new JsonResult("500", "空指针异常了");
}
}
这个我就不测试了,代码中 ExceptionController 有个 testNullPointException
方法,模拟了一个空指针异常,我们在浏览器中请求一下对应的 URL 即可看到返回的信息:
{"code":"500","msg":"空指针异常了"}
当然了,异常很多,比如还有 RuntimeException,数据库还有一些查询或者操作异常等等。由于 Exception 异常是父类,所有异常都会继承该异常,所以我们可以直接拦截 Exception 异常,一劳永逸:
@ControllerAdvice
@ResponseBody
public class GlobalExceptionHandler {
private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
/**
* 系统异常 预期以外异常
* @param ex
* @return
*/
@ExceptionHandler(Exception.class)
@ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
public JsonResult handleUnexpectedServer(Exception ex) {
logger.error("系统异常:", ex);
return new JsonResult("500", "系统发生异常,请联系管理员");
}
}
但在项目中,我们一般都会比较详细的去拦截一些常见异常,拦截 Exception 虽然可以一劳永逸,但是不利于我们去排查或者定位问题。实际项目中,可以把拦截 Exception 异常写在 GlobalExceptionHandler 最下面,如果都没有找到,最后再拦截一下 Exception 异常,保证输出信息友好。
定义异常信息
由于在业务中,有很多异常,针对不同的业务,可能给出的提示信息不同,所以为了方便项目异常信息管理,我们一般会定义一个异常信息枚举类。比如:
/**
* 业务异常提示信息枚举类
* @author shengwu ni
*/
public enum BusinessMsgEnum {
/** 参数异常 */
PARMETER_EXCEPTION("102", "参数异常!"),
/** 等待超时 */
SERVICE_TIME_OUT("103", "服务调用超时!"),
/** 参数过大 */
PARMETER_BIG_EXCEPTION("102", "输入的图片数量不能超过50张!"),
/** 500 : 一劳永逸的提示也可以在这定义 */
UNEXPECTED_EXCEPTION("500", "系统发生异常,请联系管理员!");
// 还可以定义更多的业务异常
/**
* 消息码
*/
private String code;
/**
* 消息内容
*/
private String msg;
private BusinessMsgEnum(String code, String msg) {
this.code = code;
this.msg = msg;
}
// set get方法
}
拦截自定义异常
我们可以定义一个业务异常,当出现业务异常时,我们就抛这个自定义的业务异常即可。比如我们定义一个 BusinessErrorException 异常,如下:
/**
* 自定义业务异常
* @author shengwu ni
*/
public class BusinessErrorException extends RuntimeException {
private static final long serialVersionUID = -7480022450501760611L;
/**
* 异常码
*/
private String code;
/**
* 异常提示信息
*/
private String message;
public BusinessErrorException(BusinessMsgEnum businessMsgEnum) {
this.code = businessMsgEnum.code();
this.message = businessMsgEnum.msg();
}
// get set方法
}
@ControllerAdvice
@ResponseBody
public class GlobalExceptionHandler {
private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
/**
* 拦截业务异常,返回业务异常信息
* @param ex
* @return
*/
@ExceptionHandler(BusinessErrorException.class)
@ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
public JsonResult handleBusinessError(BusinessErrorException ex) {
String code = ex.getCode();
String message = ex.getMessage();
return new JsonResult(code, message);
}
}
在业务代码中,我们可以直接模拟一下抛出业务异常,测试一下:
@RestController
@RequestMapping("/exception")
public class ExceptionController {
private static final Logger logger = LoggerFactory.getLogger(ExceptionController.class);
@GetMapping("/business")
public JsonResult testException() {
try {
int i = 1 / 0;
} catch (Exception e) {
throw new BusinessErrorException(BusinessMsgEnum.UNEXPECTED_EXCEPTION);
}
return new JsonResult();
}
}
运行一下项目,测试一下,返回 JSON 如下,说明我们自定义的业务异常捕获成功:
{"code":"500","msg":"系统发生异常,请联系管理员!"}
课程更新到此。。。
感谢倪升武老师的达人课,本篇博客为所做笔记,如果涉及到侵权行为,请告知,将立即删除。
原文链接:http://blog.maptoface.com/post/118