Spring AOP

Aspect定义

在 Spring 中使用 Aspect 需要使用 @Component 直接将其标记为一个 Bean
并且使用 @Aspec 注解将其标记为一个切面
然后在该类中定义上面我们说的切点,通知等

Pointcut

定义
@Pointcut("execution(public void WeixinService.share(String))")
    public void shareCut() {

    }

切点定义在方法上,并使用 @Pointcut 注解,注解中的值便是切点的表达式
切点的名称就是方法的名称,这里是 shareCut(),注意这里有括号
若要将具体的通知 Advice 关联的某个切点上,在 Advice 的注解上写上切点的名称就可以了,如下

@AfterReturning("shareCut()") public void log(JoinPoint joinPoint) { System.out.println(joinPoint.getSignature() + " executed"); }

execution 表达式

  • 任意公共方法的执行:execution(public * *(…))
  • 任何一个以“set”开始的方法的执行:execution(* set*(…))
    AccountService接口的任意方法的执行:execution(* com.xyz.service.AccountService.*(…))
  • 定义在service包里的任意方法的执行:execution(* com.xyz.service..(…))
  • 定义在service包或者子包里的任意方法的执行:execution(* com.xyz.service….(…))
  • 在service包里的任意连接点(在Spring AOP中只是方法执行) :within(com.xyz.service.*)
  • 在service包或者子包里的任意连接点(在Spring AOP中只是方法执行) :within(com.xyz.service…*)
指示器

切点的表达式以 指示器 开始, 指示器 就是一种关键字,用来告诉 Spring AOP 如何匹配连接点,Spring AOP 提供了以下几种指示器

execution
within
this 和 target
args
@target
@annotation

  • execution
    匹配方法执行连接点,即匹配哪个方法执行
@Pointcut("execution(public String aaric.springaopdemo.UserDao.findById(Long))")

上面这个切点会匹配在 UserDao 类中 findById 方法的调用,并且需要该方法是 public 的,返回值类型为 String,只有一个 Long 的参数。 切点的表达式同时还支持宽字符匹配,如

@Pointcut("execution(* aaric.springaopdemo.UserDao.*(..))")

上面的表达式中,第一个宽字符 * 匹配 任何返回类型,第二个宽字符 * 匹配 任何方法名,最后的参数 (…) 表达式匹配 任意数量任意类型 的参数,也就是说该切点会匹配类中所有方法的调用。

  • within
    如果要匹配一个类中所有方法的调用,便可以使用 within 指示器

@Pointcut(“within(aaric.springaopdemo.UserDao)”)
这样便可以匹配该类中所有方法的调用了。同时,我们还可以匹配某个包下面的所有类的所有方法调用,如下面的例子

@Pointcut(“within(aaric.springaopdemo…*)”)

  • this 和 target
    如果目标对象实现了任何接口,Spring AOP 会创建基于CGLIB 的动态代理,这时候需要使用 target 指示器

如果目标对象没有实现任何接口,Spring AOP 会创建基于JDK的动态代理,这时候需要使用 this 指示器

@Pointcut(“target(aaric.springaopdemo.A)”) A 实现了某个接口 @Pointcut(“target(aaric.springaopdemo.B)”) B 没有实现任何一个接口

  • args
    该指示器用来匹配具体的方法参数

@Pointcut(“execution(* …find(Long))”)
这个切点会匹配任何以 find 开头并且只有一个 Long 类型的参数的方法。 如果我们想匹配一个以 Long 类型开始的参数,后面的参数类型不做限制,我们可以使用如下的表达式

@Pointcut(“execution(* …find(Long,…))”)

  • @target
    该指示器不要和 target 指示器混淆,该指示器用于匹配连接点所在的类是否拥有指定类型的注解,如

@Pointcut("@target(org.springframework.stereotype.Repository)")

  • @annotation
    该指示器用于匹配连接点的方法是否有某个注解

@Pointcut("@annotation(org.springframework.scheduling.annotation.Async)")

  • 组合切点表达式
    切点表达式可以通过 &&、 || 和 !等操作符来组合,如

@Pointcut("@target(org.springframework.stereotype.Repository)")
public void repositoryMethods() {}

@Pointcut(“execution(* …create(Long,…))”)
public void firstLongParamMethods() {}

@Pointcut(“repositoryMethods() && firstLongParamMethods()”)
public void entityCreationMethods() {}

Advice

定义

Advice 通知,即在连接点处要执行的代码,分为以下几种类型:
Around
Before
After

开启Advice

如果要在 Spring 中使用 Spring AOP 需要开启 Advice,使用 @EnableAspectJAutoProxy 注解就可以了,代码如下

@SpringBootApplication
@EnableAspectJAutoProxy
public class SpringAopDemoApplication {
}

Before Advice

Before Advice 用来执行在方法调用之前的操作,如果 Before Advice 在执行的过程中抛出异常的话,那么连接点的方法就不会被执行。

@Aspect
@Component
public class PrintAspect {

@Pointcut("@target(org.springframework.stereotype.Repository)")
public void repositoryMethods() {};

@Before("repositoryMethods()")
public void logMethodCall(JoinPoint jp) {
    String name = jp.getSignature().getName();
    System.out.println("Before " + name);
}

}
logMethodCall Advice 会在任何 Repository 方法执行之前调用

After Advice

顾名思义,该 Advice 会在方法被调用之后被执行,但是有三种注解可以使用:

@AfterReturing:该 Advice 会在方法正常返回以后执行
@AfterThrowing: 该 Advice 会在方法抛出异常以后执行
@After: 该 Advice 无论如何,在方法执行以后都会执行

Around Advice

这个是最有用的 Advice, 它可以控制方法在执行前后的行为。 它可以选择是否继续执行连接点的方法,或者中断该方法的执行,而返回自己的返回值。

自定义AOP Annitation

  • 创建Annotation
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface CalculateExecuteTime {
    }
  • 创建切面
    @Aspect
    @Component
    public class CalculateExecuteTimeAspect {

}

  • 创建切点和通知
    @Aspect
    @Component
    public class CalculateExecuteTimeAspect {
    @Around("@annotation(CalculateExecuteTime)")
    public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
    long start = System.currentTimeMillis();
    Object proceed = joinPoint.proceed();
    long executionTime = System.currentTimeMillis() - start;
    System.out.println(joinPoint.getSignature() + " executed in " + executionTime + “ms”);
    return proceed;
    }
    }

  • 在方法上加上自定义注解
    @Service
    public class WeixinService {

    @CalculateExecuteTime
    public void share(@NotNull String articleUrl) {
    try {
    TimeUnit.SECONDS.sleep(3);
    } catch (Exception ignored) {

      }
    

    }
    }

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值