一、背景
之前针对参数校验提供了一个通用方案(见文末),但是新增一个校验代价比较大,需要修改多个类。
给出一个改进方案,新增校验时只需新增一个校验类即可,并且校验可以指定分组。
这样同样的一个组件,可以实现多个分组,指定分组条件可以让不同的方法只走指定分组的校验。
二、代码
2.1 项目结构
2.2 实体
package com.chujianyun.entity.param;
import lombok.Data;
@Data
public class UserParam {
/**
* 用户ID
*/
private Long userId;
/**
* 等级
*/
private Integer level;
}枚举
package com.chujianyun.entity.enums;
/**
* 校验分组枚举
*/
public enum UserValidateGroupEnum {
// 所有条件都校验
ALL,
// 某种条件才校验
SOME
}结果类
package com.chujianyun.entity.dto;
import lombok.Data;
import java.util.List;
@Data
public class UserDTO {
private String message;
public UserDTO() {
}
public UserDTO(String message) {
this.message = message;
}
}2.3 校验抽象类
package com.chujianyun.component;
import lombok.Data;
import java.util.Set;
@Data
public abstract class Validator<P> {
/**
* 校验分组,枚举
*/
private Set<Enum> groups;
/**
* 验证参数
*/
abstract void validate(P param);
}2.4 具体校验类
等级校验
package com.chujianyun.component;
import com.chujianyun.entity.param.UserParam;
import com.chujianyun.exception.BusinessException;
import org.apache.commons.lang3.RandomUtils;
import org.springframework.stereotype.Component;
@Component
public class UserLevelValidator extends Validator<UserParam> {
@Override
public void validate(UserParam param) {
System.out.println("验证等级");
if (param == null) {
throw new BusinessException("");
}
// 根据userId查询等级
boolean isBiggerThan50 = RandomUtils.nextBoolean();
if (!isBiggerThan50) {
throw new BusinessException("低于50级");
}
}
}性别校验
package com.chujianyun.component;
import com.chujianyun.entity.param.UserParam;
import com.chujianyun.exception.BusinessException;
import org.apache.commons.lang3.RandomUtils;
import org.springframework.stereotype.Component;
@Component
public class UserSexValidator extends Validator<UserParam> {
@Override
void validate(UserParam param) {
System.out.println("验证性别");
if (param == null) {
throw new BusinessException("");
}
// 模拟服务,根据userId查询性别
boolean isFemale = RandomUtils.nextBoolean();
if (!isFemale) {
throw new BusinessException("仅限女性玩家哦!");
}
}
}某个特殊类型校验
package com.chujianyun.component;
import com.chujianyun.entity.enums.UserValidateGroupEnum;
import com.chujianyun.entity.param.UserParam;
import com.chujianyun.exception.BusinessException;
import org.apache.commons.collections4.SetUtils;
import org.apache.commons.lang3.RandomUtils;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.HashSet;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* 特殊情况才校验
*/
@Component
public class UserSomeValidator extends Validator<UserParam> {
@PostConstruct
public void init() {
setGroups(Stream.of(UserValidateGroupEnum.SOME).collect(Collectors.toSet()));
}
@Override
void validate(UserParam param) {
System.out.println("仅检测Some类型的");
if (param == null) {
throw new BusinessException("");
}
// 模拟服务,根据userId某个条件
boolean isSome = RandomUtils.nextBoolean();
if (!isSome) {
throw new BusinessException("某种条件不满足哦!");
}
}
}2.5 校验链
package com.chujianyun.component;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.ResolvableType;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@Component
public class ValidatorChain implements ApplicationContextAware {
private Map<Class, List<Validator>> validatorMap = new HashMap<>();
/**
* 参数校验
*/
public <P> void checkParam(P param) {
checkParam(param, validator -> true);
}
/**
* 符合某种条件才参数校验
*/
public <P> void checkParam(P param, Predicate<Validator> predicate) {
List<Validator> validators = getValidators(param.getClass());
if (CollectionUtils.isNotEmpty(validators)) {
validators.stream()
.filter(predicate)
.forEach(validator -> validator.validate(param));
}
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
Map<String, Validator> beansOfType = applicationContext.getBeansOfType(Validator.class);
this.validatorMap = beansOfType.values().stream().collect(Collectors.groupingBy(validator -> getParamType(validator.getClass())));
}
/**
* 查找相关的所有校验器
*/
private List<Validator> getValidators(Class clazz) {
return validatorMap.get(clazz);
}
/**
* 解析泛型待校验参数类型
*/
private Class getParamType(Class clazz) {
ResolvableType resolvableType = ResolvableType.forClass(clazz);
return resolvableType.getSuperType().getGeneric(0).resolve();
}
}2.6 服务层
package com.chujianyun.service;
import com.chujianyun.entity.dto.UserDTO;
import com.chujianyun.entity.param.UserParam;
public interface UserService {
UserDTO checkUser(UserParam userParam);
UserDTO checkUserSome(UserParam userParam);
}服务实现
package com.chujianyun.service.impl;
import com.chujianyun.component.ValidatorChain;
import com.chujianyun.entity.dto.UserDTO;
import com.chujianyun.entity.enums.UserValidateGroupEnum;
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 ValidatorChain validatorChain;
@Override
public UserDTO checkUser(UserParam userParam) {
// 参数校验
validatorChain.checkParam(userParam);
// 业务逻辑
return new UserDTO("测试");
}
@Override
public UserDTO checkUserSome(UserParam userParam) {
// 参数校验(只校验类型为Some的)
validatorChain
.checkParam(userParam, param -> param.getGroups()
.contains(UserValidateGroupEnum.SOME));
// 业务逻辑
return new UserDTO("测试");
}
}还可以设置除了分组之外的其他条件,满足条件才会校验。
2.7 控制层
package com.chujianyun.web;
import com.chujianyun.entity.dto.UserDTO;
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 org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
@RestController
@RequestMapping("/user")
public class HelloController {
@Resource
private UserService userService;
@PostMapping("/check")
public ResponseEntity<UserDTO> checkUser(UserParam userParam) {
return new ResponseEntity<>(userService.checkUser(userParam), HttpStatus.OK);
}
@PostMapping("/checkSome")
public ResponseEntity<UserDTO> checkUserSome(UserParam userParam) {
return new ResponseEntity<>(userService.checkUserSome(userParam), HttpStatus.OK);
}
}2.8 异常处理
package com.chujianyun.web;
import com.chujianyun.exception.BusinessException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
@ControllerAdvice
public class MyExceptionHandler {
@ExceptionHandler(value = Exception.class)
public ResponseEntity handle(Exception e) {
if (e instanceof BusinessException) {
return new ResponseEntity<>("[业务异常]" + e.getMessage(), HttpStatus.BAD_REQUEST);
}
return new ResponseEntity<>("[系统异常]" + e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
}
}三、测试
直接执行check校验所有的校验器(下面只给出其中一种情况的截图)
checkSome则只会校验指定的条件
四、总结
设计模式里有一个开闭原则即“对拓展开放,对修改关闭”,本例就是一个实践。
不过还有很多可以改进的地方,大家可以考虑使用接口来实现,欢迎大家一起完善,欢迎提PR。
大家在平时开发时,要多尝试将设计模式使用到项目中,来提高代码的可维护性,灵活性。
源码地址:https://github.com/chujianyun/checkparam
之前的一个通用方案:https://blog.csdn.net/w605283073/article/details/95680896
點擊查看更多內容
2人點贊
評論
評論
共同學習,寫下你的評論
評論加載中...
作者其他優質文章
正在加載中
感謝您的支持,我會繼續努力的~
掃碼打賞,你說多少就多少
贊賞金額會直接到老師賬戶
支付方式
打開微信掃一掃,即可進行掃碼打賞哦


