Spring框架(三)——AOP--实现方式

Spring对AOP的实现包括以下3种方式:

  • 第一种方式:Spring框架结合AspectJ框架实现的AOP,基于注解方式
  • 第二种方式:Spring框架结合AspectJ框架实现的AOP,基于XML方式
  • 第三种方式:Spring框架自己实现的AOP,基于XML配置方式。

在本篇文章中,我们主要讲解前两种方式。


  1. 基于AspectJ的AOP注解式开发
    1. 定义目标类以及目标方法
      1. // 目标类
        public class OrderService {
            // 目标方法
            public void generate(){
                System.out.println("订单已生成!");
            }
        }
    2. 定义切面类
      1. // 切面类
        @Aspect
        public class MyAspect {
        }

        注解@Aspect会告诉Spring该类是一个注解类

    3. 目标类和切面类都纳入spring bean管理
      1. 在目标类OrderService上添加**@Component**注解。
        在切面类MyAspect类上添加**@Component**注解。
    4. 在spring配置文件中添加组建扫描
      1. <!--开启组件扫描-->
            <context:component-scan base-package="com.xxx.spring6.service"/>

        这里是单单扫描com.xxx.spring6.service中的所有类,如果想扩大,我们完全可以扫描com.xxx下的所有包

    5. 在切面类中添加通知
      1. // 切面类
        @Aspect
        @Component
        public class MyAspect {
            // 这就是需要增强的代码(通知)
            public void advice(){
                System.out.println("我是一个通知");
            }
        }
    6. 在通知上添加切点表达式
      1. // 切面类
        @Aspect
        @Component
        public class MyAspect {
            
            // 切点表达式
            @Before("execution(* com.xxx.spring6.service.OrderService.*(..))")
            // 这就是需要增强的代码(通知)
            public void advice(){
                System.out.println("我是一个通知");
            }
        }

        其中,注解@Before表示前置通知(具体的通知类型在上一篇有讲过AOP基础部分
        ,下面也会有方法中的通知注解)

    7. 在spring配置文件中启用自动代理
      •  <!--开启组件扫描-->
            <context:component-scan base-package="com.xxx.spring6.service"/>
            <!--开启自动代理-->
            <aop:aspectj-autoproxy proxy-target-class="true"/>

        <aop:aspectj-autoproxy  proxy-target-class="true"/> 开启自动代理之后,凡是带有@Aspect注解的bean都会生成代理对象。
        proxy-target-class="true" 表示采用cglib动态代理。
        proxy-target-class="false" 表示采用jdk动态代理。默认值是false。即使写成false,当没有接口的时候,也会自动选择cglib生成代理类(AOP的底层就是动态代理,关于动态代理的内容可查看代理机制)        

  • 通知类型
    • 前置通知:@Before 目标方法执行之前的通知
    • 后置通知:@AfterReturning 目标方法执行之后的通知
    • 环绕通知:@Around 目标方法之前添加通知,同时目标方法执行之后添加通知。
    • 异常通知:@AfterThrowing 发生异常之后执行的通知
    • 最终通知:@After 放在finally语句块中的通知
  • 接下来,编写程序来测试这几个通知的执行顺序
    • // 切面类
      @Component
      @Aspect
      public class MyAspect {
      
          @Around("execution(* com.xxx.spring6.service.OrderService.*(..))")
          public void aroundAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
              System.out.println("环绕通知开始");
              // 执行目标方法。
              proceedingJoinPoint.proceed();
              System.out.println("环绕通知结束");
          }
      
          @Before("execution(* com.xxx.spring6.service.OrderService.*(..))")
          public void beforeAdvice(){
              System.out.println("前置通知");
          }
      
          @AfterReturning("execution(* com.xxx.spring6.service.OrderService.*(..))")
          public void afterReturningAdvice(){
              System.out.println("后置通知");
          }
      
          @AfterThrowing("execution(* com.xxx.spring6.service.OrderService.*(..))")
          public void afterThrowingAdvice(){
              System.out.println("异常通知");
          }
      
          @After("execution(* com.xxx.spring6.service.OrderService.*(..))")
          public void afterAdvice(){
              System.out.println("最终通知");
          }
      
      }
        读者可自行测试,其中异常通知需要产生异常才能触发,当发生异常之后,最终通知也会执行,因为最终通知@After会出现在finally语句块中。出现异常之后,**后置通知**和**环绕通知的结束部分**不会执行
  • 切面的先后顺序
    • 我们知道,业务流程当中不一定只有一个切面,可能有的切面控制事务,有的记录日志,有的进行安全控制,如果多个切面的话,顺序如何控制:可以使用@Order注解来标识切面类,为@Order注解的value指定一个整数型的数字,数字越小,优先级越高
  • 优化使用切点表达式​​​​​​​
    • 上面的切点表达式重复写了多次,没有得到复用,同时如果要修改切点表达式,需要修改多处,难维护
    • 我们可以将切点表达式单独的定义出来,在需要的位置引入即可
      • // 切面类
        @Component
        @Aspect
        @Order(2)
        public class MyAspect {
            
            @Pointcut("execution(* com.xxx.spring6.service.OrderService.*(..))")
            public void pointcut(){}
        
            @Around("pointcut()")
            public void aroundAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
                System.out.println("环绕通知开始");
                // 执行目标方法。
                proceedingJoinPoint.proceed();
                System.out.println("环绕通知结束");
            }
        
            @Before("pointcut()")
            public void beforeAdvice(){
                System.out.println("前置通知");
            }
        
            @AfterReturning("pointcut()")
            public void afterReturningAdvice(){
                System.out.println("后置通知");
            }
        
            @AfterThrowing("pointcut()")
            public void afterThrowingAdvice(){
                System.out.println("异常通知");
            }
        
            @After("pointcut()")
            public void afterAdvice(){
                System.out.println("最终通知");
            }
        
        }

        使用@Pointcut注解来定义独立的切点表达式。
        注意这个@Pointcut注解标注的方法随意,只是起到一个能够让@Pointcut注解编写的位置

  1. 基于XML配置方式的AOP
    1. 编写目标类
      1. // 目标类
        public class VipService {
            public void add(){
                System.out.println("保存vip信息。");
            }
        }
    2. 编写切面类,并且编写通知
      1. // 负责计时的切面类
        public class TimerAspect {
            
            public void time(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
                long begin = System.currentTimeMillis();
                //执行目标
                proceedingJoinPoint.proceed();
                long end = System.currentTimeMillis();
                System.out.println("耗时"+(end - begin)+"毫秒");
            }
        }
    3. 编写spring配置文件
      1.     <!--纳入spring bean管理-->
            <bean id="vipService" class="com.xxx.spring6.service.VipService"/>
            <bean id="timerAspect" class="com.xxx.spring6.service.TimerAspect"/>
        
            <!--aop配置-->
            <aop:config>
                <!--切点表达式-->
                <aop:pointcut id="p" expression="execution(* com.xxx.spring6.service.VipService.*(..))"/>
                <!--切面-->
                <aop:aspect ref="timerAspect">
                    <!--切面=通知 + 切点-->
                    <aop:around method="time" pointcut-ref="p"/>
                </aop:aspect>
            </aop:config>
        </beans>

相关推荐

  1. Spring框架)——AOP--实现方式

    2024-07-12 12:38:01       19 阅读
  2. Spring AOP的几种实现方式

    2024-07-12 12:38:01       18 阅读
  3. Spring AOP 使用方式

    2024-07-12 12:38:01       28 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-12 12:38:01       67 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-12 12:38:01       71 阅读
  3. 在Django里面运行非项目文件

    2024-07-12 12:38:01       58 阅读
  4. Python语言-面向对象

    2024-07-12 12:38:01       69 阅读

热门阅读

  1. vue-grid-layout详解

    2024-07-12 12:38:01       23 阅读
  2. linux,docker,k8s常见命令

    2024-07-12 12:38:01       21 阅读
  3. TensorFlow 和 PyTorch 显示模型参数方法详解

    2024-07-12 12:38:01       20 阅读
  4. 【go学习合集】进阶数据类型2 -------->切片

    2024-07-12 12:38:01       20 阅读
  5. 扩展欧几里得c++

    2024-07-12 12:38:01       24 阅读
  6. elementui的table的@selection-change阻止事件改变

    2024-07-12 12:38:01       22 阅读
  7. 介绍5款.NET开源、功能强大的Windows桌面工具箱

    2024-07-12 12:38:01       17 阅读
  8. tp计算距离,筛选0-10km距离内商家

    2024-07-12 12:38:01       24 阅读
  9. n3.平滑升级和回滚

    2024-07-12 12:38:01       16 阅读
  10. 有了HTTP,为什么还需要HTTPS?

    2024-07-12 12:38:01       27 阅读
  11. k8s中Service暴露的种类以及用法

    2024-07-12 12:38:01       21 阅读