Spring Boot uses assertions to throw custom exceptions and optimize the exception handling mechanism.

Article directory

    • What is an assertion?
    • What is an exception?
    • Exception handling mechanism implemented based on assertions
      • Create a custom exception class
      • Create a global exception handler
      • Create a custom assertion class
      • Create response code class
      • Create tool class
      • Test effect

What is an assertion?

In fact, Assertion is a feature introduced in Java 1.4, which provides developers with a simple mechanism to verify and debug the expected conditions of the code.

Java 1.4 version was released in 2002 and it introduced many new features and improvements, one of which was assertions. The purpose of assertions is to perform verifiable internal checks in a program to ensure the correctness and reliability of the code.

Assertions were first proposed by American computer scientist C.A.R. Hoare in 1989, and are called “assert statements”. Then, in Java 1.4, Sun Microsystems introduced it into the Java programming language and added the keyword assert to represent assertions.

Using the assert keyword, developers can write assertion statements in their code to check whether the status or conditions of the program meet expectations. If the assertion fails, an AssertionError exception will be thrown, prompting the developer that there is a problem with the assertion point.

For example, org.springframework.util.Assert is a tool class in Spring Framework, used for parameter verification and assertion judgment. It is a final class containing static methods.

In the org.springframework.util.Assert class, a series of static methods are provided for assertion operations, such as notNull(), isTrue() code>, hasText(), etc. These methods are mainly used to verify whether the method parameters or object state meet the expected conditions. If not, runtime exceptions such as IllegalArgumentException or IllegalStateException will be thrown.

The source code of the commonly used Assert method is as follows:

//Determine whether the incoming expression is true. If it is false, throw an `IllegalArgumentException` exception and use the specified error information.
public static void isTrue(boolean expression, String message) {<!-- -->
    if (!expression) {<!-- -->
        throw new IllegalArgumentException(message);
    }
}

//Determine whether the incoming object is null. If it is null, throw an `IllegalArgumentException` exception and use the specified error information.
public static void isNull(@Nullable Object object, String message) {<!-- -->
    if (object != null) {<!-- -->
        throw new IllegalArgumentException(message);
    }
}

The methods provided in the org.springframework.util.Assert class can help us perform quick parameter verification during the coding process and avoid using cumbersome if statements or manually throwing exceptions. At the same time, these methods also provide customizable error messages to help better understand the reasons for verification failures.

What is an exception?

Exception is an error or abnormal situation that occurs when a program is running. It interrupts the normal execution flow of the program and may cause the program to crash or produce unexpected results. In Java, exceptions are usually represented as an object inherited from the Throwable class, such as RuntimeException, IllegalArgumentException, etc.

Custom exceptions refer to developers defining their own exception classes based on Exception or its subclasses in Java. Typically, custom exception classes are used to represent specific exceptions in order to better classify and manage program exceptions. Custom exception classes can contain their own properties and methods to provide more detailed exception information and handling.

The reasons for needing custom exceptions are as follows:

  1. More fine-grained exception control: The exception types in the Java standard library are relatively comprehensive, but in actual applications, we may need more specific exception types to describe exceptions in a specific scenario. For example, we can customize a PayErrorException to represent payment exceptions, which can better distinguish different types of exceptions and handle them differently.

  2. Richer information: Exception types in the Java standard library usually only contain error information. If you need to pass more relevant information, you need to customize the exception class. For example, we can add some additional attributes, such as exception codes, request parameters, etc., to the custom exception class to better record and track exception information.

  3. More friendly exception prompts: Exception information in the Java standard library is usually obscure. If you need more friendly and easy-to-understand prompt information, you need to customize the exception class. By customizing exception classes, developers can provide more intuitive and understandable exception prompt information so that users or other developers can better understand exception situations.

In actual business, we often need to set up a global exception handler to uniformly handle and manage exceptions and return corresponding error messages to ensure the stability and reliability of the application.

Exception handling mechanism based on assertion

Create a custom exception class

First, you need to create a custom exception class, inherited from RuntimeException or its subclass:

/**
 * Customized general exception class
 */
public class CommonException extends RuntimeException {<!-- -->

    /**
     * No-parameter construction method
     */
    public CommonException() {<!-- -->
    }

    /**
     *Construction method, passing in exception information
     * @param message exception information
     */
    public CommonException(String message) {<!-- -->
        super(message);
    }

    /**
     *Construction method, passing in exception information and original exception object
     * @param message exception information
     * @param cause original exception object
     */
    public CommonException(String message, Throwable cause) {<!-- -->
        super(message, cause);
    }

    /**
     *Construction method, passing in the original exception object
     * @param cause original exception object
     */
    public CommonException(Throwable cause) {<!-- -->
        super(cause);
    }
}

In the custom exception class CommonException we provide several different construction methods to facilitate the creation of exception objects in different scenarios. Each constructor calls the parent class’s constructor super to initialize the exception object.

Create a global exception handler

We also need to create a global exception handler GlobalExceptionHandler to uniformly handle the custom exception classes we throw:

/**
 * Global exception handler
 */
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {<!-- -->

    /**
     * Handle CommonException exception
     * @param e exception object
     * @return exception information
     */
    @ExceptionHandler(CommonException.class)
    public JSONObject handleCommonException(CommonException e) {<!-- -->
        log.error("Error message:{}", e.getMessage());
        return JSON.parseObject(e.getMessage());
    }
}

On the global exception handler GlobalExceptionHandler class we use the @RestControllerAdvice annotation to identify this class as a global exception handler. In this class, a method handleCommonException is defined, which is used to handle the logic after exception capture. The @ExceptionHandler() annotation is added to the method to indicate that the method handles the specified self. Define the exception CommonException.

Create a custom assertion class

For assertion classes, we can imitate the org.springframework.util.Assert class, learn from its methods, and customize an abstract class MyAssert:

/**
 * Assertion class
 */
public abstract class MyAssert {<!-- -->

    /**
     * Determine whether the object is empty. If not, throw a CommonException.
     *
     * @param object The object to be judged
     */
    public void assertIsNull(@Nullable Object object) {<!-- -->
        // If the object is not empty, throw a CommonException and serialize the current object into a JSON string as exception information
        if (object != null) {<!-- -->
            throw new CommonException(JSON.toJSONString(this));
        }
    }

}

As above, we created a assertIsNull method. The parameter object is used to receive the object to be judged. If object is empty, then CommonExceptionException, and serialize the current object into a JSON string as exception information.

Create response code class

Create a custom response code class and inherit the assertion class MyAssert, so that we can directly call the method in the assertion class through the response code for judgment:

/**
 * Response code class, inherited from assertion class MyAssert
 */
@Getter
@NoArgsConstructor
@AllArgsConstructor
public class ResCode extends MyAssert {<!-- -->

    /**
     * response code
     */
    private int code;

    /**
     * response message
     */
    private String message;

}

In the above code, the annotations of the Lombok library are used to simplify code writing. The meaning of the specific annotations is as follows:

  • @Getter: Automatically generate getter methods for properties.
  • @NoArgsConstructor: Automatically generate no-argument constructor.
  • @AllArgsConstructor: Automatically generate a constructor containing all parameters.

Create tool class

The ResCodeUtils tool class is used to call the response code for verification, throw a custom exception and return the specified error message:

/**
 * Response code tool class, inherited from response code class ResCode
 */
public class ResCodeUtils extends ResCode {<!-- -->

    /**
     * Response code naa, indicating an error
     */
    public static ResCode naa = new ResCode(300, "Error");

    /**
     * Response code naa1, indicating naa1 error
     */
    public static ResCode naa1 = new ResCode(4044, "naa1 error");

}

Two static attributes naa and naa1 are provided in the ResCodeUtils class, which represent error types with response codes of 300 and 4044 respectively, making it easier to pass < Call it directly using code>class name..

Test effect

  1. Write the TestController class and add the @RestController annotation to indicate that this class is a controller class used to handle HTTP requests and return responses:

    @RestController
    public class TestController {<!-- -->
    
        @RequestMapping("/send1")
        public String send1() {<!-- -->
            ResCodeUtils.naa.assertIsNull(null);
            return "success";
        }
    
        @RequestMapping("/send2")
        public String send2() {<!-- -->
            ResCodeUtils.naa1.assertIsNull(new ResCode());
            return "success";
        }
    
    }
    

    In the TestController class, two request processing methods send1() and send2() are defined. These two methods use the @RequestMapping annotation to specify their corresponding URL paths.

    • ResCodeUtils.naa.assertIsNull(null) is called in the send1() method, which means that the naa response code is asserted and the incoming Whether the parameter is null. If the parameter is null, the assertion passes, otherwise an exception is thrown.

    • ResCodeUtils.naa1.assertIsNull(new ResCode()) is called in the send2() method, which means that the naa1 response code is asserted and judged Whether the parameter passed in is null. Since the parameter new ResCode() is not null, an exception will be thrown here.

    Whether it is the send1() or send2() method, the string “success” will eventually be returned as the processing result.

  2. Start the project and use ApiFox to access the corresponding interface.

    • Access send1(). Since the parameter is null, the assertion passes and the string “success” is returned:

      image-20231101211357371

    • When accessing send2(), since the parameter is new ResCode() and is not null, the assertion fails and the custom exception is thrown and is processed by the global exception handler GlobalExceptionHandler. Capture and return corresponding error information:

      image-20231101211431765

      At the same time, you can see the printed log information on the console

      image-20231101211511456

This article ends here. Thank you for reading. I hope it will be helpful to you! ! !