SpringAOP看这篇就够了

旧城等待, 2023-02-16 06:59 164阅读 0赞

文章目录

      • 一、理解AOP思想
      • 二、AspectJ和SpringAOP的区别与联系
        • 1.AspectJ框架
        • 2.SpringAOP
        • 3.总结
      • 三、AOP操作术语
      • 四、Spring AOP的使用
        • 1.XML方式
          • 1.创建用于拦截的Bean
          • 2.编写通知类
          • 3.编写XML配置文件AOP
          • 4.测试类
          • 5.执行结果
        • 2.注解方式
          • 1.引入AspectJ包
          • 2.创建用于拦截的Bean
          • 3.创建Advisor
          • 4.创建配置文件
          • 5.测试
          • 6.执行结果
        • 3.小结
        • 4.AOP execution表达式
      • 五、Spring AOP五种通知的执行顺序
        • 1.XML配置方式
          • (1)测试前置、后置、环绕通知、后置返回、异常—未抛出异常执行结果—目标方法有返回值
          • (2)测试前置、后置、环绕通知、后置返回、异常—未抛出异常执行结果—目标方法无返回值
          • (3)测试前置、后置、环绕通知、后置返回、异常—抛出异常执行结果—目标方法有返回值
          • (4)测试前置、后置、环绕通知、后置返回、异常—抛出异常执行结果—目标方法无返回值
        • 2.注解方式
          • (1)测试前置、后置、环绕通知、后置返回、异常—未抛出异常执行结果—目标方法有返回值
          • (2)测试前置、后置、环绕通知、后置返回、异常—未抛出异常执行结果—目标方法无返回值
          • (3)测试前置、后置、环绕通知、后置返回、异常—抛出异常执行结果—目标方法有返回值
          • (4)测试前置、后置、环绕通知、后置返回、异常—抛出异常执行结果—目标方法无返回值
        • 3.小结

一、理解AOP思想

我们知道在面向对象OOP编程存在一些弊端,当需要为多个不具有继承关系的对象引入同一个公共行为时,例如日志、安全监测等,我们只有在每个对象里引入公共行为,这样程序中就产生了大量的重复代码,所以有了面向对象编程的补充,面向切面编程(AOP)

使用AOP技术,可以将一些系统性相关的编程工作或重复代码,独立提取出来独立实现,然后通过切面切入程序中,可以让开发者更专注于业务逻辑的实现,减少了大量重复代码,提高了效率和可维护性。

AOP(Aspect Oriented Programming),它是面向对象编程的一种补充,主要应用于处理一些具有横切性质的系统级服务,如日志收集、事务管理、安全检查、缓存、对象池管理等。

轻松理解AOP思想

AOP实现的关键就在于AOP框架自动创建的AOP代理,AOP代理则可分为 静态代理动态代理 两大类,其中静态代理是指使用AOP框架提供的命令进行编译,从而在编译阶段就可生成 AOP 代理类,因此也称为 编译时增强;而动态代理则在运行时借助于JDK动态代理、CGLIB等在内存中 “临时” 生成AOP动态代理类,因此也被称为 运行时增强

二、AspectJ和SpringAOP的区别与联系

在提到AOP时我们总会想到SpringAOP,而在提到SpringAOP时又总会跟AspectJ扯上关系,那么这两者之间究竟有什么关系呢?

1.AspectJ框架

AspectJ是一个面向切面的框架,它扩展了Java语言,定义了AOP语法,能够在编译器提供代码的织入,所以它有一个专门的编译器用来生成遵守字节码规范的Class文件。

AspectJ属于静态织入,通过修改代码来实现,有如下几个织入的时机:

​1.编译期织入(Compile-time weaving)
如类 A 使用 AspectJ 添加了一个属性,类 B 引用了它,这个场景就需要编译期的时候就进行织入,否则没法编译类 B。

2.编译后织入(Post-compile weaving)
也就是已经生成了.class 文件,或已经打成 jar 包了,这种情况我们需要增强处理的话,就要用到编译后织入。

3.类加载后织入(Load-time weaving)
指的是在加载类的时候进行织入,要实现这个时期的织入,有几种常见的方法。

  • 1)自定义类加载器来干这个,这个应该是最容易想到的办法,在被织入类加载到 JVM 前去对它进行加载,这样就可以在加载的时候定义行为了。
  • 2)在 JVM 启动的时候指定 AspectJ 提供的 agent:
    -javaagent:xxx/xxx/aspectjweaver.jar。

2.SpringAOP

众所周知,Spring两大特色或者说是核心即 IoCAOP,AspectJ框架提供了一套完整的AOP解决方案,而SpringAOP的目的并不是为了提供最完整的AOP实现 (虽然SpringAOP具有相当的能力),而是要帮助解决企业应用中的常见问题,提供一个AOP实现与Spring IOC之间的紧密集成,能够处理业务中的横切关注点。

1.SpringAOP 是基于动态代理来实现横切的,代理的方式提供了两种:

  • 基于JDK的动态代理
    必须是面向接口的,只有实现了具体接口的类才能生成代理对象。
  • 基于CGLIB动态代理
    对于没有实现了接口的类,也可以产生代理,产生这个类的子类的方式。

2.SpringAop需要依赖IOC容器来管理,并且只能作用于Spring容器,使用纯Java代码实现。

3.在性能上,由于SpringAop是基于动态代理来实现的,在容器启动时需要生成代理实例,在方法调用上也会增加栈的深度,使得性能不如AspectJ那么好。

在这里插入图片描述

3.总结

总的来说,两者都是AOP思想的一种实现,但是在面向切面编程关注的点不同:AspectJ 可以做 Spring AOP 干不了的事情,它是AOP编程的完全解决方案;Spring AOP则致力于解决企业级开发中最普遍的AOP(方法织入),而不是成为像 AspectJ 一样的AOP方案。因为AspectJ 在实际运行之前就完成了织入,所以说它生成的类是没有额外运行时开销的。

相同点和区别我们都知道了,而两者之间有什么联系呢,为什么在使用Spring AOP的时候还需要(非必须)导入AspectJ的包呢?

曾经以为 AspectJ 是 Spring AOP 一部分,但其实是Spring通过集成AspectJ实现了以注解的方式定义切面,这样大大减少了配置文件的工作量,所以使用注解方式需要导入包。

  1. <dependency>
  2. <groupId>org.aspectj</groupId>
  3. <artifactId>aspectjweaver</artifactId>
  4. <version>1.9.5</version>
  5. </dependency>

使用了 @Aspect 来定义切面,使用 @Pointcut 来定义切入点,使用Advice来定义增强处理:@Before@AfterReturning@AfterThrowing@After@Around,这几个注解都是在org.aspectj.lang.annotation包中。虽然使用了Aspect的Annotation,但是并没有使用它的编译器和织入器。

此外,因为Java的反射机制无法获取方法参数名,Spring还需要利用轻量级的字节码处理框架asm (已集成在Spring Core模块中) 处理@AspectJ中所描述的方法参数名。

三、AOP操作术语

  • 1.通知(Advice):就是你想要的功能(切面要完成的功能),也就是上文说的日志、事务、安全等。先定义好,然后在想用的地方使用。

    Spring切面可以应用五种通知类型:

  1. 前置通知(Before):在目标方法被调用之前调用通知功能。
  2. 后置通知(After)(最终通知):在方法返回前执行通知,就算目标方法抛出异常,后置通知也会执行,即目标方法无论执行成功与否最终都会执行。
  3. 后置返回通知(After-Returning):在方法执行return后执行的,这个是不可能可以修改方法的返回值的,这里需要注意和后置通知不同,目标方法抛出异常,后置返回通知不会执行,即目标方法成功执行完毕并return后才会执行。
  4. 异常通知(After-throwing):在目标方法抛出异常后调用通知。
  5. 环绕通知(Around):通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为。

    • 2.连接点(JoinPoint):程序执行的某个特定位置:如类开始初始化前、类初始化后、类某个方法调用前、调用后、方法抛出异常后。一个类或一段程序代码拥有一些具有边界性质的特定点,这些特定点就称为连接点。

    简单来说,就是Spring允许你使用通知的地方,基本每个方法的前后(两者都有也行),或抛出异常时都可以是连接点,Spring只支持方法连接点,其它如AspectJ还可以让你在构造器或属性注入时都行,只要记住,和方法有关的前前后后(抛出异常),都是连接点。

    • 3.切入点(PointCut):上面说的连接点的基础上,来定义切入点,你的一个类里有15个方法,那就有几十个连接点了,但是你并不想在所有方法附近都使用通知(使用称为“织入”,下文有详细说明),你只想让其中的几个,在调用这几个方法之前/之后/抛出异常时干点什么,那么就用切点来定义这几个方法,让切点来筛选连接点,选中那几个你想要增强的方法。
    • 4.切面(Aspect):切面是通知和切入点的结合。其实没连接点什么事,连接点就是为了让我们好理解切点,明白这个概念就可以。通知说明了干什么和什么时候干(什么时候通过方法名中的before/after/around就能知道),而切入点说明了在哪干(指定到底是哪个方法),这就是一个完整切面定义。
    • 5.目标对象(Target):通知逻辑的织入目标类,如果没有AOP,目标业务类需要自己实现所有业务逻辑,而在AOP的帮助下,目标业务类只实现那些非横切逻辑的程序逻辑,而性能监视和事务管理等这些横切逻辑则可以使用AOP动态织入到特定的连接点上。
    • 6.引介(Introduction):是一种特殊的通知,在不修改源代码的前提下,它可以在运行期为类动态地添加一些属性和方法。
    • 7.织入(weaving):织入是将通知添加到目标类具体连接点上的过程,AOP像一台织布机,将目标类、通知或引介通过AOP这台织布机
      天衣无缝的编制到一起,AOP有三种织入的方式:
      1)编译器织入,这要求使用特殊的Java编译器。
      2)类装载期织入,这要求使用特殊的类装载器。
      3)动态代理织入,在运行期为目标类添加通知生成子类的方式。

    把切面应用到目标对象来创建新的代理对象的过程,Spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入。

    • 8.代理(proxy):一个类被织入通知后,就产出了一个结果类,它是融合了原类和通知逻辑的代理类。根据不同的代理方式,代理类既可能是和原类具有相同接口的类,也可能是原类的子类,所以我们可以采用调用原类相同的方式调用代理类。

四、Spring AOP的使用

1.XML方式

1.创建用于拦截的Bean
  1. public class TestBean {
  2. private String message="test bean";
  3. public String getMessage() {
  4. return message;
  5. }
  6. public void setMessage(String message) {
  7. this.message = message;
  8. }
  9. public void test(){
  10. System.out.println(this.getMessage());
  11. }
  12. }
2.编写通知类
  1. public class TestAdvisor {
  2. /**
  3. * 前置通知
  4. */
  5. public void beforeAdvisor(){
  6. System.out.println("beforeAdvisor");
  7. }
  8. /**
  9. * 后置通知
  10. */
  11. public void afterAdvisor(){
  12. System.out.println("afterAdvisor");
  13. }
  14. /**
  15. * 环绕通知
  16. * @param joinPoint
  17. */
  18. public void aroundAdvisor(ProceedingJoinPoint joinPoint){
  19. System.out.println("aroundAdvisor.....before");
  20. Object o = null;
  21. try{
  22. //执行proceed方法的作用就是让目标方法执行,环绕通知=前置+目标方法执行+后置通知
  23. o = joinPoint.proceed();
  24. }catch(Throwable e){
  25. e.printStackTrace();
  26. }
  27. System.out.println("aroundAdvisor.....after");
  28. }
  29. }
3.编写XML配置文件AOP
  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:context="http://www.springframework.org/schema/context"
  5. xmlns:aop="http://www.springframework.org/schema/aop"
  6. xsi:schemaLocation="http://www.springframework.org/schema/beans
  7. http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
  8. <bean id="test" class="com.xmlaop.TestBean">
  9. <property name="message" value="测试XML配置方式实现AOP"/>
  10. </bean>
  11. <!-- 声明增强方法所在的bean -->
  12. <bean id="theAdvisor" class="com.xmlaop.TestAdvisor"/>
  13. <!-- 配置切面 -->
  14. <aop:config>
  15. <!--定义切入点 -->
  16. <aop:pointcut id="pointcut" expression="execution(* *.test(..))"/>
  17. <!--引用包含增强方法的Bean -->
  18. <aop:aspect ref="theAdvisor">
  19. <!--前置通知-->
  20. <aop:before method="beforeAdvisor" pointcut-ref="pointcut"/>
  21. <!--后置通知-->
  22. <aop:after method="afterAdvisor" pointcut-ref="pointcut"/>
  23. <!--环绕通知-->
  24. <aop:around method="aroundAdvisor" pointcut-ref="pointcut" arg-names="joinPoint"/>
  25. </aop:aspect>
  26. </aop:config>
  27. </beans>
4.测试类
  1. public class TestAOP {
  2. public static void main(String[] args) {
  3. ApplicationContext context=new ClassPathXmlApplicationContext("aspectTest.xml");
  4. TestBean testBean=(TestBean)context.getBean("test");
  5. testBean.test();
  6. }
  7. }
5.执行结果

在这里插入图片描述

2.注解方式

1.引入AspectJ包
  1. <!-- aspectjweaver -->
  2. <dependency>
  3. <groupId>org.aspectj</groupId>
  4. <artifactId>aspectjweaver</artifactId>
  5. <version>${aspectj.version}</version>
  6. </dependency>
2.创建用于拦截的Bean
  1. public class TestBean {
  2. private String message = "test bean";
  3. public String getMessage() {
  4. return message;
  5. }
  6. public void setMessage(String message) {
  7. this.message = message;
  8. }
  9. public void test(){
  10. System.out.println(this.message);
  11. }
  12. }
3.创建Advisor

在AspectJTest类中,我们要做的就是在所有类的test方法执行前在控制台beforeTest。而在所有类的test方法执行后打印afterTest,同时又使用环绕的方式在所有类的方法执行前后在此分别打印before1和after1,以下是AspectJTest的代码:

  1. @Aspect
  2. public class AspectJTest {
  3. @Pointcut("execution(* *.test(..))")
  4. public void test(){
  5. }
  6. @Before("test()")
  7. public void beforeTest(){
  8. System.out.println("beforeTest");
  9. }
  10. @Around("test()")
  11. public Object aroundTest(ProceedingJoinPoint p){
  12. System.out.println("around.....before");
  13. Object o = null;
  14. try{
  15. o = p.proceed();
  16. }catch(Throwable e){
  17. e.printStackTrace();
  18. }
  19. System.out.println("around.....after");
  20. return o;
  21. }
  22. @After("test()")
  23. public void afterTest()
  24. {
  25. System.out.println("afterTest");
  26. }
  27. }
4.创建配置文件

要在Spring中开启AOP功能,,还需要在配置文件中作如下声明:

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:context="http://www.springframework.org/schema/context"
  5. xmlns:aop="http://www.springframework.org/schema/aop"
  6. xsi:schemaLocation="http://www.springframework.org/schema/beans
  7. http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
  8. <aop:aspectj-autoproxy/>
  9. <bean id="test" class="com.yhl.myspring.demo.aop.TestBean">
  10. <property name="message" value="这是一个苦逼的程序员"/>
  11. </bean>
  12. <bean id="aspect" class="com.yhl.myspring.demo.aop.AspectJTest"/>
  13. </beans>
5.测试
  1. public class Test {
  2. public static void main(String[] args) {
  3. ApplicationContext bf = new ClassPathXmlApplicationContext("aspectTest.xml");
  4. TestBean bean = (TestBean)bf.getBean("test");
  5. bean.test();
  6. }
  7. }
6.执行结果

在这里插入图片描述

看到这里细心的你可能注意到了两种方式同样都使用了前置、后置和环绕,但是通知的执行顺序却不一样,下文中我们再探讨。

这篇文章也很不错,可以做个参考。
SpringAOP的两种配置方式详细示例

3.小结

如果项目采用JDK5.0以上版本,可以考虑使用 @AspectJ 注解方式,减少配置的工作量;如果不愿意使用注解或项目采用的JDK版本较低而无法使用注解,则可以选择使用<aop:aspect>配合普通JavaBean的形式。

4.AOP execution表达式

先来看一个表达式

  1. execution(* com.sample.service.impl..*.*(..))

解释如下


































符号 含义
execution() 表达式的主体
第一个 表示返回值可以是任意类型
com.sample.service.impl AOP所切的服务的包名,即,我们的业务部分
包名后面的两个点 表示当前包及子包
第二个 表示类名,即所有类
.*(点点) 表示 任何方法名,括号表示参数,两个点表示任何参数类型

AspectJ中的exection表达式基本语法格式为:

在这里插入图片描述
除了返回类型模式、方法名模式和参数模式外,其它项都是可选的。

下面,我们给出各种使用execution()函数实例。

1)通过方法签名定义切点

  1. execution(public * *(..))

匹配所有目标类的public方法。第一个*代表返回类型,第二个*代表方法名,而..代表任意入参的方法;

  1. execution(* *To(..))

匹配目标类所有以To为后缀的方法。第一个*代表返回类型,而*To代表任意以To为后缀的方法;

2)通过类定义切点

  1. execution(* com.test.Waiter.*(..))

匹配Waiter的所有方法。第一个*代表返回任意类型,com.test.Waiter.* 代表Waiter类中的所有方法。

  1. execution(*com.test.Waiter+.*(..))

匹配Waiter类及其所有实现类的方法。

3)通过类包定义切点

在类名模式串中,*表示包下的所有类,而..表示包、子孙包下的所有类。

  1. execution(* com.test.*(..))

匹配com.test包下所有类的所有方法;

  1. execution(* com.test..*(..))

匹配com.test包、子孙包下所有类的所有方法,如com.test.daocom.test.service包下的所有类的所有方法都匹配。..出现在类名中时,后面必须跟*,表示包、子孙包下的所有类;

  1. execution(* com..*.*Dao.find*(..))

匹配包名前缀为com的任何包下类名后缀为Dao的方法,方法名必须以find为前缀。如com.test.UserDao.findByUserId()com.test.dao.TestDao#findAll)的方法都匹配切点。

4)通过方法入参定义切点

切点表达式中方法入参部分比较复杂,可以使用 *.. 通配符,其中 * 表示任意类型的参数,而 .. 表示任意类型参数且参数个数不限。

  1. execution(* joke(String,int))

匹配 joke(String,int) 方法,且joke()方法的第一个入参是String,第二个入参是int。如果方法中的入参类型是Java.lang包下的类,可以直接使用类名,否则必须使用全限定类名,如joke(java.util.List,int)。

  1. execution(* joke(String,*))

匹配目标类中的joke()方法,该方法第一个入参为String,第二个入参可以是任意类型,如joke(String s1,String s2)joke(String s1,double d2)都匹配,但joke(String s1,double d2,String s3)则不匹配;

  1. execution(* joke(String,..))

匹配目标类中的joke()方法,该方法第一个入参为String,.. 表示后面可以有任意个入参且入参类型不限,如 joke(String s1)joke(String s1,String s2)joke(String s1,double d2,Strings3)都匹配。

  1. execution(* joke(Object+))

匹配目标类中的joke()方法,方法拥有一个入参,且入参是Object类型或该类的子类。它匹配joke(String s1)和joke(Client c)。如果我们定义的切点是execution(*joke(Object)),则只匹配joke(Object object)而不匹配joke(String str)joke(Client c)

五、Spring AOP五种通知的执行顺序

这里分别用XML配置方式和注解方式对SpringAOP五种通知:

  • @Before前置通知
  • @After 后置通知
  • @Around 环绕通知
  • @AfterReturning 后置返回通知
  • @AfterThrowing 异常通知

在代码有无异常和目标方法有无返回值的情况做了测试,这里贴出执行结果并做一个总结。

1.XML配置方式

(1)测试前置、后置、环绕通知、后置返回、异常–未抛出异常执行结果–目标方法有返回值

执行结果

  1. beforeAdvisor
  2. 17:24:39.678 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'theAdvisor'
  3. aroundAdvisor.....before
  4. 测试XML配置方式实现AOP
  5. 17:24:39.700 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'theAdvisor'
  6. afterReturnindAdvisor
  7. aroundAdvisor.....after
  8. 17:24:39.700 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'theAdvisor'
  9. afterAdvisor

执行顺序
前置通知
环绕开始
目标方法执行
后置返回通知
环绕结束
后置通知

(2)测试前置、后置、环绕通知、后置返回、异常–未抛出异常执行结果–目标方法无返回值

执行结果

  1. beforeAdvisor
  2. 17:33:47.846 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'theAdvisor'
  3. aroundAdvisor.....before
  4. 测试XML配置方式实现AOP
  5. 17:33:47.868 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'theAdvisor'
  6. afterReturnindAdvisor
  7. aroundAdvisor.....after
  8. 17:33:47.868 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'theAdvisor'
  9. afterAdvisor

执行顺序:
前置通知
环绕开始
目标方法执行
后置返回通知
环绕结束
后置通知

(3)测试前置、后置、环绕通知、后置返回、异常–抛出异常执行结果–目标方法有返回值

执行结果

  1. beforeAdvisor
  2. 17:28:26.589 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'theAdvisor'
  3. aroundAdvisor.....before
  4. 测试XML配置方式实现AOP
  5. 17:28:26.651 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'theAdvisor'
  6. exceptionAdvisor
  7. java.lang.ArithmeticException: / by zero
  8. at com.xmlaop.TestBean.test(TestBean.java:22)
  9. at com.xmlaop.TestBean$$FastClassBySpringCGLIB$$4b9bd518.invoke(<generated>)
  10. aroundAdvisor.....after
  11. 17:28:26.654 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'theAdvisor'
  12. afterAdvisor
  13. at java.lang.reflect.Method.invoke(Method.java:498)
  14. at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:643)
  15. at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:632)

执行顺序:
前置通知
环绕开始
目标方法执行
目标方法报错,执行异常通知
环绕结束
后置通知

(4)测试前置、后置、环绕通知、后置返回、异常–抛出异常执行结果–目标方法无返回值

执行结果

  1. beforeAdvisor
  2. 17:36:14.319 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'theAdvisor'
  3. aroundAdvisor.....before
  4. 测试XML配置方式实现AOP
  5. 17:36:14.341 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'theAdvisor'
  6. exceptionAdvisor
  7. java.lang.ArithmeticException: / by zero
  8. at com.xmlaop.TestBean.test(TestBean.java:22)
  9. at com.xmlaop.TestBean$$FastClassBySpringCGLIB$$4b9bd518.invoke(<generated>)
  10. at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
  11. aroundAdvisor.....after
  12. 17:36:14.343 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'theAdvisor'
  13. afterAdvisor

执行顺序:
前置通知
环绕开始
目标方法执行
目标方法报错,执行异常通知
环绕结束
后置通知

2.注解方式

(1)测试前置、后置、环绕通知、后置返回、异常–未抛出异常执行结果–目标方法有返回值

执行结果

  1. around.....before
  2. beforeTest
  3. 这是一个苦逼的程序员
  4. around.....after
  5. afterTest
  6. afterReturnindAdvisor

执行顺序:
环绕开始
前置通知
目标方法执行
环绕结束
后置通知
返回通知

(2)测试前置、后置、环绕通知、后置返回、异常–未抛出异常执行结果–目标方法无返回值

执行结果

  1. around.....before
  2. beforeTest
  3. 这是一个苦逼的程序员
  4. around.....after
  5. afterTest
  6. afterReturnindAdvisor

执行顺序:
环绕开始
前置通知
目标方法执行
环绕结束
后置通知
返回通知

(3)测试前置、后置、环绕通知、后置返回、异常–抛出异常执行结果–目标方法有返回值

执行结果

  1. around.....before
  2. beforeTest
  3. 这是一个苦逼的程序员
  4. afterTest
  5. exceptionAdvisor==========/ by zero
  6. Exception in thread "main" java.lang.ArithmeticException: / by zero
  7. at com.aop.TestBean.test(TestBean.java:22)
  8. at com.aop.TestBean$$FastClassBySpringCGLIB$$f5e4153b.invoke(<generated>)

执行顺序:
环绕开始
前置通知
目标方法执行
后置通知
异常通知

(4)测试前置、后置、环绕通知、后置返回、异常–抛出异常执行结果–目标方法无返回值

执行结果

  1. around.....before
  2. beforeTest
  3. 这是一个苦逼的程序员
  4. afterTest
  5. exceptionAdvisor==========/ by zero
  6. Exception in thread "main" java.lang.ArithmeticException: / by zero
  7. at com.aop.TestBean

执行顺序:
环绕开始
前置通知
目标方法执行
后置通知
异常通知

3.小结

从执行结果来看,有以下几点
1.无论使用XML配置还是注解方式,目标方法执行有无异常,有无返回值,后置通知即@After都会执行。

2.无论使用XML配置还是注解方式,目标方法执行异常,后置返回通知@AfterReturning不会执行,这里需要注意的是,如果对异常进行了捕获,异常通知将不执行,而后置返回通知会执行。

3.在进行统一异常处理时,异常通知的异常类型级别必须大于或等于程序实际运行抛出的异常,不然捕获不到,异常通知也就不会执行。

4.XML配置方式和注解方式执行顺序还是存在差异。

XML配置方式执行顺序在正常情况下(即执行无异常,无论有无返回值)

  1. 前置通知
  2. 环绕开始
  3. 目标方法执行
  4. 后置返回通知
  5. 环绕结束
  6. 后置通知

XML配置方式执行顺序(执行异常未捕获,无论有无返回值)

  1. 前置通知
  2. 环绕开始
  3. 目标方法执行
  4. 目标方法报错,执行异常通知【与无异常相比,这里后置返回通知没有执行,执行了异常通知,其他无差别】
  5. 环绕结束
  6. 后置通知

注解方式通知执行顺序(执行无异常,无论是否有返回值)

  1. 环绕开始
  2. 前置通知
  3. 目标方法执行
  4. 环绕结束
  5. 后置通知
  6. 返回通知

注解方式通知执行顺序(执行出现异常未捕获,无论是否有返回值)

  1. 环绕开始
  2. 前置通知
  3. 目标方法执行
  4. 后置通知
  5. 异常通知

参考文章

execution表达式https://blog.csdn.net/zz210891470/article/details/54175107

AOP 使用
https://www.cnblogs.com/java-chen-hao/p/11589531.html

轻松理解 AOP思想 https://www.cnblogs.com/Wolfmanlq/p/6036019.html

面向切面编程 AOP https://www.zhihu.com/question/24863332

关于Spring AOP你该知晓的一切
https://zhuanlan.zhihu.com/p/25522841

整理不易,对你有帮助的话点个赞关注下谢谢。

关注我的微信公众号【程序媛琬淇】,专注分享Java干货,给你意想不到的收获。

在这里插入图片描述

发表评论

表情:
评论列表 (有 0 条评论,164人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Java String,

    String,是Java中最重要的类。这句肯定的推断不是Java之父詹姆斯·高斯林说的,而是沉默王二说的,因此你不必怀疑它的准确性。 关于字符串,有很多的面试题,但我总觉得理