一、背景
有这样一个业务场景,用户要申请一个功能,但是这个功能需要有校验多种资格。如果有些资格不满足需要给用户提示。
下面给出一个简单的通用方案。
这个方案的优势是,加新的校验非常容易,只需要写一个新的校验函数,添加校验条件即可,不至于把所有校验写在一大串代码里,导致可读性,可维护性都不好。
其实还可以更强大一些,可以在应用启动后获取某个注解或者继承自某个类或接口的所有校验类,然后校验时自动调用。
二、方法
2.1 项目结构
2.2 pom文件
<?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> <groupId>com.chujianyun</groupId> <artifactId>lambdacheck</artifactId> <version>1.0-SNAPSHOT</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.6.RELEASE</version> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.8</version> </dependency> <!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 --> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.9</version> </dependency> </dependencies> <properties> <java.version>1.8</java.version> </properties> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
2.3 实体
import lombok.Data;@Datapublic class UserParam { /** * 用户ID */ private Long userId; }
结果
package com.chujianyun.entity.dto;import lombok.Data;import java.util.List;@Datapublic class UserCheckResultDTO { /** * 是否有效 */ private Boolean isValidUser; /** * 是否白名单 */ private Boolean isInWhiteList; /** * 是否等级高 */ private Boolean isHighLevel; /** * 失败原因 */ private List<String> failedMessages; }
上下文
import com.chujianyun.entity.dto.UserCheckResultDTO;import lombok.Data;@Datapublic class UserCheckContext { private UserCheckResultDTO userCheckResultDTO = new UserCheckResultDTO(); // 可以携带其他结果}
2.4 核心封装
import com.chujianyun.entity.context.UserCheckContext;import com.chujianyun.entity.dto.UserCheckResultDTO;import com.chujianyun.entity.param.UserParam;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;@Componentpublic class UserCheckFuntions { // 注入校验所需的各种Bean public Function<UserCheckContext, UserCheckContext> checkIsValid(UserParam userParam) { return 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<UserCheckContext, UserCheckContext> checkIsInWhiteList(UserParam userParam) { return 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<UserCheckContext, UserCheckContext> checkIsHighLevel(UserParam userParam) { return buildCheck(userCheckContext -> { UserCheckResultDTO userCheckResultDTO = userCheckContext.getUserCheckResultDTO(); // 模拟调用服务C,检查是否高级用户 boolean result = (userParam.getUserId() > 30); if (result) { userCheckResultDTO.setIsHighLevel(true); } else { userCheckResultDTO.setIsHighLevel(false); addFailedMessage(userCheckResultDTO, "等级不够"); } }); } public Function<UserCheckContext, UserCheckContext> buildCheck(Consumer<UserCheckContext> userCheckContextConsumer) { return (userCheckContext) -> { userCheckContextConsumer.accept(userCheckContext); return userCheckContext; }; } /** * 添加失败的信息 */ public void addFailedMessage(UserCheckResultDTO userCheckResultDTO, String message) { List<String> failMessages = userCheckResultDTO.getFailedMessages(); if (failMessages == null) { failMessages = new ArrayList<>(); userCheckResultDTO.setFailedMessages(failMessages); } failMessages.add(message); } }
2.5 服务类
import com.chujianyun.entity.dto.UserCheckResultDTO;import com.chujianyun.entity.param.UserParam;public interface UserService { UserCheckResultDTO checkUser(UserParam userParam); }
实现
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;@Servicepublic 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 控制器
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<UserCheckResultDTO> checkUser(UserParam userParam) { return new ResponseEntity<>(userService.checkUser(userParam), HttpStatus.OK); } }
2.7 测试
三、总结
本文主要演示Lambda表达式在参数校验的特殊场景下的一个很有趣的应用,可读性,可拓展性更强。
给我们的启发是要灵活运用Java8提供的新的函数式类。
本文源码:https://github.com/chujianyun/lambdacheck
创作不易,如果觉得本文对你有帮助,欢迎点赞,欢迎关注我,如果有补充欢迎评论交流,我将努力创作更多更好的文章。