用切面定义一个自定义注解

730 阅读2分钟

这是我参与11月更文挑战的第7天,活动详情查看:2021最后一次更文挑战

前言

在实现自定义注解之前,我们要知道什么是注解,为什么要用注解。这两个问题。

什么是注解

注解就是某种注解类型的一个实例,我们可以用它在某个类上进行标注,这样编译器在编译我们的文件时,会根据我们自己设定的方法来编译类。

为什么要用注解

我们可以通过实际使用场景来解答这个问题。一款产品有许多属性,多人协同去配置这款产品,也就是update方法。但当配置的人多了,配置的次数多了,就会出现这样一个问题,就是想知道某个属性之前的值是多少,或者需要定位到配置这个属性值的人是谁?这就需要对每次操作都进行记录,要记录操作前的值、操作后的值、修改人、修改时间等。
这就可以用到注解了,定义个@OptRecord,注解到update方法上,然后再实现注解的逻辑,就可以实现了。这样做的好处,一方面减少了对update方法的代码侵入性,另一方面也减少了重复代码的编写,使代码更加简洁。

实现

定一个注解

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface OptRecord {
}

Target代表注解使用在哪里的,我们这是用在update方法上的,所以用的是METHOD
Retention代表注解的生命周期,因为需要在update方法前后用到,所以需要标记注解由jvm保留,运行时环境可以使用它。

实现注解逻辑

@Slf4j
@Aspect
@Component
public class OptRecordAspect {

    @Pointcut("@annotation(com.app.config.aspect.annotation.OptRecord)")
    public void execute() {
        log.info("OptRecord execute");
    }

    @Around(value = "execute()")
    public Object around(ProceedingJoinPoint point) {
        //todo 操作前逻辑
        //获取方法名
        String method = point.getSignature().getName();
        //获取方法参数
        Object arg = point.getArgs()[0];

        //业务操作
        Object o = null;
        try {
            o = point.proceed();
        } catch (Throwable throwable) {

        }

        //todo 操作前逻辑

        return o;
    }
}

定义一个切面类,@Aspect代表切面。@Pointcut说明该切面类的切点是注解OptRecord。这样读到OptRecord注解时,就会走OptRecordAspect类的execute方法。在用@Around注解环绕处理execute方法,这样就可以在around方法里具体实现我们自定义注解OptRecord的实现逻辑了。
通过参数point我们可以获取到被注解的方法的方法名、参数等信息。通过point.proceed()来控制何时去执行被注解的方法。这样,我们一个自定义的注解就实现完成了。