Spring中Aop是如何使用的呢?

Posted lllllLiangjia

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring中Aop是如何使用的呢?相关的知识,希望对你有一定的参考价值。

目录

一、Advice通知类型

1.1 前置通知

1.2 返回后通知

1.3 抛出异常后通知

1.4 后置通知

1.5 环绕通知

1.5.1 环绕通知参数JoinPoint作用

二、方法参数与advice结合

2.1 根据参数类型查询

实例

2.2 将参数传递给advice

三、Aop代码解耦操作

四、体现的三种切面覆盖方式

4.1 定义使用注解的

4.2 定义某个方法的

4.3 定义某个类的


一、Advice通知类型

1.1 前置通知

使用切入点表达式

    @Before("execution(* com.xyz.myapp.dao.*.*(..))")
    public void doAccessCheck() {
        // ...
    }

1.2 返回后通知

    @AfterReturning(
        pointcut="com.xyz.myapp.CommonPointcuts.dataAccessOperation()",
        returning="retVal")
    public void doAccessCheck(Object retVal) {
        // ...
    }

returning属性中使用的名称必须与建议方法中的参数名称相对应。当方法执行返回时,该返回值将作为相应的参数值传递到通知方法。还将匹配限制为仅返回指定类型的值的那些方法执行。

 

1.3 抛出异常后通知

    @AfterThrowing("com.xyz.myapp.CommonPointcuts.dataAccessOperation()")
    public void doRecoveryActions() {
        // ...
    }

希望通知仅在引发给定类型的异常时才运行,并且您通常还需要在通知正文中访问所引发的异常。您可以使用该throwing属性来限制匹配,throwing属性中使用的名称必须与建议方法中的参数名称相对应。也可限制能匹配到抛出指定类型的异常。

 

1.4 后置通知

    @After("com.xyz.myapp.CommonPointcuts.dataAccessOperation()")
    public void doReleaseLock() {
        // ...
    }

@AfterAspectJ中的建议被定义为“ after finally建议”,类似于try-catch语句中的finally块。它将针对从连接点(用户声明的目标方法)引发的任何结果,正常返回或异常调用。

1.5 环绕通知

    @Around("com.xyz.myapp.CommonPointcuts.businessService()")
    public Object doBasicProfiling(ProceedingJoinPoint joinPoint) throws Throwable {
        // start stopwatch
        Object retVal = joinPoint.proceed();
        // stop stopwatch
        return retVal;
    }

围绕建议在匹配方法的执行过程中“围绕”运行。它有机会在该方法运行之前和之后进行工作,并确定何时,如何以及即使该方法实际上可以运行。如果需要以线程安全的方式(例如,启动和停止计时器)在方法执行之前和之后共享状态,则通常使用环绕建议。

 

获取方法的第一个参数必须为类型ProceedingJoinPoint。在通知的主体,要求proceed()ProceedingJoinPoint导致运行基本方法。

 

proceed方法也可以传入Object[]。数组中的值在方法执行proceed时用作参数。

        Object[] args = joinPoint.getArgs();
        args[0] = "哈哈哈";
        joinPoint.proceed(args);

1.5.1 环绕通知参数JoinPoint作用

我们在使用@Around环绕通知时,方法中的参数是ProceedingJoinPoint类型的,它继承了JoinPoint接口,在此我们了解下该接口。

JoinPoint接口提供的方法:

  • getArgs():返回方法参数。

  • getThis():返回代理对象。

  • getTarget():返回目标对象。

  • getSignature():返回建议使用的方法的描述。

  • toString():打印有关所建议方法的有用描述。

 

二、方法参数与advice结合

2.1 根据参数类型查询

@Before("execution(* ..Sample+.sampleGenericMethod.*(..)) && args(param)")
public void beforeSampleMethod(String param) {
    // Advice implementation
}

定义sampleGenericMethod类中参数类型为String的方法才进行切面。

实例

1.切面类

@Component
@Aspect
public class GetNameAdvise {

    @Before(value = "execution(* com.example.aopdemo.demo1.controller.DemoController.alive(..))" +
            " && args(c,b)")
    public void audit(String c,Integer b) {
        System.out.println(c);
        System.out.println(b);
    }

}

2.具体方法

@RestController
public class DemoController {
    //String和Integer类型参数的方法
    @GetMapping("/aop/http/alive1")
    public String alive(String a,Integer d) {
        System.out.println("String和Integer类型参数的方法执行");
    }

    //两个String类型参数的方法
    @GetMapping("/aop/http/alive2")
    public String alive(String b,String c) {
        System.out.println("两个String类型参数的方法执行");
    }

}

3.执行结果

访问http://localhost:8080/aop/http/alive1?a=asdfaf&d=123链接时,控制台打印结果

访问http://localhost:8080/aop/http/alive2?b=asdfaf&c=ccccc链接时,控制台打印结果

由此可知,由于@Before前置增强方法两个参数是String和Integer的,所以只对参数是String和Integer的方法生效。

2.2 将参数传递给advice

@Before("com.xyz.myapp.CommonPointcuts.dataAccessOperation() && args(account,..)")
public void validateAccount(Account account) {
    // ...
}

args(account,..)切入点表达式的一部分用于两个目的。首先,它将匹配限制为仅方法采用至少一个参数且传递给该参数的参数为的实例的方法执行Account。其次,它Account通过account 参数使实际对象可用于advice。此处跟2.1同理,不再写示例。

 

三、Aop代码解耦操作

当存在多个Advice通知时,面向的切点是一样的话,可以创建一个空方法,在这个方法上定义切面。后期容易修改,@Pointcut一处进行修改,它对应的的advice处处生效。

    
    //统一定义的切点
    @Pointcut("within(com.example.aopdemo.demo2.service.NeedLogService)")
    public void pointcut() {
    }

    // 定义 advise
    @Before("pointcut()")
    public void logMethodInvokeParam(JoinPoint joinPoint) {
        logger.info("---Before method {} invoke, param: {}---", joinPoint.getSignature().toShortString(), joinPoint.getArgs());
    }

    @AfterReturning(pointcut = "pointcut()", returning = "retVal")
    public void logMethodInvokeResult(JoinPoint joinPoint, Object retVal) {
        logger.info("---After method {} invoke, result: {}---", joinPoint.getSignature().toShortString(), joinPoint.getArgs());
    }

    @AfterThrowing(pointcut = "pointcut()", throwing = "exception")
    public void logMethodInvokeException(JoinPoint joinPoint, Exception exception) {
        logger.info("---method {} invoke exception: {}---", joinPoint.getSignature().toShortString(), exception.getMessage());
    }

四、体现的三种切面覆盖方式

4.1 定义使用注解的

关键字annotation

@Pointcut("@annotation(com.example.aopdemo.demo1.config.AuthChecker)")

4.2 定义某个方法的

关键字execution

  @Pointcut("execution(* com.example.aopdemo.demo1.controller.DemoController.alive())")

4.3 定义某个类的

关键字within

   @Pointcut("within(com.example.aopdemo.demo1.controller.DemoController))")

 

aop中Pointcut切入点指示符操作,请阅读

https://blog.csdn.net/liangjiabao5555/article/details/115362251

以上是关于Spring中Aop是如何使用的呢?的主要内容,如果未能解决你的问题,请参考以下文章

spring aop中this和target区别

阿里四面:你知道Spring AOP创建Proxy的过程吗?

AOP在Spring Boot中如何使用

手动实现自己的AOP

说说AOP和IOC的概念以及在spring中是如何应用的

Spring 容器AOP的实现原理——动态代理