如何实现自定义注解校验参数?(aop)

目录

一、实现目的

二、实现原理

三、代码详情

1.自定义注解

2.切面类

3.工具类(判断逻辑)

4.全局异常拦截

【1】自定义异常

【2】全局异常拦截

5.注解使用

【1】controller

【2】实体类

6.返回效果


一、实现目的

在编写接口的时候,通常会先对参数进行一次校验,这样业务逻辑代码就略显冗杂,如果可以把校验参数的代码进行统一管理,在方法或者属性上直接添加注解就可以实现参数的校验,就可以提升代码编写的效率。

二、实现原理

通过自定义注解给定参数需要满足的条件,然后通过面向切面编程的相关知识进行解析,对比参数和条件,抛出异常统一处理返回。

三、代码详情

1.自定义注解

(1)先定义一个注解用于aop识别切点,参数根据实际情况自定义

@Target({ElementType.PARAMETER,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PCheck {
    boolean open() default true;
}

(2)然后定义一个注解用于写入对应条件(这里是对字符串的解析)

        min() : 参数最小长度

        max():参数最大长度

        regex():正则表达式

        info(): 参数名称

        ifNull():是否允许为空

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface StrVal {
    int min() default 0;
    int max() default 25;
    String regex() default "";
    String info() default "参数";
    //默认是不允许为null的
    boolean ifNull() default false;
}

2.切面类

@Aspect
@Component
public class ParamsCheckAspect {


    /***
     * 定义Pcheck使用的方法为切入点
     * @param: []
     * @return: void
     * @author: kevin
     * @date: 2023/5/10 9:56
     */
    @Pointcut("@annotation(com.xxx.PCheck)") // 定义切点
    public void pointcut() {}

    /***
     * 生命周期 为方法执行前
     * @param: [joinPoint]
     * @return: void
     * @author: kevin
     * @date: 2023/5/10 9:56
     */
    @Before("pointcut()")
    public void before(JoinPoint joinPoint) throws Exception {
        // 获取方法参数
        Object[] args = joinPoint.getArgs(); 
        for (Object arg : args) {
            // 对参数进行校验
            PcheckUtil.validate(arg); 
        }
    }

}

3.工具类(判断逻辑)

@Component
public class PcheckUtil {

    /***
     * 校验传过来的参数
     * @param: [arg]
     * @return: void
     * @author: kevin
     * @date: 2023/5/10 9:58
     */
    public static void validate(Object arg) throws IllegalAccessException {
        // 获取对象中所有的字段和方法
        Field[] fields = arg.getClass().getDeclaredFields();
        for (Field field : fields) {
            if (field.isAnnotationPresent(StrVal.class)){
                //存在某个注解就进行对应的处理
                StrVal annotation = field.getAnnotation(StrVal.class);
                String regex = annotation.regex();
                int max = annotation.max();
                int min = annotation.min();
                String info = annotation.info();
                boolean ifNull = annotation.ifNull();
                //设置属性可见性
                field.setAccessible(true);
                String value = (String) field.get(arg);
                //先判断空
                //如果可以为空 值也为空就不进行处理
                if (ifNull && StringUtils.isBlank(value)){
                    continue;
                }
                if (!ifNull && StringUtils.isBlank(value)){
                    throw new ParamsException(info+":不能为空!");
                }
                //如果正则不匹配就直接返回
                if (StringUtils.isNotBlank(regex)){
                    if (!value.matches(regex)){
                        throw new ParamsException(info+":格式不正确!");
                    }
                }
                //最后判断长度
                if (value.length()<min || value.length()>max){
                    throw new ParamsException(info+":请将字符长度控制在"+min+"~"+max+"之间");
                }
            }
        }
    }
}

4.全局异常拦截

【1】自定义异常

@EqualsAndHashCode(callSuper = true)
@AllArgsConstructor
@NoArgsConstructor
@Data
public class ParamsException extends RuntimeException {
    private static final long serialVersionUID = 7060237606941777850L;
    private String message; // 异常信息

    @Override
    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}

【2】全局异常拦截

注:Result类是我自己封装的,可以自己写Map或者实体类返回

@ControllerAdvice
public class GlobalExceptionHandler {
    /***
     * 拦截参数错误返回
     * @param: [e]
     * @return: com.atomee.rbacdemo.common.Result
     * @author: kevin
     * @date: 2023/5/10 10:42
     */
    @ExceptionHandler(ParamsException.class)
    @ResponseBody
    public Result handleMyException(ParamsException e) {
        return Result.getResult(411,e.getMessage(),null);
    }
}

5.注解使用

【1】controller

    @PCheck
    @PostMapping("/xxxx/cooks/add")
    public Result addCooks(@RequestBody AddCooksParams addCooksParams){
        return cooksService.addCooks(addCooksParams);
    }

【2】实体类

注:我使用了lambok

@Data
@NoArgsConstructor
@AllArgsConstructor
public class AddCooksParams implements Serializable {
    private static final long serialVersionUID = 2145635852726787978L;
    @StrVal(info = "菜品名称",max = 26)
    private String name;
    private String src;
    @StrVal(info = "菜品详情",max = 250)
    private String detail;
}

6.返回效果

 

  • 4
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
首先,我们需要定义一个自定义注解 `@RequiresPermissions`,用于标识需要授权访问的方法,例如: ```java @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface RequiresPermissions { String[] value(); // 权限 } ``` 然后,我们需要实现一个切面,用于拦截被 `@RequiresPermissions` 标识的方法,并进行权限校验,例如: ```java @Component @Aspect public class PermissionCheckAspect { @Autowired private AuthService authService; @Around("@annotation(requiresPermissions)") public Object checkPermission(ProceedingJoinPoint joinPoint, RequiresPermissions requiresPermissions) throws Throwable { // 获取当前用户 User user = authService.getCurrentUser(); if (user == null) { throw new UnauthorizedException("用户未登录"); } // 获取当前用户的权限列表 List<String> permissions = authService.getUserPermissions(user); // 校验权限 for (String permission : requiresPermissions.value()) { if (!permissions.contains(permission)) { throw new ForbiddenException("没有访问权限:" + permission); } } // 执行目标方法 return joinPoint.proceed(); } } ``` 在切面中,我们首先通过 `AuthService` 获取当前用户及其权限列表,然后校验当前用户是否拥有被 `@RequiresPermissions` 标识的方法所需的所有权限,如果没有则抛出 `ForbiddenException` 异常,如果有则继续执行目标方法。 最后,我们需要在 Spring 配置文件中启用 AOP 自动代理,并扫描切面所在的包,例如: ```xml <aop:aspectj-autoproxy /> <context:component-scan base-package="com.example.aspect" /> ``` 这样,我们就通过 Spring AOP自定义注解模拟实现了类似 Shiro 权限校验的功能。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值