自定义注解 AOP实现 (写给自己看)

首先需要知道五个元注解,(主要用到的是基本的四个,后面新增了一个但不常用),自己网上查都有教程

先来说一下自定义注解的实现思路 (动态代理情况)

首先自定义一个注解,一般默认value为参数,想要多参数的话,自己编写参数,默认都是缺省default范围,下面是我自己写的一个权限校验的自定义注解demo

/**
 * @author 申恒基
 * 创建时间 2023-07-20 10:44
 * 描述:自定义权限校验注解
 */
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface AllowUserRole {
    RoleEnum[] value() default {};
}
​

这样我们在一个方法/类/...(看@Target的限制)上就可以使用我们的注解了

自定义注解肯定是要实现一些我们想要的功能的,比如权限校验,参数注入等等。那我们怎样才能达到我们想要的这种效果呢,思路也很简单,就是在执行我们注解标记的方法之前,先执行我们自己写的方法,在自己写的方法中,我们编写对当前标记注解的类/方法执行前需要操作的逻辑即可,那这要怎么写呢,目前实现的方法有两种:

1.AspectJ ---- SpringAOP

2.Interceptor ---- 拦截器

1. AspectJ ---- SpringAOP

(以下内容需掌握SpringAOP能帮助更好的理解,如果阅读吃力可以先去复习一下SpringAOP的相关知识点再来继续阅读)

引入依赖

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>6.0.9</version>
</dependency>

先介绍一下aop的核心功能,也是决定注解是否生效的决定因素,切点!

aop的 环绕通知 -- around 可以让我们手动决定注解标记的方法是否需要执行,其中常用的切点的定义如下:

    @Around("@annotation(com.example.learn.common.utils.annotion.AllowUserRole)")
    @Around("@within(com.example.learn.common.utils.annotion.AllowUserRole)")

关键区别在于@annotation和@within,@annotation注解的意思是该注解标记的方法前都会进入aop,而@within注解的意思是标记在类上的所有方法执行时都会进入aop,如果出现作用在某一块注解不生效的情况,请检查该位置的注解类型是否正确

aop目前支持的切入点指示符如下:

execution: 用于匹配方法执行的连接点;

within: 用于匹配指定类型内的方法执行;

this:用于匹配当前AOP代理对象类型的执行方法;注意是AOP代理对象的类型匹配,这样就可能包括引入接口也类型匹配;

target:用于匹配当前目标对象类型的执行方法;注意是目标对象的类型匹配,这样就不包括引入接口也类型匹配;

args: 用于匹配当前执行的方法传入的参数为指定类型的执行方法;

@within: 用于匹配所以持有指定注解类型内的方法;

@target: 用于匹配当前目标对象类型的执行方法,其中目标对象持有指定的注解:

@args: 用于匹配当前执行的方法传入的参数持有指定注解的执行;

@annotation: 用于匹配当前执行方法持有指定注解的方法;

bean: Spring AOP扩展的,AspectJ没有对于指示符,用于匹配特定名称的Bean对象的执行方法reference pointcut: 表示引用其他命名切入点,只有@ApectJ风格支持,Schema风格不支持。

介绍完AOP后接下来我们说说注解对应的方法要怎么写,非常简单

先看代码:

​
/**
 * @author 申恒基
 * 创建时间 2023-07-20 11:35
 * 描述:自定义权限校验注解
 */
@Aspect
@Component
public class AllowUserRoleAspect {
​
    /**
     * 对类上标记的权限注解进行校验
     */
    @Around("@within(com.example.learn.common.utils.annotion.AllowUserRole)")
    public Object allowUserRoleInClass(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        AllowUserRole annotationMethod = signature.getMethod().getAnnotation(AllowUserRole.class);
        //如果方法上该注解存在,就放行,给校验方法的aop自己校验
        if(annotationMethod == null){
            AllowUserRole annotation = joinPoint.getTarget().getClass().getAnnotation(AllowUserRole.class);
            if (annotation != null && roleIsNull(annotation.value())) {
                throw new CustomException(ExceptionEnum.INSUFFICIENT_PRIVILEGE_ERROR);
            }
        }
        return joinPoint.proceed();
    }
​
    /**
     * 对方法上标记的权限注解进行校验
     */
    @Around("@annotation(com.example.learn.common.utils.annotion.AllowUserRole)")
    public Object allowUserRoleInMethod(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        //获取类注解标注的方法
        Method method = methodSignature.getMethod();
        AllowUserRole annotation = method.getAnnotation(AllowUserRole.class);
        if (annotation == null) {
            return joinPoint.proceed();
        }
        if (roleIsNull(annotation.value())) {
            throw new CustomException(ExceptionEnum.INSUFFICIENT_PRIVILEGE_ERROR);
        }
        return joinPoint.proceed();
    }
​
    /**
     * 检查权限存在,不存在返回true,存在返回false
     *
     * @param roleEnums 权限枚举类
     * @return ture/false
     */
    private boolean roleIsNull(RoleEnum[] roleEnums) {
        for (RoleEnum role : roleEnums) {
            if (Objects.equals(RoleEnum.matchOf(UserUtils.getRole()), role)) {
                return false;
            }
        }
        return true;
    }
}

核心在这两句:

//获取方法标签 
 MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
//获取类注解标注的方法
Method method = methodSignature.getMethod();
//获取指定的注解
AllowUserRole annotation = method.getAnnotation(AllowUserRole.class);

既然能获得注解,那也就可以获得注解的参数了

//获取注解当中的参数
annotation.value();

如果想要获得类上的注解,得这么写

//getTarget()是获取目标类,其他就不用我解释了吧
AllowUserRole annotation = joinPoint.getTarget().getClass().getAnnotation(AllowUserRole.class);

简单吧,既然获得了数据就可以写我们自己的业务了,写完打上注解就能自动生效啦,如果没有生效可以看一下我上面写的注解未生效的原因,@annotation和@within,如果想获得方法传入的参数啥的等等,就百度一下,看aop的ProceedingJoinPoint的API,基本上都可以搜到。

因为我用到了枚举类这里给大家放一下我枚举类的代码:

/**
 * @author 申恒基
 * 创建时间 2023-07-19 13:18
 * 描述:角色枚举类
 */
@Getter
@AllArgsConstructor
public enum RoleEnum {
    /*
    系统角色
     */
    ADMIN("ROLE_admin"),
​
    TEACHER("ROLE_teacher"),
​
    STUDENT("ROLE_student");
​
    final String role;
​
    public static RoleEnum matchOf(String role){
        for (RoleEnum value : RoleEnum.values()) {
            if (StringUtils.equals(value.getRole(),role)){
                return value;
            }
        }
        return null;
    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

树亘

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值