SpringBoot加Lambda进行参数校验的一个通用方案

2021-08-31 14:53:03 浏览数 (2)

一、背景

有这样一个业务场景,用户要申请一个功能,但是这个功能需要有校验多种资格。如果有些资格不满足需要给用户提示。

下面给出一个简单的通用方案。

这个方案的优势是,加新的校验非常容易,只需要写一个新的校验函数,添加校验条件即可,不至于把所有校验写在一大串代码里,导致可读性,可维护性都不好。

其实还可以更强大一些,可以在应用启动后获取某个注解或者继承自某个类或接口的所有校验类,然后校验时自动调用。

参见另外一篇博客:https://cloud.tencent.com/developer/article/1870281

二、方法

2.1 项目结构

2.2 pom文件

代码语言:javascript复制
    4.0.0

    com.chujianyun
    lambdacheck
    1.0-SNAPSHOT

    
        org.springframework.boot
        spring-boot-starter-parent
        2.1.6.RELEASE
    

    
        
            org.springframework.boot
            spring-boot-starter-web
        

        
            org.projectlombok
            lombok
            1.18.8
        

        
        
            org.apache.commons
            commons-lang3
            3.9
        

    

    
        1.8
    
    
        
            
                org.springframework.boot
                spring-boot-maven-plugin

2.3 实体

代码语言:javascript复制
import lombok.Data;

@Data
public class UserParam {
    /**
     * 用户ID
     */
    private Long userId;
}

结果

代码语言:javascript复制
package com.chujianyun.entity.dto;

import lombok.Data;

import java.util.List;

@Data
public class UserCheckResultDTO {

    /**
     * 是否有效
     */
    private Boolean isValidUser;

    /**
     * 是否白名单
     */
    private Boolean isInWhiteList;

    /**
     * 是否等级高
     */
    private Boolean isHighLevel;

    /**
     * 失败原因
     */
    private List failedMessages;
}

上下文

代码语言:javascript复制
import com.chujianyun.entity.dto.UserCheckResultDTO;
import lombok.Data;

@Data
public class UserCheckContext {

    private UserCheckResultDTO userCheckResultDTO = new UserCheckResultDTO();

    // 可以携带其他结果
}

2.4 核心封装

代码语言:javascript复制
package com.chujianyun.component;

import com.chujianyun.entity.context.UserCheckContext;
import com.chujianyun.entity.dto.UserCheckResultDTO;
import com.chujianyun.entity.param.UserParam;
import com.chujianyun.util.CheckUtil;
import org.apache.commons.lang3.RandomUtils;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;

@Component
public class UserCheckFuntions {

    // 注入校验所需的各种Bean

    public Function checkIsValid(UserParam userParam) {

        return CheckUtil.buildCheck(userCheckContext -> {
            UserCheckResultDTO userCheckResultDTO = userCheckContext.getUserCheckResultDTO();
            // 模拟调用服务A,检查有效性
            boolean result = (userParam.getUserId() > 50);
            if (result) {
                userCheckResultDTO.setIsValidUser(true);
            } else {
                userCheckResultDTO.setIsValidUser(false);
                addFailedMessage(userCheckResultDTO, "无效");
            }
        });
    }

    public Function checkIsInWhiteList(UserParam userParam) {
        return CheckUtil.buildCheck(userCheckContext -> {
            UserCheckResultDTO userCheckResultDTO = userCheckContext.getUserCheckResultDTO();
            // 模拟调用服务B,检查是否在白名单
            boolean result = (userParam.getUserId() > 500);
            if (result) {
                userCheckResultDTO.setIsInWhiteList(true);
            } else {
                userCheckResultDTO.setIsInWhiteList(false);
                addFailedMessage(userCheckResultDTO, "不在白名单");
            }
        });
    }

    public Function checkIsHighLevel(UserParam userParam) {
        return CheckUtil.buildCheck(userCheckContext -> {
            UserCheckResultDTO userCheckResultDTO = userCheckContext.getUserCheckResultDTO();
            // 模拟调用服务C,检查是否高级用户
            boolean result = (userParam.getUserId() > 30);
            if (result) {
                userCheckResultDTO.setIsHighLevel(true);
            } else {
                userCheckResultDTO.setIsHighLevel(false);
                addFailedMessage(userCheckResultDTO, "等级不够");
            }
        });
    }

    /**
     * 添加失败的信息
     */
    public void addFailedMessage(UserCheckResultDTO userCheckResultDTO, String message) {

        List failMessages = userCheckResultDTO.getFailedMessages();
        if (failMessages == null) {
            failMessages = new ArrayList<>();
            userCheckResultDTO.setFailedMessages(failMessages);
        }
        failMessages.add(message);

    }
}

2.5 服务类

代码语言:javascript复制
import com.chujianyun.entity.dto.UserCheckResultDTO;
import com.chujianyun.entity.param.UserParam;

public interface UserService {

    UserCheckResultDTO checkUser(UserParam userParam);
}

实现

代码语言:javascript复制
import com.chujianyun.component.UserCheckFuntions;
import com.chujianyun.entity.context.UserCheckContext;
import com.chujianyun.entity.dto.UserCheckResultDTO;
import com.chujianyun.entity.param.UserParam;
import com.chujianyun.service.UserService;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

@Service
public class UserServiceImpl implements UserService {

    @Resource
    private UserCheckFuntions userCheckFuntions;

    @Override
    public UserCheckResultDTO checkUser(UserParam userParam) {
        UserCheckContext userCheckContext = new UserCheckContext();
        return userCheckFuntions.checkIsValid(userParam)
                .andThen(userCheckFuntions.checkIsInWhiteList(userParam))
                .andThen(userCheckFuntions.checkIsHighLevel(userParam))
                .apply(userCheckContext)
                .getUserCheckResultDTO();
    }
}

如果需要新增一个校验,则结果对象里加一个boolean属性,在Function里加一个校验函数,然后再实现类里加一个andThen的校验即可。

2.6 控制器

代码语言:javascript复制
import com.chujianyun.entity.dto.UserCheckResultDTO;
import com.chujianyun.entity.param.UserParam;
import com.chujianyun.service.UserService;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.annotation.Resource;

@Controller
@RequestMapping("/user")
public class HelloController {

    @Resource
    private UserService userService;


    @PostMapping("/check")
    public ResponseEntity checkUser(UserParam userParam) {

        return new ResponseEntity<>(userService.checkUser(userParam), HttpStatus.OK);
    }
}

2.7 校验工具类

代码语言:javascript复制
package com.chujianyun.util;

import java.util.function.Consumer;
import java.util.function.Function;

public class CheckUtil {

    public static  Function buildCheck(Consumer checkConsumer) {
        return (checkContext) -> {
            checkConsumer.accept(checkContext);
            return checkContext;
        };
    }
}

2.8 测试

三、总结

本文主要演示Lambda表达式在参数校验的特殊场景下的一个很有趣的应用,可读性,可拓展性更强。

给我们的启发是要灵活运用Java8提供的新的函数式类。

0 人点赞