Official account: Java Xiaokaxiu, website: Javaxks.com

Author: return to the home page nothing of the blog, source: cnblogs.com/xuwujing/p/10933082.html

preface

This article mainly introduces the SpringBoot project to handle global exceptions.

SpringBoot global exception preparation

Note: If you want to get the project directly, you can jump to the bottom and download the project code through the link.

The development of preparation

Environment requirements JDK: 1.8 SpringBoot: 1.5.17.RELEASE

First, there are Maven dependencies:

< properties > < project. Build. SourceEncoding > utf-8 < / project. Build. SourceEncoding > < Java version > 1.8 < / Java version > Piler < maven.com. Source > 1.8 < / maven.com piler. Source > < maven.com piler. Target > 1.8 < / maven.com piler. Target > < / properties > <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.17.RELEASE</version> <relativePath /> </parent> <dependencies> <! <dependency> <groupId>org.springframework. Boot </groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <! --> <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>Copy the code

The configuration file is basically unchanged, and global exception handling is done in code.

The code

The SpringBoot project already has some exception handling, but it may not be suitable for us developers, so we need to uniformly catch and handle these exceptions. SpringBoot has a ControllerAdvice annotation that indicates that global exception catching is enabled. We need to use the ExceptionHandler annotation in a custom method and define the type of exception to catch.

Let’s look at the following example to see how this annotation is used.

Sample code:

@ControllerAdvice public class MyExceptionHandler { @ExceptionHandler(value =Exception.class) public String ExceptionHandler (Exception E){system.out.println (" unknown Exception! The reason is: "+ e); return e.getMessage(); }}Copy the code

In the example above, we do a simple secondary processing of the caught exception and return the exception information. Although this can let us know the cause of the exception, in many cases, it may not be humanized enough to meet our requirements. So we can use custom exception classes and enumeration classes to implement the kind of data we want.

Custom base interface classes

Start by defining a basic interface class that a custom error description enumeration class implements. The code is as follows:

Public interface BaseErrorInfoInterface {/** Error code */ String getResultCode(); /** error description */ String getResultMsg(); }Copy the code

Custom enumerated classes

We are then customizing an enumerated class and implementing the interface. The code is as follows:

Public enum CommonEnum implements BaseErrorInfoInterface {// ), BODY_NOT_MATCH("400"," Requested data format does not match!" ), SIGNATURE_NOT_MATCH("401"," Requested digital signature does not match!" ), NOT_FOUND("404", "Resource not found!" ), INTERNAL_SERVER_ERROR("500", "server internal error!" ), SERVER_BUSY("503"," Server is busy, please try again later!" ); /** error code */ 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; }}Copy the code

Custom exception classes

We then customize an exception class to handle the business exceptions we have. The code is as follows:

public class BizException extends RuntimeException { private static final long serialVersionUID = 1L; /** * errorCode */ protected String errorCode; /** * error message */ 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; }}Copy the code

Custom data formats

By the way, let’s define the data transfer format here. The code is as follows:

Public class ResultBody {/** * response code */ private String code; /** * response message */ 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; } public static ResultBody success() {return success(null); } 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();} public static ResultBody error(BaseErrorInfoInterface errorInfo) {ResultBody rb = new ResultBody(); rb.setCode(errorInfo.getResultCode()); rb.setMessage(errorInfo.getResultMsg()); rb.setResult(null); return rb; } /** * failed */ 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); }}Copy the code

Custom global exception handling classes

Finally, we will write a custom global exception handling class. The code is as follows:

@ControllerAdvice public class GlobalExceptionHandler { private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class); /** * Handle custom service exceptions * @param req * @param e * @return */ @ExceptionHandler(value = bizException.class) @responseBody public ResultBody bizExceptionHandler(HttpServletRequest req, BizException e){logger.error(" Service exception! {}", lltterrorMsg ()); return ResultBody.error(e.getErrorCode(),e.getErrorMsg()); } / handle null pointer exception * * * * @ param the req * @ param e * @ return * / @ ExceptionHandler @ ResponseBody (value = NullPointerException. Class) Public ResultBody exceptionHandler(HttpServletRequest req, NullPointerException e){logger.error(" NullPointerException! The reason is: ", 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(" Unknown Exception! The reason is: ", e); return ResultBody.error(CommonEnum.INTERNAL_SERVER_ERROR); }}Copy the code

Since we are only implementing and testing global exception handling, we only need to add an entity class and a control layer class.

Entity class

The All-purpose User List (▽)

The code is as follows:

public class User implements Serializable{ private static final long serialVersionUID = 1L; /** id */ private int id; /** name */ private String name; /** age */ 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); }}Copy the code

The Controller control layer

The control layer is also relatively simple, using Restful CRUD functionality, except that here I deliberately throw exceptions so that they can be caught and handled. There are custom exceptions thrown, empty pointer exceptions thrown, of course, there are unpredictable exceptions thrown (I use type conversion exceptions instead), so after we finish writing the code, see if these exceptions can be caught and handled successfully!

The code is as follows:

@RestController @RequestMapping(value = "/api") public class UserRestController { @PostMapping("/user") public boolean Insert (@requestBody User User) {system.out.println (" start adding...") ); // Manually throw a custom exception if the name is empty! If (user.getName()==null){throw new BizException("-1"," User name cannot be empty! ); } return true; } @putMapping ("/user") public Boolean update(@requestBody user user) {system.out.println (" start updating... ); String STR =null; str.equals("111"); return true; } @deletemapping ("/user") public Boolean delete(@requestBody user user) {system.out.println (" start delete... ); Integer.parseint ("abc123"); return true; } @getMapping ("/user") public List< user > findByUser(user user) {system.out.println (" start query... ); List<User> userList =new ArrayList<>(); User user2=new User(); user2.setId(1L); user2.setName("xuwujing"); user2.setAge(18); userList.add(user2); return userList; }}Copy the code

App entrance

Basically the same as a normal SpringBoot project.

The code is as follows:

@SpringBootApplication public class App { public static void main( String[] args ) { SpringApplication.run(App.class, args); System.out.println(" The program is running...") ); }}Copy the code

A functional test

After we successfully started the program, we used the Postman tool to test the interface.

First query, check whether the normal operation of the program is OK, use GET to request.

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

The return parameter is:

{“id”:1,”name”:”xuwujing”,”age”:18}

Figure:

You can see that the program returns normally, not affected by the custom global exception.

We then test to see if the custom exception can be caught and handled correctly.

POST is used to make requests

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

The Body argument is:

{“id”:1,”age”:18}

The return parameter is:

{“code”:”-1″,”message”:” User name cannot be empty!” ,”result”:null}

Figure:

You can see that wrapping the exception that we throw in data, and then returning the exception.

We then test to see if null-pointer exceptions can be caught and handled correctly. In custom global exceptions, we define not only null-pointer Exception handling, but also one of the highest levels of Exception. So when null-pointer Exception occurs here, which one does it use first? So let’s test it out here.

Use PUT to request.

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

The Body argument is:

{“id”:1,”age”:18}

The return parameter is:

{“code”:”400″,”message”:” Requested data format is not correct!” ,”result”:null}

Figure:

We can see that this is indeed an exception care that returns a null pointer, and we can conclude that global exception handling takes precedence over subclass exceptions.

So let’s try handling an unspecified exception to see if the exception can be caught.

Use DELETE to make the request.

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

The Body argument is:

{“id”:1}

The return parameter is:

{“code”:”500″,”message”:” Server internal error!” ,”result”:null}

You can see here that it uses the Exception handling method we used in our custom global Exception handling class. At this point, the test is over. By the way, custom global exception handling can handle jumps to the page in addition to the data format described above, by specifying the jump path on the return handler of the new exception method without using the ResponseBody annotation. Those of you who are careful may have noticed that the GlobalExceptionHandler class uses the ControllerAdvice annotation instead of the RestControllerAdvice annotation. If you use the RestControllerAdvice annotation, It automatically converts the data to JSON format, similar to Controller and RestController, so we have some flexibility after using global exception handling.

other

SpringBoot’s elegant global exception handling is now complete.

The project address

SpringBoot global exception handling project Address: github.com/xuwujing/sp…

SpringBoot entire collection address: github.com/xuwujing/sp…