用自定义注解去控制AOP切口位置
内容目录

参考资料

https://juejin.cn/post/6999570632409088008
细说Spring——AOP详解(AOP概览)-CSDN博客

定义

AOP,即面向切面编程,其核心思想就是把业务分为核心业务非核心业务两大部分。例如一个论坛系统,用户登录、发帖等等这是核心功能,而日志统计等等这些就是非核心功能。

image.png|450

即,提取公共的且非核心的业务,将其封装成一个类(例如上图的检测有效性等,还有使用较多的打印接口调用记录日志功能等)

术语

  • Aspect(切面): Aspect 声明类似于 Java 中的类声明,在 Aspect 中会包含着一些 Pointcut 以及相应的 Advice。
  • Joint point(连接点):表示在程序中明确定义的点,典型的包括方法调用,对类成员的访问以及异常处理程序块的执行等等,它自身还可以嵌套其它 joint point。
  • Pointcut(切点):表示一组 joint point,这些 joint point 或是通过逻辑关系组合起来,或是通过通配、正则表达式等方式集中起来,它定义了相应的 Advice 将要发生的地方。
  • Advice(增强):Advice 定义了在 Pointcut 里面定义的程序点具体要做的操作,它通过 before、after 和 around 来区别是在每个 joint point 之前、之后还是代替执行的代码。
  • Target(目标对象):织入 Advice 的目标对象.。
  • Weaving(织入):将 Aspect 和其他对象连接起来, 并创建 Adviced object 的过程

实现

引入依赖

在Spring Boot中,我们使用@AspectJ注解开发AOP,首先需要在pom.xml中引入如下依赖:

<!-- Spring Boot AOP -->  
<dependency>  
    <groupId>org.springframework.boot</groupId>  
    <artifactId>spring-boot-starter-aop</artifactId>  
</dependency>

常用注解

  • @Pointcut 定义切点
  • @Before 前置通知
  • @After 后置通知
  • @AfterReturning 返回通知
  • @AfterThrowing 异常通知
  • @Around 环绕通知

    定义切面

    
    import org.aspectj.lang.annotation.*;  
    import org.slf4j.Logger;  
    import org.slf4j.LoggerFactory;  
    import org.springframework.stereotype.Component;  
    @Aspect  
    @Component  
    public class LogAspect {  
    
    /**  
    * 日志打印  
    */  
    private Logger logger = LoggerFactory.getLogger(this.getClass());  
    /**  
    * 使用Pointcut给这个方法定义切点,即UserService中全部方法均为切点。<br>  
    * 这里在这个log方法上面定义切点,然后就只需在下面的Before、After等等注解中填写这个切点方法"log()"即可设置好各个通知的切入位置。  
    * 其中:  
    * <ul>  
    *     <li>execution:代表方法被执行时触发</li>  
    *     <li>*:代表任意返回值的方法</li>  
    *     <li>com.example.springbootaop.service.impl.UserServiceImpl:这个类的全限定名</li>  
    *     <li>(..):表示任意的参数</li>  
    * </ul>  
    */  
    @Pointcut("execution(* com.example.springbootaop.service.impl.UserServiceImpl.*(..))")  
    public void log() {  
    
    }  
    /**  
    * 前置通知:在被代理方法之前调用  
    */  
    @Before("log()")  
    public void doBefore() {  
      logger.warn("调用方法之前:");  
      logger.warn("接收到请求!");  
    }  
    
    /**  
    * 后置通知:在被代理方法之后调用  
    */  
    @After("log()")  
    public void doAfter() {  
      logger.warn("调用方法之后:");  
      logger.warn("打印请求内容完成!");  
    }  
    
    /**  
    * 返回通知:被代理方法正常返回之后调用  
    */  
    @AfterReturning("log()")  
    public void doReturning() {  
      logger.warn("方法正常返回之后:");  
      logger.warn("完成返回内容!");  
    }  
    
    /**  
    * 异常通知:被代理方法抛出异常时调用  
    */  
    @AfterThrowing("log()")  
    public void doThrowing() {  
      logger.error("方法抛出异常!");  
    }  
    }

感觉实际使用中,直接通过类似` @Pointcut("execution( com.example.springbootaop.service.impl.UserServiceImpl.(..))") `的方法去定义切点位置很不灵活
因此考虑与注解结合,然后用注解去控制切口位置

通过注解去控制AOP

自定义注解

import java.lang.annotation.ElementType;  
import java.lang.annotation.Retention;  
import java.lang.annotation.RetentionPolicy;  
import java.lang.annotation.Target;  

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

这个注解没有实现具体功能,仅仅是作为切面的定位作用

  • `@Target(ElementType.METHOD)`指定这个注解只能用在方法上。
  • `@Retention(RetentionPolicy.RUNTIME)`表示这个注解在运行时保留,这样AOP框架在运行时能够通过反射读取这个注解。

切面

import org.aspectj.lang.annotation.*;  
import org.slf4j.Logger;  
import org.slf4j.LoggerFactory;  
import org.springframework.stereotype.Component;  

@Aspect  
@Component  
public class LogAspect {  

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

   @Pointcut("@annotation(com.hellomeme.v2.common.Annotation.LogExecution)")  
   public void log() {  
   }  

   @Before("log()")  
   public void doBefore() {  
      logger.warn("调用方法之前:");  
      logger.warn("接收到请求!");  
   }

核心代码:@Pointcut("@annotation(com.hellomeme.v2.common.Annotation.LogExecution)")
表示所有标有@LogExecution注解的方法都会被匹配。
@annotation(xxx)中的参数是子类定义注解类的位置

image.png|348

实现

image.png|475

随后只需要在方法类上面打上一开始的注解,便可以实现这个功能。
image.png

上一篇
下一篇