继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

使用AOP实现参数验证

0xcaffebabe
关注TA
已关注
手记 43
粉丝 10
获赞 21

今天,我们使用AOP来开发一个参数验证。

首先,我们理理这个参数验证的原理以及流程。

第一步,我们会定义一个注解,这个注解可以被用来修饰某一方法的参数,如下:

public Object login(@RequestBody @CustomValid LoginDto loginDto, HttpSession session){
       return loginService.login(loginDto,session);
   }

这里的@CustomValid就是我们的自定义注解。

 它的代码如下:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface CustomValid {
}

第二步,我们会在切入点的环绕通知中获取所有的方法参数:

//获取所有的方法参数
       Signature signature = joinPoint.getSignature();
       MethodSignature methodSignature = (MethodSignature)signature;
       Method targetMethod = methodSignature.getMethod();

       Parameter[] parameters=targetMethod.getParameters();

通过这一句代码来获取需要验证的方法参数类型:

List<Class> list=validationProcessor.getValidateClass(parameters);

其内部实现:

public List<Class> getValidateClass(Parameter[] parameters){

       List<Class> list=new ArrayList<Class>();

       for (Parameter parameter : parameters) {
           Annotation[] annotations=parameter.getAnnotations();
           for (Annotation annotation : annotations) {
               if(annotation instanceof CustomValid){
                   list.add(parameter.getType());
               }
           }
       }
       return list;
   }

原理就是通过反射获取参数数组中各个参数上的注解,并判断是否有@CustomValid这个注解,如果有就加入列表,最终返回。

第三步,获取到需要验证的参数类型之后,我们进行双重循环,找出需要进行参数验证的参数对象:

for (Object arg : joinPoint.getArgs()) {
           for (Class aClass : list) {
               //将参数与需要验证的参数类型进行匹配
               if(arg.getClass().getName().equals(aClass.getName())){
                   try {
                       //如果验证失败
                       ValidResult validResult=validationProcessor.valid(arg.getClass().getDeclaredFields(),arg);
                       if(!validResult.isValid()){
                           return new Result<String>(ResultState.ERROR,validResult.getValidFailMessage());
                       }
                   } catch (IllegalAccessException e) {
                       e.printStackTrace();
                   }
               }
           }
       }

这里validationProcessor有一个valid方法,该方法通过参数对象的所有成员变量,并取出每个成员变量上的注解,进一步进行验证,代码如下:

public ValidResult valid(Field[] fields, Object target) throws IllegalAccessException {

       for (Field field : fields) {
           //获取每个成员的注解
           Annotation[] annotations=field.getDeclaredAnnotations();

//            判断注解是否在validMapping里面
           for (Annotation annotation : annotations) {
               ValidInterface validInterface=validMapping.get(annotation.annotationType());
               //如果validMapping拥有相关实现
               if(validInterface!=null){
                   field.setAccessible(true);
                   //如果这个成员变量注解验证失败
                   if(! validInterface.valid(field.get(target))){
                       return new ValidResult(false,validInterface.getErrorMessage(field.getName()));
                   }
               }
           }

       }

       return new ValidResult(true);
   }

这里面有一个validMapping对象,该map维护验证注解与内部验证类的映射关系:

private HashMap<Class<? extends Annotation>,ValidInterface> validMapping=new HashMap<>();

   {
       //注册验证注解实现
       validMapping.put(CustomNotNull.class, new CustomNotNull.Validation());
       validMapping.put(CustomEmail.class,new CustomEmail.validation());
   }

其中,每一个验证注解都会实现一个ValidInterface接口,代码如下:

public interface ValidInterface {

    boolean valid(Object object);

    String getErrorMessage(String fieldName);
}

当验证器进行验证的话,valid方法会被调用,如果验证通过,返回true 反之false。

getErrorMessage 则是返回验证失败后的提示信息。 

这是ValidResult的代码:

public class ValidResult {

   private boolean isValid;

   private String validFailMessage;

   public ValidResult(boolean isValid,String validFailMessage){
       this.isValid=isValid;
       this.validFailMessage=validFailMessage;
   }

   public ValidResult(boolean isValid){
       this.isValid=isValid;
   }
}

其主要职责就是存放验证结果。

这样,一个完整的参数验证流程就完成了,让我们来总结一下:

1.循环判断参数是否含有@CustomValid注解,若有则进行下一步,否则退出。

2.取出含有@CustomValid注解的参数对象,取出其所有的成员变量。

3.循环获得每个成员变量上的注解,若在验证处理器内部拥有注册,则调用这个验证注解提供的验证接口。

4.返回验证结果,让切入点的环绕通知决定下一步结果。


不完美的地方:

在这里,我们通过手工的方式进行了验证接口的注册:

private HashMap<Class<? extends Annotation>,ValidInterface> validMapping=new HashMap<>();

   {
       //注册验证注解实现
       validMapping.put(CustomNotNull.class, new CustomNotNull.Validation());
       validMapping.put(CustomEmail.class,new CustomEmail.validation());
   }

这个做法在我看来,是不够完美的,我想是否可以运用多态或者继承,来实现验证注解与验证处理接口的联系?而非这样进行手工注册。

这里是一个需要思考的地方。


打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP