Spring AOP的概念及用法(aop,spring,开发技术)

时间:2024-05-10 05:07:53 作者 : 石家庄SEO 分类 : 开发技术
  • TAG :
目录
  • 什么是AOP?

  • AOP术语

    • 通知(Advice)

    • 连接点(Join point)

    • 切点(Pointcut)

      • 连接点和切点的区别

    • 切面(Aspect)

      • 引入(Introduction)

        • 织入(Weaving)

        • SpringAOP

          • SpringAOP的特点

            • SpringBoot集成SpringAOP

              • - 依赖引入

              • - 创建注解

              • - 定义切面

              • - 设置切点

              • - 业务接口编写

              • - 测试

            • 通知时机

              • - 正常情况

              • - 异常情况


          什么是AOP?

          AOP,即我们平时经常提到的面向切面编程。首先我们要理解一个叫横切关注点(cross-cutting concern)的概念,它其实是描述我们应用中的功能,假如有一个功能,它在应用程序中很多个地方都用了,那么我们把这样的功能称之为横切关注点。

          日常开发中,我们都会将不同的业务场景抽象出对应的模块进行开发,而不同的模块,除了那些针对特定领域的核心功能外,还有一些相同的辅助功能,比如日志管理、安全管理、事务管理等等。横切关注点这个概念其实就点明了:类似这样的功能就是我们面向切面编程需要关注的地方。这也是面向切面编程的意义所在:它帮助我们实现横切关注点和他们所影响的对象之间的解耦。

          面向切面编程的实质,就是讲横切关注点模块化成称为切面的特殊的类。

          AOP术语

          以下讨论都基于SpringAOP

          通知(Advice)

          切面的工作被称为通知。也就是定义了切面的要做什么,以及何时做的问题。下面是Spring切面有5种类型的通知,以及相对应的在SpringBoot中的五个注解

          • 前置通知(Before):方法调用之前,对应@Before

          • 后置通知(After):方法调用之后(不关心方法输出是什么)对应@After

          • 返回通知(After-returning):目标方法成功执行之后,对应@AfterReturning

          • 异常通知(After-throwing):目标方法抛出异常之后,对应@AfterThrowing

          • 环绕通知(Around):方法调用之前和之后,对应@Around

          • 后置通知和返回通知的区别

          后置通知应用时机在返回通知之后,任何情况下都会应用,而返回通知只有方法正常执行

          正常返回后执行

          • 环绕通知与其它通知的区别

          不同于其它的通知,环绕通知有目标方法的执行权,能够控制目标方法是否执行。而其它的通知更多是对目标方法的一个增强,无法影响目标方法的执行

          以上两点,我们下面会通过一个例子更好的体会其中的差异。

          连接点(Join point)

          程序中那些我们想要应用通知的地方,就是连接点。这个点可以是我们调用方法时、抛出异常时或甚至是修改某一个字段的时候。切面代码(通知)可以通过这些点插入到应用的正常流程中,是原本的功能增添新的行为。

          切点(Pointcut)

          我们的应用程序可能会有很多个连接点需要我们应用通知,所以我们有必要把连接点的分类汇总,抽象出相同的特点,好让正确的切面切入到正确的地方去,各司其职,而不是切入所有的连接点。切点定义 了一个切面需要在哪里进行切入。是一堆具有特定切面切入需求的连接点的共性抽象。

          我们通常通过明确类和方法名、或者匹配正则表达式的方式来指定切点

          连接点和切点的区别

          切点是对的拥有相同特点的连接点集合的一个抽象。切点定义了连接点的特性,是一个描述性的归纳,由于在SpringAOP中切点的切入级别是方法,所以那些符合切点定义的方法,都是一个连接点,连接点是切点的具象表现。

          切面(Aspect)

          切面是通知和切点的结合。通知和切点共同定了切面的全部内容——它想要干什么,在何时何地完成功能。

          引入(Introduction)

          引入能够让我们在不修改原有类的代码的基础上,添加生的方法或属性。

          织入(Weaving)

          织入是把切面应用到目标对象并创建新的代理对象的过程。

          SpringAOP

          SpringAOP的特点

          SpringAOP是通过动态代理实现的。不同于AspectJ等其他aop框架,SpringAOP只支持方法级别的切点,不支持类构造器或字段级别的切点。因此只能拦截方法进行通知,而在对象创建或对象变量的修改时,都无法应用通知

          SpringBoot集成SpringAOP

          SpringBoot引入AOP依赖后,spring.aop.auto属性默认是开启的,也就是说只要引入了AOP依赖后,默认已经增加了@EnableAspectJAutoProxy

          Spring AOP的概念及用法

          现在我们来设想一个场景:我有若干个业务场景,每个场景对应一个服务,有些服务需要我在调服务的时候,打印开始和结束信息,并输入服务处理的时长。

          - 依赖引入

          首先在项目中引入SpringAOP的依赖:

          <!--引入AOP依赖--><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId></dependency>
          - 创建注解

          创建一个自定义注解@ApiLog,用boolean类型的字段来决定是否开启日志输入功能,代码如下:

          @Target({ElementType.TYPE,ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)@Documentedpublic@interfaceApiLog{booleanisOn()defaulttrue;}
          - 定义切面

          定义一个切面类MyAspect.java,代码如下

          @Aspect@ComponentpublicclassMyAspect{@Pointcut("execution(*com.acelin.hello.study.springTest.aop.AspectController.*(..))")privatevoidonePointcut(){}@Around("onePointcut()")publicObjectaround(ProceedingJoinPointpoint)throwsThrowable{Signaturesignature=point.getSignature();MethodSignaturemethodSignature=(MethodSignature)signature;Methodmethod=methodSignature.getMethod();/*获取方法上的注解*/ApiLogapiLog=method.getAnnotation(ApiLog.class);if(apiLog!=null&&apiLog.isOn()){System.out.println("ApiLogison!");}Object[]objects=point.getArgs();StringbussinessName=(String)objects[0];System.out.println("--"+bussinessName+"start--");longbegin=System.currentTimeMillis();Objectobject=point.proceed();longend=System.currentTimeMillis();System.out.println("--"+bussinessName+"end耗时"+(end-begin)+"ms--");returnobject;}}
          - 设置切点
          @Pointcut("execution(*com.acelin.hello.study.springTest.aop.AspectController.*(..))")privatevoidonePointcut(){}

          @Pointcut注解表示声明一个切点,然后我们要配置execution指示器,它用来匹配所有符合条件的连接点。已上面的指示器配置* com.acelin.hello.study.springTest.aop.AspectController.*(..)为例,分析一下指示器的写法

          • 第一个*号:表示的不关心方法的返回值类型

          • 中间的com.acelin.hello.study.springTest.aop.AspectController.*指定方法的特点

          • 第二个*号:表示AspectController类下的任意方法

          • (..):表示不关心参数个数和类型

          - 业务接口编写

          我们写一个简单的接口,然后注释我们自定义的注解,开启相关日志的输出。

          @RestController@RequestMapping("/aop")publicclassAspectControllerimplementsIAspect{@RequestMapping("/test/1/{businessName}")@ApiLog(isOn=true)publicvoidtestAop(@PathVariable("businessName")StringbusinessName){System.out.println("Thisisacontrolleraboutaoptest!");}}
          - 测试

          测试结果如下:

          ApiLog is on!
          -- 业务1 start --
          This is a controller about aop test!
          -- 业务1 end 耗时5ms --

          通知时机

          以上我们通过一个简单的例子,初步体验了aop的魅力。接下来我们来讨论以下各类通知的特点以及应用时机问题。我们对的上面的切面类进行了修改,加上所有类型的通知。

          @Aspect@ComponentpublicclassMyAspect{@Pointcut("execution(*com.acelin.hello.study.springTest.aop.AspectController.*(..))")privatevoidonePointcut(){}@Before("onePointcut()")publicvoidbefore(){System.out.println("--before");}@After("onePointcut()")publicvoidafter(JoinPointjoinPoint)throwsNoSuchMethodException{System.out.println("--after");}@AfterThrowing("onePointcut()")publicvoidafterThrowing(){System.out.println("--afterThrowing");}@AfterReturning("onePointcut()")publicvoidafterReturning(){System.out.println("--afterReturning");}@Around("onePointcut()")publicObjectaround(ProceedingJoinPointpoint)throwsThrowable{Signaturesignature=point.getSignature();MethodSignaturemethodSignature=(MethodSignature)signature;Methodmethod=methodSignature.getMethod();/*获取方法上的注解*/ApiLogapiLog=method.getAnnotation(ApiLog.class);if(apiLog!=null&&apiLog.isOn()){System.out.println("ApiLogison!");}Object[]objects=point.getArgs();StringbussinessName=(String)objects[0];System.out.println("--"+bussinessName+"start");longbegin=System.currentTimeMillis();Objectobject=point.proceed();longend=System.currentTimeMillis();System.out.println("--"+bussinessName+"end耗时"+(end-begin)+"ms");returnobject;}}

          然后修改接口,怎加业务名称的一个长度判断,如果名称太长,抛出异常,来模拟方法执行过程中法僧异常

          @RequestMapping("/test/1/{businessName}")@ApiLogpublicvoidtestAop(@PathVariable("businessName")StringbusinessName){System.out.println("Thisisacontrolleraboutaoptest!");if(businessName.length()>4){thrownewRuntimeException("业务名称太长!");}}
          - 正常情况

          来看一下当方法正常执行的情况下,前置通知、后置通知、返回通知和环绕通知的应用时机。

          再次调用测试接口,可看到如下结果:

          ApiLog is on!
          -- 业务 start
          -- before
          This is a controller about aop test!
          -- afterReturning
          -- after
          -- 业务 end 耗时6ms

          - 异常情况

          然后我们修改业务名称为,使其超过规定长度导致触发异常抛出,结果如下:

          ApiLog is on!
          -- 超级复杂的业务 start
          -- before
          This is a controller about aop test!
          -- afterThrowing
          -- after

          java.lang.RuntimeException: 业务名称太长!

          at com.acelin.hello.study.springTest.aop.AspectController.testAop(AspectController.java:16)

          从上面结果可以看出的,前置通知应用于目标方法执行前,然后当方法正常执行时,会在方法结束前执行返回通知,如果发生异常,执行异常通知,返回通知不执行(因为方法已经中断,不正常返回),而后置通知的是不管方法执行情况,都会在方法结束后执行。环绕通知则包裹着以上所有流程。

           </div> <div class="zixun-tj-product adv-bottom"></div> </div> </div> <div class="prve-next-news">
          本文:Spring AOP的概念及用法的详细内容,希望对您有所帮助,信息来源于网络。
          上一篇:C++归并排序的实现方法下一篇:

          3 人围观 / 0 条评论 ↓快速评论↓

          (必须)

          (必须,保密)

          阿狸1 阿狸2 阿狸3 阿狸4 阿狸5 阿狸6 阿狸7 阿狸8 阿狸9 阿狸10 阿狸11 阿狸12 阿狸13 阿狸14 阿狸15 阿狸16 阿狸17 阿狸18