To avoid too much non-null validation for a large number of parameters, we can customize a non-null validation annotation because spring’s @RequestParam does not allow non-null validation for parameters

The preparatory work

You need to create a Spring Boot project and import the relevant Maven dependencies. The POM file is as follows:

<? 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 http://maven.apache.org/xsd/maven-4.0.0.xsd"> < modelVersion > 4.0.0 < / modelVersion > < the parent > < groupId > org. Springframework. Boot < / groupId > The < artifactId > spring - the boot - starter - parent < / artifactId > < version > 2.1.1. RELEASE < / version > < relativePath / > <! -- lookup parent from repository --> </parent> <groupId>com.ooliuyue</groupId> <artifactId>aoptest</artifactId> < version > 0.0.1 - the SNAPSHOT < / version > < name > aoptest < / name > < description > Demo projectforSpring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

Copy the code

Custom annotations

The general idea is to define a custom annotation, add the annotation to the required parameter, define a facet, verify that the parameter is null, if it is null, throw a custom exception, which is caught by the custom exception handler, and return the corresponding error message. Create an annotation called ‘ParamCheck’ as follows:

package com.ooliuyue.springboot_aoptest.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * @Auther: ly * @Date: */ ** * "Parameter not empty" annotation, */ @target (elementtype.parameter) // indicates that the Annotation is applied to method parameters. /** * @retention defines how long the Annotation is retained: Some annotations appear only in the source code and are discarded by the compiler; * While others are compiled in class files; * Annotations compiled in a class file may be ignored by the virtual machine, * while others will be read when the class is loaded (note that class execution is not affected, since annotations and classes are used separately). * Use this meta-annotation to limit the "life cycle" of an Annotation. * &emsp; &emsp; Purpose: Indicates the level at which the annotation information needs to be saved and is used to describe the lifecycle of the annotation (i.e., the extent to which the described annotation is valid). &emsp; The value of RetentionPoicy is * & EMsp; &emsp; &emsp; &emsp; 1.SOURCE: valid in the SOURCE file (i.e. retained in the SOURCE file) &emsp; &emsp; &emsp; 2.CLASS: valid in a CLASS file. &emsp; &emsp; &emsp; RUNTIME: This parameter is valid at RUNTIME (that is, retained at RUNTIME) */ @Retention(retentionPolicy.runtime) public @interface ParamCheck {/** * Specifies whether the parameter is non-empty. The default value cannot be empty * @return
     */
    boolean notNull() default true;
}

Copy the code

Custom exception classes

This exception class is used in conjunction with a custom annotation that throws an exception when the argument with ‘@paramCheck’ is null, as follows:

package com.ooliuyue.springboot_aoptest.exception; /** * custom exception class * @auther: ly * @date: 2018/12/27 09:55 */ public class ParamIsNullException extends RuntimeException { private final String parameterName; private final String parameterType; public StringgetParameterName() {
        return parameterName;
    }

    public String getParameterType() {
        return parameterType;
    }


    public ParamIsNullException(String parameterName, String parameterType) {
        super("");
        this.parameterName = parameterName;
        this.parameterType = parameterType;
    }

    public String getMessage() {return "Request parameters"  + this.parameterName + "It cannot be empty!; }}Copy the code

Custom AOP

The code is as follows:

package com.ooliuyue.springboot_aoptest.aop; import com.ooliuyue.springboot_aoptest.annotation.ParamCheck; import com.ooliuyue.springboot_aoptest.exception.ParamIsNullException; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.aspectj.lang.reflect.MethodSignature; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import java.lang.annotation.Annotation; import java.lang.reflect.Method; /** * @Auther: ly * @Date: @aspect public class ParamCheckAop {private Logger Logger = LoggerFactory.getLogger(this.getClass()); /** * define a Pointcut for classes in the Controller package */ @pointcut ("execution(public * com.ooliuyue.springboot_aoptest.controller.. *. * (..) )")
    public void checkParam@param joinPoint */ @before (){} @param joinPoint */"checkParam()")
    public void doBefore(JoinPoint joinPoint){
        logger.info("Perform non-null validation");

    }

    @Around("checkParam()")
    public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { MethodSignature signature = (MethodSignature) proceedingJoinPoint.getSignature(); Method = signature.getMethod(); Annotation / / acquisition method parameters, returned a two-dimensional array is because there may be some parameters more annotations Annotation [] [] parameterAnnotations = method. The getParameterAnnotations ();if (parameterAnnotations == null || parameterAnnotations.length == 0) {
            returnproceedingJoinPoint.proceed(); } / / acquisition method parameter name String [] paramNames = signature. GetParameterNames (); / / get the parameter value Object [] paranValues = proceedingJoinPoint. GetArgs (); // Get method parameter type Class<? >[] parameterTypes = method.getParameterTypes();for (int i = 0; i < parameterAnnotations.length; i++ ) {
            for(int j = 0; j < parameterAnnotations[i].length; J++) {// if this parameter is preceded by an instance of ParamCheck and notNull()=true, non-null check is performedif(parameterAnnotations[i][j] ! = null && parameterAnnotations[i][j] instanceof ParamCheck && ((ParamCheck) parameterAnnotations[i][j]).notNull()) { paramIsNull(paramNames[i], paranValues[i], parameterTypes[i] == null ? null : parameterTypes[i].getName());break; }}}returnproceedingJoinPoint.proceed(); } /** * at the pointcutreturn* * @joinPoint */ @afterreturning ("checkParam()")
    public void doAfterReturning(JoinPoint JoinPoint) {} /** * ParamIsNullException exception * @param paramName * @param Value * @param parameterType */ private void paramIsNull(String) paramName, Object value, String parameterType) {if (value == null || "".equals(value.toString().trim())) { throw new ParamIsNullException(paramName, parameterType); }}}Copy the code

Global exception handler

The exception handler catches the ParamIsNullException thrown in the ParamCheckAop class and processes it as follows:

package com.ooliuyue.springboot_aoptest.exception;

import com.ooliuyue.springboot_aoptest.common.Result;
import com.ooliuyue.springboot_aoptest.enums.EnumResultCode;
import com.ooliuyue.springboot_aoptest.utils.ResponseMsgUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;


/**
 * @Auther: ly
 * @Date: 2018/12/27 13:50
 */
@RestControllerAdvice
public class GlobalExceptionHandler {

    private static final Logger LOGGER = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    @ExceptionHandler({MissingServletRequestParameterException.class, ParamIsNullException.class})
    public Result<String> getException(Exception ex){
        LOGGER.error("request Exception:", ex);
        returnResponseMsgUtil.builderResponse(EnumResultCode.FAIL.getCode(),ex.getMessage(),null); }}Copy the code

Controller

Create a new class called HelloController to test with the following code:

package com.ooliuyue.springboot_aoptest.controller;

import com.ooliuyue.springboot_aoptest.annotation.ParamCheck;
import com.ooliuyue.springboot_aoptest.common.Result;
import com.ooliuyue.springboot_aoptest.enums.EnumResultCode;
import com.ooliuyue.springboot_aoptest.utils.ResponseMsgUtil;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

/**
 * @Auther: ly
 * @Date: 2018/12/27 11:43
 */
@RestController

public class HelloController {
    /**
     * 测试@RequestParam注解
     * @param name
     * @return
     */
    @GetMapping("/hello1")
    public Result<String> hello1(@RequestParam String name){
        return ResponseMsgUtil.builderResponse(EnumResultCode.SUCCESS.getCode(),"Request successful"."Hello,"+ name); } /** * Test @paramCheck annotation * @param name * @return
     */
    @GetMapping("/hello2")
    public Result<String> hello2(@ParamCheck String name){
        return ResponseMsgUtil.builderResponse(EnumResultCode.SUCCESS.getCode(),"Request successful"."Hello,"+ name); } /** * tests @paramCheck and @requestParam ** @param name * @return
     */
    @GetMapping("/hello3")
    public Result<String> hello3(@ParamCheck @RequestParam String name){
        return ResponseMsgUtil.builderResponse(EnumResultCode.SUCCESS.getCode(),"Request successful"."Hello,"+ name); }}Copy the code

The test results

  • The parameter value is empty In the browser type http://localhost:8080/hello1, the results are as follows:

    The console output an error message

  • The parameter value is not empty Input http://localhost:8080/hello1? in your browser Name = zhang SAN, the result is as follows:

  • The parameter value is empty Input http://localhost:8080/hello2? in your browser Name =, the result is as follows:

    The console output an error message

  • The parameter value is not empty Input http://localhost:8080/hello2? in your browser Name = Li Si, the result is as follows:

    Same as the result of testing @paramCheck

Test summary

The @requestParam annotation does not report an error when the parameter name is not null and the value is null, but the @paramCheck annotation indicates that the parameter ‘name’ is null

Source code download address

Making code cloud