使用AOP实现参数验证

今天,我们使用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());
    }

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

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring AOPJava代码实现主要包括以下几个步骤: 1. 将业务逻辑类和切面类都加入到容器中,告诉Spring哪个类是切面类。在配置类中使用`@Bean`注解将业务逻辑类和切面类分别注入容器中。其中,业务逻辑类使用`MathCalculator`作为示例,切面类使用`AopLogAspect`作为示例。 2. 在切面类的每个通知方法上标注通知注解,告诉Spring何时何地运行。切面类中的通知方法使用注解标注,例如`@Before`、`@After`等,来指定在目标方法执行前、执行后等时机执行切面逻辑。通知方法所在的切面类使用`@Aspect`注解标注,以告诉Spring这是一个切面类。 3. 开启基于注解的AOP模式。在配置类上使用`@EnableAspectJAutoProxy`注解开启基于注解的AOP模式。这样Spring就会自动扫描切面类并将其应用到相应的目标方法上。 4. 编写测试类来验证AOP的功能。测试类使用`AnnotationConfigApplicationContext`来创建一个基于注解的应用上下文,并将配置类`ApiLogOfAop`作为参数传入。然后从应用上下文中获取`MathCalculator`的实例,并调用其方法进行测试。 以上是Spring AOPJava代码实现的主要步骤,通过将业务逻辑类和切面类加入容器、标注通知注解以及开启基于注解的AOP模式,可以实现对目标方法的切面逻辑的注入和执行。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [理解Spring AOP实现与思想 案例代码](https://download.csdn.net/download/qq_37171817/12502814)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [Spring AOP使用完整代码实现 (日志记录)-- 完整代码](https://blog.csdn.net/qq_41219586/article/details/122826601)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值