The use and partial analysis of Assert in SpringBoot’s built-in tool class

2023.10.29 update:

Disadvantages of using assert:

1. The assert keyword needs to be explicitly enabled at runtime to take effect, otherwise your assertion will be meaningless. Currently, mainstream Java IDE tools do not enable the -ea assertion checking function by default. This means that if you use IDE tools to code, you will have some trouble when debugging and running. Moreover, for Java Web applications, the program code is deployed in the container, and you cannot directly control the running of the program. If you must turn on the -ea switch, you need to change the running configuration parameters of the Web container. This brings great inconvenience to the transplantation and deployment of programs.

2. Using assert instead of if is the second trap. The judgment of assert is similar to that of if statement, but the functions of the two are essentially different: the assert keyword is intended to be used when testing and debugging the program, but if you accidentally use assert to control the business process of the program, then it will not work properly during testing and debugging. Removing the assert keyword after completion means modifying the normal logic of the program.

3. If the assert assertion fails, the program will exit. This would never be tolerated in a production environment. Potential errors in the program are generally solved through exception handling. But using assertions is very dangerous. Once it fails, the system will hang.

Therefore, you should avoid using the assert keyword in Java, unless one day Java supports the -ea switch by default, then you can consider it. Compare how much benefit and how much trouble assert can bring you. This is the principle for us to choose whether to use it.

Foreword

Let’s take an example of the encapsulation method used to verify the parameter object in the service demo. After using the Assert tool class, does it look much simpler than the ordinary if(xxx) { throw new RuntimeException(msg) }?

Introduction to the Assert tool class

  1. Assertion is a judgment logic used to check for situations that should not happen;
  2. Determination rules for assertions:
    2.1. When the value is true, the program continues execution from the assertion statement;
    2.2. When the value is false, the program throws an exception from the assertion statement and stops execution;
    2.3. As early as version 1.4 of JDK, assertion assert has been introduced. It can be turned on through the command -enableassertions and turned off through the command -disableassertions. Without parameters, it will take effect globally. If parameters are added, it will only be used in a certain class. Specifically, you can use the java help command. Check;
  3. The Springframework framework also provides the assertion tool class Assert, which is usually used for data validity verification. That’s what we’re talking about today.

Usage of Assert tool class

public static void main(String[] argo){<!-- -->
    
    Object obj = null;
    
    Assert.isNull(obj, "The object must be NULL, otherwise an exception will be thrown and it will not be released");
    
    Assert.notNull(new Object(), "The object cannot be NULL, otherwise an exception will be thrown and it will not be released");
    
    Assert.state(true, "The parameter must be true, otherwise an exception will be thrown and the release will not be allowed");
    
    Assert.isTrue(true, "The parameter must be true, otherwise an exception will be thrown and the release will not be allowed");
    
    // null or empty string assertion fails, space assertion succeeds
    Assert.hasLength(" ", "The parameter must have a length, otherwise an exception will be thrown and it will not be released");
    // Null, empty string, and pure whitespace assertions fail
    Assert.hasText(" dd", "The parameter must have text, otherwise an exception will be thrown and it will not be released");
    
    Assert.doesNotContain("text", "bb", "The first parameter cannot contain the second parameter, otherwise an exception will be thrown and the release will not be allowed");
    
    Object[] objArray = {<!-- -->true, false,};
    // In addition to object arrays, there are other types of arrays. I will not give examples here.
    Assert.notEmpty(objArray, "The object array cannot be empty, otherwise an exception will be thrown and it will not be released");
    
    Assert.noNullElements(objArray, "There cannot be null elements in the object array, otherwise an exception will be thrown and no release will be allowed");
    
    Map<String,String> map = new HashMap<String,String>();
    
    Assert.isInstanceOf(Map.class, map, "The second parameter must be an instance of the first parameter, otherwise an exception will be thrown and it will not be released");
    
    Assert.isAssignable(Map.class, List.class, "The second parameter must be a subclass or implementation class of the first parameter, otherwise an exception will be thrown and will not be released");
    
    System.out.println("All assertions successful~!");
  }

When the last assertion fails, an exception is thrown at the failed statement, and the program terminates here. The running results are as follows:

Exception in thread "main" java.lang.IllegalArgumentException: The second parameter must be a subclass or implementation class of the first parameter, otherwise an exception will be thrown and will not be allowed: interface java.util.List
  at org.springframework.util.Assert.assignableCheckFailed(Assert.java:720)
  at org.springframework.util.Assert.isAssignable(Assert.java:651)
  at com.example.util.SpringUtilTest.main(SpringUtilTest.java:40)

Part of the source code of the Assert tool class

import java.util.Collection;
import java.util.Map;
import java.util.function.Supplier;

import org.springframework.lang.Nullable;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

/**
 * Assertion tool class
 * SpringBoot 2.1.4.RELEASE
 * Continue execution if the conditions are met, otherwise an exception will be thrown and the release will not be allowed.
 */
public abstract class Assert {<!-- -->

  /**
   * The parameter expression must be true, otherwise an exception will be thrown and the release will not be allowed.
   * @param expression boolean expression
   * @param message is used to customize the exception message content
   */
  public static void state(boolean expression, String message) {<!-- -->
    if (!expression) {<!-- -->
      throw new IllegalStateException(message);
    }
  }

  /**
   * The parameter expression must be true, otherwise an exception will be thrown and the release will not be allowed.
   * @param expression
   * @param message
   */
  public static void isTrue(boolean expression, String message) {<!-- -->
    if (!expression) {<!-- -->
      throw new IllegalArgumentException(message);
    }
  }

  /**
   * The object object must be null, otherwise an exception will be thrown and the release will not be allowed.
   * @param object
   * @param message
   */
  public static void isNull(@Nullable Object object, String message) {<!-- -->
    if (object != null) {<!-- -->
      throw new IllegalArgumentException(message);
    }
  }
  
  /**
   * It is required that the parameter object is not null, otherwise an exception will be thrown and will not be released;
   * Opposite of isNull() method
   * @param object
   * @param message
   */
  public static void notNull(@Nullable Object object, String message) {<!-- -->
    if (object == null) {<!-- -->
      throw new IllegalArgumentException(message);
    }
  }

  /**
   * It is required that the parameter text must have a length, not null and the length is greater than 0, otherwise an exception will be thrown and will not be released.
   * @param text string text
   * @param message
   */
  public static void hasLength(@Nullable String text, String message) {<!-- -->
    if (!StringUtils.hasLength(text)) {<!-- -->
      throw new IllegalArgumentException(message);
    }
  }

  /**
   * It is required that the parameter text must have content, otherwise an exception will be thrown and the release will not be allowed.
   * @param text string text
   * @param message
   */
  public static void hasText(@Nullable String text, String message) {<!-- -->
    if (!StringUtils.hasText(text)) {<!-- -->
      throw new IllegalArgumentException(message);
    }
  }

  /**
   * It is required that textToSearch does not contain substring, otherwise an exception will be thrown and will not be released.
   * @param textToSearch The string text to be retrieved
   * @param substring retrieved characters
   * @param message
   */
  public static void doesNotContain(@Nullable String textToSearch, String substring, String message) {<!-- -->
    if (StringUtils.hasLength(textToSearch) & amp; & amp; StringUtils.hasLength(substring) & amp; & amp;
        textToSearch.contains(substring)) {<!-- -->
      throw new IllegalArgumentException(message);
    }
  }

  /**
   * It is required that the parameter array is not null, otherwise an exception will be thrown and the release will not be allowed.
   * @param array object array
   * @param message
   */
  public static void notEmpty(@Nullable Object[] array, String message) {<!-- -->
    if (ObjectUtils.isEmpty(array)) {<!-- -->
      throw new IllegalArgumentException(message);
    }
  }
  
  /**
   * It is required that there is no Null element in the object array array, otherwise an exception will be thrown and the release will not be allowed.
   * @param array
   * @param message
   */
  public static void noNullElements(@Nullable Object[] array, String message) {<!-- -->
    if (array != null) {<!-- -->
      for (Object element : array) {<!-- -->
        if (element == null) {<!-- -->
          throw new IllegalArgumentException(message);
        }
      }
    }
  }
  
  /**
   * It is required that the collection is not null or empty, otherwise an exception will be thrown and the release will not be allowed.
   * @param collection
   * @param message
   */
  public static void notEmpty(@Nullable Collection<?> collection, String message) {<!-- -->
    if (CollectionUtils.isEmpty(collection)) {<!-- -->
      throw new IllegalArgumentException(message);
    }
  }

  /**
   * It is required that the Map set is not null or empty, otherwise an exception will be thrown and the release will not be allowed.
   * @param map the map to check
   * @param message the exception message to use if the assertion fails
   * @throws IllegalArgumentException if the map is {@code null} or contains no entries
   */
  public static void notEmpty(@Nullable Map<?, ?> map, String message) {<!-- -->
    if (CollectionUtils.isEmpty(map)) {<!-- -->
      throw new IllegalArgumentException(message);
    }
  }
  
  /**
   * It is required that the object obj must be an instance of the specified class type, otherwise an exception will be thrown and it will not be released.
   * @param type
   * @param obj
   * @param message
   */
  public static void isInstanceOf(Class<?> type, @Nullable Object obj, String message) {<!-- -->
    notNull(type, "Type to check against must not be null");
    if (!type.isInstance(obj)) {<!-- -->
      instanceCheckFailed(type, obj, message);
    }
  }

  /**
   * It is required that the object obj must be an instance of the specified class type, otherwise an exception will be thrown and it will not be released.
   * @param type
   * @param obj
   * @param message
   */
  public static void isInstanceOf(Class<?> type, @Nullable Object obj) {<!-- -->
    isInstanceOf(type, obj, "");
  }

  /**
   * It is required that the parameter subType must be a subclass or implementation class of the parameter superType, otherwise an exception will be thrown and the release will not be allowed.
   * @param superType
   * @param subType
   * @param message
   */
  public static void isAssignable(Class<?> superType, @Nullable Class<?> subType, String message) {<!-- -->
    notNull(superType, "Super type to check against must not be null");
    if (subType == null || !superType.isAssignableFrom(subType)) {<!-- -->
      assignableCheckFailed(superType, subType, message);
    }
  }

  /**
   * It is required that the parameter subType must be a subclass or implementation class of the parameter superType, otherwise an exception will be thrown and will not be released.
   * @param superType
   * @param subType
   * @param message
   */
  public static void isAssignable(Class<?> superType, Class<?> subType) {<!-- -->
    isAssignable(superType, subType, "");
  }
  
  private static void instanceCheckFailed(Class<?> type, @Nullable Object obj, @Nullable String msg) {<!-- -->
    String className = (obj != null ? obj.getClass().getName() : "null");
    String result = "";
    boolean defaultMessage = true;
    if (StringUtils.hasLength(msg)) {<!-- -->
      if (endsWithSeparator(msg)) {<!-- -->
        result = msg + " ";
      }
      else {<!-- -->
        result = messageWithTypeName(msg, className);
        defaultMessage = false;
      }
    }
    if (defaultMessage) {<!-- -->
      result = result + ("Object of class [" + className + "] must be an instance of " + type);
    }
    throw new IllegalArgumentException(result);
  }

  private static void assignableCheckFailed(Class<?> superType, @Nullable Class<?> subType, @Nullable String msg) {<!-- -->
    String result = "";
    boolean defaultMessage = true;
    if (StringUtils.hasLength(msg)) {<!-- -->
      if (endsWithSeparator(msg)) {<!-- -->
        result = msg + " ";
      }
      else {<!-- -->
        result = messageWithTypeName(msg, subType);
        defaultMessage = false;
      }
    }
    if (defaultMessage) {<!-- -->
      result = result + (subType + " is not assignable to " + superType);
    }
    throw new IllegalArgumentException(result);
  }

  private static boolean endsWithSeparator(String msg) {<!-- -->
    return (msg.endsWith(":") || msg.endsWith(";") || msg.endsWith(",") || msg.endsWith("."));
  }

  private static String messageWithTypeName(String msg, @Nullable Object typeName) {<!-- -->
    return msg + (msg.endsWith(" ") ? "" : ": ") + typeName;
  }

  @Nullable
  private static String nullSafeGet(@Nullable Supplier<String> messageSupplier) {<!-- -->
    return (messageSupplier != null ? messageSupplier.get() : null);
  }
}

To put it simply, if the verification fails, an exception is thrown and the execution of the code is terminated.

Summary

There are about 30 static methods in the Assert tool class for external classes to call. Its characteristic is that it continues to execute if it meets the conditions, otherwise an IllegalArgumentException is thrown. This tool class is a tool class in the Spring framework util package (org.springframework.util)

Reference: SpringBoot built-in tool class Assert