0. 定义:
“Java的反射(reflection)机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。” —— 百度百科
1.起因
在日常开发过程中,后端同学通常是完成功能性需求后稍微自测下就一把梭(懂事的会有个接口文档,不懂事的直接聊天记录奉上),这个前提是你自己在调用自己定义的接口,知道要传什么,不传怎么?那对于其它同学来说,我TMD怎么知道,反正我调用就是出错了,然后就是你来我往的心里作用
2.诉求
对于调用者(不管是前端还是测试同学),要求很简单,告诉我哪儿出错了,而不是系统层面抛出一个异常,比如你用了@Valid或@Validated注解会仍出一个系统参数异常,这种情况提示非常不具备可阅读性也不友好
3.主题
为节约前端同学对接口mock或在调用时更明确的知道后端同学的接口定义,特增加该逻辑,旨在让每一次请求、响应都更有可阅读性,提高团队人效
3.1 自定义注解
/**
* @author siri
*/
public ParamValid {
}
3.2实现切面逻辑
public class ParamValidAspect {
private static final Logger logger = LoggerFactory.getLogger(ParamValidAspect.class);
)
public void ValidParam(JoinPoint joinPoint) throws IllegalAccessException {
String method = joinPoint.getSignature().getDeclaringTypeName() + <span data-raw-text="" "="" data-textnode-index-1685352259513="53" data-index-1685352259513="958" data-textnode-notemoji-index-1685352259513="958" class="character">".<span data-raw-text="" "="" data-textnode-index-1685352259513="53" data-index-1685352259513="960" data-textnode-notemoji-index-1685352259513="960" class="character">" + joinPoint.getSignature().getName();
Object[] args = joinPoint.getArgs();
if(null != args && args.length>0){
for (Object arg : args) {
Field[] fields = arg.getClass().getDeclaredFields();
for (Field field : fields) {
field.setAccessible(Boolean.TRUE);
final Object val = field.get(arg);
//概率上来说把较大可能的放在上面,避免浪费时间
//字符串
final NotBlank notBlank = field.getAnnotation(NotBlank.class);
if(null != notBlank && Objects.isNull(val)){
String msg = field.getName()+<span data-raw-text="" "="" data-textnode-index-1685352259513="97" data-index-1685352259513="1610" data-textnode-notemoji-index-1685352259513="1610" class="character">":<span data-raw-text="" "="" data-textnode-index-1685352259513="97" data-index-1685352259513="1612" data-textnode-notemoji-index-1685352259513="1612" class="character">"+notBlank.message();
logger.error(<span data-raw-text="" "="" data-textnode-index-1685352259513="100" data-index-1685352259513="1670" data-textnode-notemoji-index-1685352259513="1670" class="character">"当前请求方法:{}携带的参数:{}无效<span data-raw-text="" "="" data-textnode-index-1685352259513="100" data-index-1685352259513="1690" data-textnode-notemoji-index-1685352259513="1690" class="character">",method,msg);
throw new BizException(HttpStatus.BAD_REQUEST.value(), msg);
}
//基本类型的封装类上
final NotNull notNull = field.getAnnotation(NotNull.class);
if(null != notNull && Objects.isNull(val)){
String msg = field.getName()+<span data-raw-text="" "="" data-textnode-index-1685352259513="121" data-index-1685352259513="2035" data-textnode-notemoji-index-1685352259513="2035" class="character">":<span data-raw-text="" "="" data-textnode-index-1685352259513="121" data-index-1685352259513="2037" data-textnode-notemoji-index-1685352259513="2037" class="character">"+notNull.message();
logger.error(<span data-raw-text="" "="" data-textnode-index-1685352259513="124" data-index-1685352259513="2094" data-textnode-notemoji-index-1685352259513="2094" class="character">"当前请求方法:{}携带的参数:{}无效<span data-raw-text="" "="" data-textnode-index-1685352259513="124" data-index-1685352259513="2114" data-textnode-notemoji-index-1685352259513="2114" class="character">",method,msg);
throw new BizException(HttpStatus.BAD_REQUEST.value(), msg);
}
//常作用在集合上
final NotEmpty notEmpty = field.getAnnotation(NotEmpty.class);
if(null != notEmpty && Objects.isNull(val)){
String msg = field.getName()+<span data-raw-text="" "="" data-textnode-index-1685352259513="145" data-index-1685352259513="2461" data-textnode-notemoji-index-1685352259513="2461" class="character">":<span data-raw-text="" "="" data-textnode-index-1685352259513="145" data-index-1685352259513="2463" data-textnode-notemoji-index-1685352259513="2463" class="character">"+notEmpty.message();
logger.error(<span data-raw-text="" "="" data-textnode-index-1685352259513="148" data-index-1685352259513="2521" data-textnode-notemoji-index-1685352259513="2521" class="character">"当前请求方法:{}携带的参数:{}无效<span data-raw-text="" "="" data-textnode-index-1685352259513="148" data-index-1685352259513="2541" data-textnode-notemoji-index-1685352259513="2541" class="character">",method,msg);
throw new BizException(HttpStatus.BAD_REQUEST.value(), msg);
}
}
}
}
}
}
3.3 在需要的方法中增加限定约束,其中@ParamValid和BindingResult 是必须的,@Validated 可换成 @Valid
)
public Result<OrderSaveVo> save( OrderSaveDto orderSaveDto, BindingResult bindingResult) {
OrderSaveVo orderSaveVo = orderService.saveOrder(orderSaveDto);
return Result.success(orderSaveVo);
}
3.4定义入参数限制规则
public class OrderSaveDto implements Serializable {
/**
* 产品id
*/
(message = <span data-raw-text="" "="" data-textnode-index-1685352259513="187" data-index-1685352259513="3127" data-textnode-notemoji-index-1685352259513="3127" class="character">"产品id不能为空<span data-raw-text="" "="" data-textnode-index-1685352259513="187" data-index-1685352259513="3136" data-textnode-notemoji-index-1685352259513="3136" class="character">")
private Long itemId;
/**
* 产品总数量
*/
(message = <span data-raw-text="" "="" data-textnode-index-1685352259513="199" data-index-1685352259513="3211" data-textnode-notemoji-index-1685352259513="3211" class="character">"产品总数量不能为空<span data-raw-text="" "="" data-textnode-index-1685352259513="199" data-index-1685352259513="3221" data-textnode-notemoji-index-1685352259513="3221" class="character">")
private Integer itemTotalCount;
/**
* 支付回调通知地址
*/
private String notifyUrl;
}
3.5 自测校验
通过以上的方式,调用者可以清晰的知道接口调用时哪个字段出了问题。避免来回的问浪费团队时间;其中3.2可以根据自己的实际业务场景进行扩展,祝君好运
P.S:我不是在讲反射,是在与大家一块儿分享工作中解决问题的方式,你可以用来作参数校验、也可以用来作登录校验、也可以用来作敏感词过滤,不要受限于我的Demo
反射有很多API记不住???其实你只需要在反射视觉下记住:
java.lang.Class:代表一个类
java.lang.reflect.Method:代表一个类下的方法
java.lang.reflect.Field:代表一个类的成员变量
java.lang.reflect.Constructor:代表一个类的构造器
如有任何问题,欢迎指正