java手写aop_用spring Aop 和反射,手写牛p的验证框架

目前spring常用的验证框架有 JSR - 303 、Bean验证框架等等。但是这些验证框架都有一个毛病,就是使用困难,内聚性差,并且代码可控性低。

比如JSR - 303框架注解,@Null、@NotNull 等等,太过于繁琐,并且一个实体类中每个字段都要注解验证。但是我们常用的验证可能只有空值检查,或者适合我们项目的常用验证。并且自定义验证太过于麻烦,并且报错信息控制繁琐到让人难以忍受。所以本人按照个人理解和使用习惯,写了一个自定义验证框架,盼望各位看官多多指教。

首先写一个自定义注解

//自定义注解,用于放置需要验证的controller方法上

@Target(ElementType.METHOD)

@Retention(RetentionPolicy.RUNTIME)

public @interface BetterCheck {

CheckGroupEumn group() default CheckGroupEumn.NORMAL;

}

public enum CheckGroupEumn {

NORMAL(1, "普通组"),

UPDATE(2, "更新组")

;

}

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

这个自定义注解里面,包含了一个组枚举类,用于区分校验组,比如新建组,和修改组。这样可以应付多种情况下的检测,从而达到少写实体类的目的。

//检验注解,普通组和更新组

@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD})

@Retention(RetentionPolicy.RUNTIME)

public @interface NormalCheck {

CheckTypeEumn type() default CheckTypeEumn.NOT_CHECK;

boolean allGroup() default true;

long max() default 100;

long min() default 0;

}

@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD})

@Retention(RetentionPolicy.RUNTIME)

public @interface UpdateCheck {

CheckTypeEumn type() default CheckTypeEumn.NOT_CHECK;

long max() default 100;

long min() default 0;

}

public enum CheckTypeEumn {

NOT_CHECK(1, "不做检测"),

MOBILE(2, "手机"),

MONEY(3, "金钱"),

RANGE(4, "范围"),

NUll(5,"空值")

;

}

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

可以看到普通组中加入了 allGroup这个 boolean变量,这是为了可以让一个注解,同时在两个组都能进行验证。并且本验证模块,和本人写这个模块的最大原因就是,空值检查自动检查。因为几乎所有变量默认都是检查空值。。。(标题吹比框架....,,容我做次标题党 O(∩_∩)O哈哈)

基本工作准备完毕,下面就开始写切面类,对方法进行增强

public class ValidaAspect {

@Pointcut("@annotation(com.myvalida.anntion.BetterCheck)")

public void checkPoint() {

}

@Around("checkPoint()")

public Object arount(ProceedingJoinPoint point) throws Throwable {

//获取当前方法

Method method = ((MethodSignature) point.getSignature()).getMethod();

//获取当前注解的Group环境

BetterCheck betterCheck = method.getAnnotation(BetterCheck.class);

CheckGroupEumn group = betterCheck.group();

//获取所有的参数,和所有的参数变量

Object[] objects = point.getArgs();

Parameter[] parameters = method.getParameters();

//进入检验函数

check(objects, parameters, group);

return point.proceed(objects);

}

}

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

//首先对所有参数和变量逐个检查,检查函数用迭代,目前只嵌套2次

private void check(Object[] objs, Parameter[] paras, CheckGroupEumn group) throws Throwable {

for (int i = 0; i < objs.length; i++) {

Parameter para = paras[i];

Object obj = objs[i];

Annotation[] annotations = para.getAnnotations();

String paraName = para.getName();

doCheck(paraName, obj, annotations, group, 2);

}

}

//doCheck函数

private void doCheck(String paraName, Object obj, Annotation[] annotations, CheckGroupEumn group, int floor) throws Throwable {

if (annotations == null || annotations.length == 0) {

checkNotNull(paraName, obj, group, floor);

} else {

for (Annotation annotation : annotations) {

NormalCheck checkType = checkGroup(group, annotation);

if (checkType != null) {

switch (checkType.type()) {

case NOT_CHECK:

break;

case MONEY:

checkMoney(obj, checkType.type().getLabel());

break;

。。。。。。。。

}

} else {

checkNotNull(paraName, obj, group, 1);

}

}

}

}

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

当变量没有注解时候,就默认做空值检查。

private void checkNotNull(String paraName, Object obj, CheckGroupEumn group, int floor) throws Throwable {

if (ObjectUtils.isEmpty(obj)) {

throw new OperationNotAllowException(paraName + "值不能为空");

} else if (floor > 1) {

Class> clazz = obj.getClass();

Field[] fields = clazz.getDeclaredFields();

for (Field field : fields) {

field.setAccessible(true);

Annotation[] annotations = field.getAnnotations();

Object o = field.get(obj);

doCheck(field.getName(), o, annotations, group, floor - 1);

}

}

}

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

当对象不为空的时候,对实体类内部所有变量进行检查。只重复一次。

//检查分组功能,默认NormalCheck就包含了两个组的检测。

private NormalCheck checkGroup(CheckGroupEumn group, Annotation annotation) {

if (group.equals(CheckGroupEumn.NORMAL)) {

if (annotation instanceof NormalCheck) {

return (NormalCheck) annotation;

}

} else {

if (annotation instanceof UpdateCheck) {

return modelMapper.map(annotation, NormalCheck.class);

} else if (annotation instanceof NormalCheck && ((NormalCheck) annotation).allGroup()) {

return (NormalCheck) annotation;

}

}

return null;

}

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

检测方法举例

private void checkNull(String paraName, Object obj) throws Throwable {

if (!ObjectUtils.isEmpty(obj)) {

throw new OperationNotAllowException("不能传入" + paraName + ",值必须为空");

}

}

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

聊聊亮点:

1.  内聚性强,所有的检测方法都在一个类,有复杂方法,可以引入实现类。

2.  不用记多个注解,只要三个注解  @BetterCheck   @NormalCheck   @UpdateCheck

3.  异常可控性强,想怎么玩就怎么玩

4.  代码量少,如果默认非空检测,只要一个@BetterCheck,不会影响原来任何代码

5.  注解更清晰,枚举类说明,清晰介绍检查是干啥子的

6.  性能更强,少了反射调用方法的代码,性能更上n层楼

7.  轻量级结构,仅仅依赖于Spring

使用示范:

@ApiOperation(value = "添加组")

@BetterCheck

@GetMapping("testadd")

public void testAdd(

TestClass cmd

, @NormalCheck(type = CheckTypeEumn.RANGE, max = 13L, min = 11L)

Integer size

, String normal

) {

System.out.println("因为有了无奈");

}

@ApiOperation(value = "更新组")

@BetterCheck(group = CheckGroupEumn.UPDATE)

@GetMapping("testUpdate")

public void testUpdate(

TestClass cmd

, @UpdateCheck(type = CheckTypeEumn.NUll) String mustNull

, String normal

) {

System.out.println("这就是人生");

}

public class TestClass {

private String defaultCheck;

@NormalCheck(type = CheckTypeEumn.NUll,allGroup = false)

//UpdateCheck组中默认做空值检测

private Long id;

@NormalCheck(type = CheckTypeEumn.RANGE )

private Long range;

@NormalCheck(type = CheckTypeEumn.MOBILE)

private String mobile;

@NormalCheck(type = CheckTypeEumn.MONEY)

private BigDecimal money;

@NormalCheck

private String notCheck;

}

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

两个组,添加组和更新组,个人感觉还是比较清晰,简洁。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值