Spring Aop核心注解的使用和详解

要在 Spring 中声明 AspectJ 切面, 只需要在 IOC 容器中将切面声明为 Bean 实例. 当在 Spring IOC 容器中初始化 AspectJ 切面之后, Spring IOC 容器就会为那些与 AspectJ 切面相匹配的 Bean 创建代理。
在切面类中需要定义切面方法用于响应响应的目标方法,切面方法即为通知方法,通知方法需要用注解标识,AspectJ 支持 5 种类型的通知注解:

@Before: 前置通知, 在方法执行之前执行
@After: 后置通知, 在方法执行之后执行 。
@AfterRunning: 返回通知, 在方法返回结果之后执行
@AfterThrowing: 异常通知, 在方法抛出异常之后
@Around: 环绕通知, 围绕着方法执行
下面分别举例5中通知方法的使用

首先建立一个目标接口ArithmeticCalculator:

package lzj.com.spring.aop;

public interface ArithmeticCalculator {
    int add(int i, int j);
    int div(int i, int j);
}

然后创建接口的实现类ArithmeticCalculatorIml :

package lzj.com.spring.aop;
import org.springframework.stereotype.Component;

@Component("arithmeticCalculator")
public class ArithmeticCalculatorIml implements ArithmeticCalculator {

    @Override
    public int add(int i, int j) {
        int result = i + j;
        System.out.println("add->result:" + result);
        return result;
    }

    @Override
    public int div(int i, int j) {
        int result = i / j;
        System.out.println("div->result:" + result);
        return result;
    }

}

创建测试类:

package lzj.com.spring.aop;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {

    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("bean-aop.xml");
        ArithmeticCalculator arithmetic = (ArithmeticCalculator) ctx.getBean("arithmeticCalculator");
        arithmetic.add(3, 2);
        arithmetic.div(4, 2);

    }

}

运行结果:

add->result:5
div->result:2

一、@Before前置通知

用@Before标识的方法为前置方法,在目标方法的执行之前执行,即在连接点之前进行执行。
示例如下:

package lzj.com.spring.aop;
import java.util.Arrays;
import java.util.List;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class LogProxy {

    @Before("execution(public int lzj.com.spring.aop.ArithmeticCalculator.*(int, int))")
    public void beforMethod(JoinPoint point){
        String methodName = point.getSignature().getName();
        List<Object> args = Arrays.asList(point.getArgs());
        System.out.println("调用前连接点方法为:" + methodName + ",参数为:" + args);
    }

}

执行测试类,输出结果如下:

调用前连接点方法为:add,参数为:[3, 2]
add->result:5
调用前连接点方法为:div,参数为:[4, 2]
div->result:2

在目标方法add和div之前分别执行了前置通知方法。

二、@After后置通知方法

后置方法在连接点方法完成之后执行,无论连接点方法执行成功还是出现异常,都将执行后置方法。

三、@AfterRunning返回通知方法

当连接点方法成功执行后,返回通知方法才会执行,如果连接点方法出现异常,则返回通知方法不执行。返回通知方法在目标方法执行成功后才会执行,所以,返回通知方法可以拿到目标方法(连接点方法)执行后的结果

@Aspect
@Component
public class LogProxy {

    /*通过returning属性指定连接点方法返回的结果放置在result变量中,在返回通知方法中可以从result变量中获取连接点方法的返回结果了。*/
    @AfterReturning(value="execution(public int lzj.com.spring.aop.ArithmeticCalculator.*(int, int))",
            returning="result")
    public void afterReturning(JoinPoint point, Object result){
        String methodName = point.getSignature().getName();
        List<Object> args = Arrays.asList(point.getArgs());
        System.out.println("连接点方法为:" + methodName + ",参数为:" + args + ",目标方法执行结果为:" + result);
    }
}

 

四、@AfterThrowing异常通知

异常通知方法只在连接点方法出现异常后才会执行,否则不执行。在异常通知方法中可以获取连接点方法出现的异常

五、@Around环绕通知

环绕通知方法可以包含上面四种通知方法,环绕通知的功能最全面。环绕通知需要携带 ProceedingJoinPoint 类型的参数,且环绕通知必须有返回值, 返回值即为目标方法的返回值。在切面类中创建环绕通知方法

六,@Pointcut切入点设置

设置以上注释方法的使用时机,注意以上注释可以单独使用,也可以配合该注解使用,该注解时常配合注解@annotation:用于匹配当前执行方法持有指定注解的方法使用

    // 配置织入点
    @Pointcut("@annotation(com.ruoyi.common.log.annotation.OperLog)")
    public void logPointCut()
    {
    }

    /**
     * 处理完请求后执行
     *
     * @param joinPoint 切点
     */
    @AfterReturning(pointcut = "logPointCut()")
    public void doAfterReturning(JoinPoint joinPoint)
    {
        handleLog(joinPoint, null);
    }

@Pointcut注解可以在一个切面内定义可重用的切点。

由于Spring切面粒度最小是达到方法级别,而execution表达式可以用于明确指定方法返回类型,类名,方法名和参数名等与方法相关的部件,并且实际中,大部分需要使用AOP的业务场景也只需要达到方法级别即可,因而execution表达式的使用是最为广泛的。如图是execution表达式的语法:

execution表示在方法执行的时候触发。以“”开头,表明方法返回值类型为任意类型。然后是全限定的类名和方法名,“”可以表示任意类和任意方法。对于方法参数列表,可以使用“…”表示参数为任意类型。如果需要多个表达式,可以使用“&&”、“||”和“!”完成与、或、非的操作。

获取节点方法实例或者参数

// 下面两个数组中,参数值和参数名的个数和位置是一一对应的。
        Object[] args = joinPoint.getArgs(); // 参数值
         String[] argNames =  ((MethodSignature)joinPoint.getSignature()).getParameterNames(); // 参数名


    Signature signature = pjp.getSignature();  
    MethodSignature methodSignature = (MethodSignature)signature;  
    Method targetMethod = methodSignature.getMethod(); //获取方法实例

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

EntyIU

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值