基于SpringBoot AOP实现自定义注解实践
在编写代码,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
来源:稀土掘金