alreadyspringboot收藏

基于SpringBoot AOP实现自定义注解实践

2021-12-15  本文已影响0人  马小莫QAQ

在编写代码,spring和Java自带注解不能满足使用需要时,可以通过自定义注解进行实践。自定义注解可以通过多种方式实现,在Spring中常见的有通过定义 AOP 的PointCut切点进行扫描并进行注解的解析。也可以通过BeanPostProccess等Bean初始化时的钩子方法对Bean进行扫描和注解解析。

1、定义一个注解类HunterLogAnno

/**
 *  定义一个普通注解
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface HunterLogAnno {
    String value() default "";
}

2、首先通过使用AOP对注解进行解析

import com.example.annotations.HunterLogAnno;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * 使用 Aspect 实现注解,别忘记添加 @Component 注解让Aspect注解生效
 */
@Component
@Aspect
public class HunterAspect {

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    /**
     *  这里定义一个PointCut,用于描述满足条件的方法
     */
    @Pointcut("execution(public * com.example.Services..*(*))")
    private void normalPointCut() {
        System.out.println("pointCut");
    }

    /** 将Services 文件下,且带有 HunterLogAnno 注解的类进行
     *
     * @param joinPoint
     */
    @Around("normalPointCut() && @annotation(com.example.annotations.HunterLogAnno)")
    private void normalPointAround(ProceedingJoinPoint joinPoint) throws Throwable {
        logger.info("===========================");
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        // 执行具体的方法,在使用 Around 类的时候,需要对执行方法进行调用,否则只会执行 Around 方法 normalPointBefore 里面的逻辑
        joinPoint.proceed();
        // 注解解析
        HunterLogAnno hunterLogAnno = method.getAnnotation(HunterLogAnno.class);
        String value = hunterLogAnno.value();
        // 打印注解记录
        logger.info(value);
        logger.info("===========================");
    }

}

上边定义了一个切点和一个环绕处理方法,通过定义 @PointCut 和 @annotation 来对注解解析。这里有个容易忽视的点,使用 @Around 环绕织入时需要JointPoint 的proccess方法执行原方法 (未被代理时) ,否则只会执行上图中的 normalPointAround() 方法的流程,稍后可以利用这个特性实现方法的异步执行。在这里,使用@Around注解,获取被代理方法的属性,并输出log日志,其中 MethodSignature 类可以获取方法的名称,输入参数等,可以通过ProceedingJoinPoint 类获取该类的各种属性。

接下来,实现一个Service类,并定义value的值,启动SpringBoot并通过Controller接收请求。

import com.example.annotations.HunterAsyncAnno;
import com.example.annotations.HunterLogAnno;
import org.springframework.stereotype.Service;

@Service
public class HunterServiceImpl {

    @HunterLogAnno(value = "Integra")
    public void getTheLogs(String name) {
        System.out.println(name);
    }

    @HunterAsyncAnno
    public void asyncSystemOut(String name) {
        System.out.println("This text loggout in 5s!!");
    }

}

3、使用自定义注解实现方法的异步执行

首先自定义一个异步处理的注解@HunterAsyncAnno

import java.lang.annotation.*;

/**
 * 异步执行方法的注解
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface HunterAsyncAnno {
    // 核心线程数
    int coreSize() default 1;

    // 最大核心线程数
    int maxSize() default 1;

}

在Aspect中定义一个注解的解析,对标注了@HunterAsyncAnno的方法进行解析,这里使用上边所说的@Around 中JoinPoint.proccess()方法,在执行AOP外部方法时使用Thread方法对其进行包裹并执行。

    /**
     *  定义一个注解的切点
     * @param joinPoint
     */
    @Around("normalPointCut() && @annotation(com.example.annotations.HunterAsyncAnno)")
    private void asyncPointAround(ProceedingJoinPoint joinPoint) {
        try {
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Method method = signature.getMethod();
            HunterAsyncAnno hunterAsyncAnno = method.getAnnotation(HunterAsyncAnno.class);
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(5000);
                        joinPoint.proceed();
                        System.out.println("END TIME:" + System.currentTimeMillis());
                    } catch (Throwable throwable) {
                        throwable.printStackTrace();
                    }
                }
            }).start();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
    }

同样使用Controller方法进行测试,可以看到异步执行的结果:

可以看到使用Thread类start后进行异步处理proccess()。 以上,介绍了SpringBoot中结合Aspect自定义注解的简单实践。

作者:joy悦
链接:https://juejin.cn/post/7040258854751305764
来源:稀土掘金

上一篇 下一篇

猜你喜欢

热点阅读