首页 > 其他分享 >Springboot:aop注解实现

Springboot:aop注解实现

时间:2023-01-18 14:00:58浏览次数:37  
标签:test2 methodName Springboot 通知 args aop joinPoint 注解 执行

概念

Aop原指面向切面编程。但在spring中对aop的实现指的是:

对方法执行前后加入其它逻辑代码,达到增强方法的目的

spring-aop的底层实现

一般实现aop,有两种方案:

  1. JDK动态代理
  2. cglib代理

spring中两者都可使用,默认使用JDK动态代理。具体如何实现不作讨论,本文仅讲如何使用

spring-aop基本术语

  1. 连接点(JoinPoint):能被增强(通知)的方法,指具有被增强的资格。
  2. 切入点(CutPoint):实际被增强的方法
  3. 通知(Advice):增强的代码

通知有5种通知:

  1. 前置通知(Before):被增强方法执行之前执行
  2. 后置通知(AfterReturning):被增强方法执行之后执行,出现异常就不执行了。
  3. 环绕通知(Around):方法执行前后都执行
  4. 异常通知(AfterThrowing):方法执行出现异常时执行
  5. 最终通知(After):被增强方法执行之后执行,不管是否有异常,都会执行。类似finally
  1. 切面(Aspect):切入点+通知,指通知织入到切入点的过程

切入点表达式:execution

切入点就是要增强的方法,因此需要一个类似正则那样的表达式来描述哪些方法要被增强,他就是execution表达式。语法如下:

execution([方法权限修饰符] [方法返回值] [类全路径][方法名称] ([参数列表]))

举例:

  1. 对 com.xx.User 的 add方法进行增强。

execution(* com.xx.User.add(..))

  1. 对 com.xx.User 的 所有方法进行增强。

execution(* com.xx.User.*(..))

  1. 对 com.xx包下的所有方法进行增强。

execution(* com.xx..(..))

小知识

相信大家在某些代码上看到并没有使用execution表达式,而是这种:

@Pointcut("@annotation(com.ruoyi.framework.aspectj.lang.annotation.Log)")

此方式表示对加了Log注解的所有方法都进行增强。

springboot注解实现aop

  1. 创建一个类作为通知类,添加@Component和@Aspect注解。
  2. 配置切入点;编写一个无参的普通方法,添加@Pointcut
  3. 编写通知方法。

通知类实现如下:

@Component
@Aspect
public class MyAspect {

    @Pointcut("execution(* com.ruoyi.xx.test.JobTestController.test2(..))")
    public void point() { }

    /**
     * 前置通知
     * 连接点之前执行
     */
    @Before("point()")
    public void before(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        System.out.println("before() methodName:" + methodName + ", args:" + Arrays.toString(args));
    }

    @Around("point()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        System.out.println("around() before proceed");
        Object proceed = joinPoint.proceed();
        System.out.println("around() after proceed methodName:" + methodName + ", args:" + Arrays.toString(args) + ", result:" + proceed);
        return proceed;
    }

    /**
     * 后置通知
     * 连接点方法完成后执行
     * 无论连接点执行成功与否该通知都会执行
     */
    @After("point()")
    public void after(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        System.out.println("after() methodName:" + methodName + ", args:" + Arrays.toString(args));
    }

    /**
     * 连接点方法执行成功后执行
     * 可以拿到返回结果
     */
    @AfterReturning(value = "point()", returning = "result")
    public void result(JoinPoint joinPoint, Object result) {
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        System.out.println("AfterReturning() methodName:" + methodName + ", args:" + Arrays.toString(args) + ", result:" + result);
    }

    /**
     * 连接点方法执行异常后执行
     * 可以拿到异常信息
     */
    @AfterThrowing(value = "point()", throwing = "exception")
    public void afterThrowing(JoinPoint joinPoint, Exception exception) {
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        System.out.println("afterThrowing() methodName:" + methodName + ", args:" + Arrays.toString(args) + ", exception:" + exception);
    }

}

被加强方法:

public void test2() {
        System.out.println("---------------------我是test2------------------------");
    }

测试:

    @PostMapping(value = "/test3")
    @ResponseBody
    public void test3() throws Exception {
        JobTestController bean = SpringUtils.getBean(JobTestController.class);
        bean.test2();
    }

打印结果如下:

around() before proceed
before() methodName:test2, args:[]
---------------------我是test2------------------------
around() after proceed methodName:test2, args:[], result:null
after() methodName:test2, args:[]
AfterReturning() methodName:test2, args:[], result:null

注意,在around方法中一般都会手动调用被增强方法的,因此执行顺序如下:

  1. 环绕通知前
  2. 前置通知
  3. 被增强方法
  4. 环绕通知后
  5. 最终通知
  6. 后置通知

上面是全部通知类型都有,且正常执行的流程下。现在稍作改动,在环绕通知中去掉对切入点的调用,则打印如下:

around() before proceed
around() after proceed methodName:test2, args:[], result:null
after() methodName:test2, args:[]
AfterReturning() methodName:test2, args:[], result:null

现在顺序是:

  1. 环绕通知前
  2. 环绕通知后
  3. 最终通知
  4. 后置通知

可以看到去掉对切入点的调用,前置通知和增强方法居然不执行了,但后置通知和最终通知任然执行。那如果我们把环绕通知代码去掉,打印结果会如何呢?如下:

before() methodName:test2, args:[]
---------------------我是test2------------------------
after() methodName:test2, args:[]
AfterReturning() methodName:test2, args:[], result:null

可以看到,前置通知和增强方法执行了。

因此可以得到一个小结论:

如果使用了环绕通知,则必须对增强方法进行调用

最后大家可能还想看异常通知出现,那我们把通知类恢复到最刚开始的代码,然后在增强方法中制造异常,如下:

public void test2() {
        System.out.println("---------------------我是test2------------------------");
        throw new RuntimeException();
    }

打印结果如下:

around() before proceed
before() methodName:test2, args:[]
---------------------我是test2------------------------
after() methodName:test2, args:[]
afterThrowing() methodName:test2, args:[], exception:java.lang.RuntimeException

执行顺序是:

  1. 环绕通知前
  2. 前置通知
  3. 方法体(一部分)
  4. 最终通知
  5. 异常通知

可以看到,有几个变化:

  1. 方法体还是执行,但只执行了异常前部分的代码
  2. 环绕通知后和后置通知不执行了,最终通知还是执行
  3. 异常通知出现了

标签:test2,methodName,Springboot,通知,args,aop,joinPoint,注解,执行
From: https://www.cnblogs.com/ibcdwx/p/17059344.html

相关文章

  • SpringBoot2注解:@Configuration
    @configuration@Configuration这个注解作用就是告诉springboot这是一个配置类。配置类以及类里的方法都可以作为bean。用@Bean标记@Configuration包含 proxyBeanMeth......
  • 学习笔记——AOP-代理模式
    2023-01-18一、AOP前奏-代理模式1、手动实现动态代理环境搭建(1)基于接口实现动态代理:JDK动态代理(2)基于继承实现动态代理:Cglib、javassist动态代理2、实现动态代理的步......
  • MyBatis-XML和注解
    参考:https://www.cnblogs.com/lyc-smile/p/9055856.htmlhttps://ld246.com/article/1488810828678https://blog.csdn.net/qq_41617744/article/details/80224721  ......
  • 学习笔记——Spring中组件扫描(包含扫描、排除扫描)、Spring中完全注解开发;Spring整合Ju
    2023-01-18一、Spring中组件扫描1、默认使用的情况<context:component-scanbase-package="com.hh"></context:component-scan>2、包含扫描注:使用包含扫描之前,必须......
  • 12.AOP
    1.什么是AOPAOP(AspectOrientedProgramming)意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一......
  • 学习笔记——Spring中的注解;Spring中装配对象的注解;使用注解配置对象中属性
    2023-01-17一、Spring中的注解 1、使用注解的原因(1)使用注解将对象装配到IOC容器中(2)使用注解管理对象之间依赖关系(自动装配)2、Spring中装配对象的注解(1)@Component标......
  • Springboot整合策略模式概念->使用场景->优缺点->企业级实战
    一、前言策略模式可能是在工作中使用最多的,也是在面试中最常提到的,代码重构和优化的必备!小编之前也是一直说,其实没有真正的实战;最近有了机会实战了一下,来分享一下使用心得......
  • 统一返回对象封装和统一异常捕获封装springboot starter
    好久没有更新文章了,高龄开发没什么技术,去了外包公司后没怎么更新文章了。今天分享下统一处理starter,相信开发web系统的时候都是会涉及到前后端的交互,而后端返回数据的时候......
  • 注解使用
    1.@Secured角色判断1.1开启注解App启动类中加入注解@EnableGlobalMethodSecurity(securedEnabled=true,prePostEnabled=true)1.2controller层下@PostMapping......
  • @Transactional注解失效问是
    1.Transactional注解修饰在非public方法上的时候,2.被自己类中的别的方法调用说明:被注解修饰的方法所在的类,该类中的方法直接互相调用就会失效,只有别的类掉这个类的......