集成SpringAOP
1,添加SpringAOP相关的依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.1.12.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.9</version>
</dependency>
上述依赖中 aspectjweaver 为SpringAOP的依赖
2,刷新依赖,并检查artifact,具体操作查看《【徒手搭建SSM开发环境三】集成Spring(上)——集成SpringIOC》4-5部分内容,此处不再截图演示
3,在com.ssm.demo包下创建common代码包,此包下用于存放项目公用的类
4,在com.ssm.demo.common包下创建advice包,用于存放公用的AOP相关的代码
5,在com.ssm.demo.common.advice包下,新建两个类:ControllersExecutionAdvice和ServiceExecutionAdvice,并都实现MethodInterceptor接口,这两个类分别用于对controller和service的方法添加横切逻辑代码
6 在 ControllersExecutionAdvice类和ServiceExecutionAdvice类的invoke方法中加入环绕通知横切逻辑,使用形参invocation的proceed()实现对原方法的调用
7,在applicationContext.xml引入AOP所需要的标签定义文件
8,在applicationContext.xml对ControllersExecutionAdvice和ServiceExecutionAdvice这两个通知类进行注入,并且对其切面进行注册
9,在单元测试类SpringTest中创建单元测试方法SpringAOPTest,并在方法中添加代码,首先加载Spring配置文件,并据此文件创建Spring容器上下文对象,然后获取ProductController对象,最后调用ProductController对象的方法
10,运行单元测试类SpringTest的SpringAOPTest方法,如果ControllersExecutionAdvice和ServiceExecutionAdvice两个类中的环绕通知方法的打印语句都可正常执行,则表明SpringAOP集成成功
11,注解方式AOP测试
11.1 在com.ssm.demo.common.advice包下创建注解方式AOP切面类AnnotationExecutionAdvice,并在该类定义上添加@Component注解和@Aspect,@Component用于将该类的对象交给SpringIOC管理,@Aspect注解表明该类时一个切面定义类
11.2 在AnnotationExecutionAdvice类中添加切面声明方法pointcut,方法名称任意取即可,只要是public就行,同时在该方法定义上通过@Pointcut声明切面
11.3 在AnnotationExecutionAdvice中添加前置通知方法(切面执行前执行)before,返回通知方法(切面执行成功后执行)afterReturning,错误通知方法(切面出错时执行)afterThrowing,后置通知方法(切面执行完成后执行,不管切面是否执行成功,都会执行该方法)after,最后在这些通知方法上分别使用@Before,@AfterReturning,@AfterThrowing,@After注解与切面定义方法进行关联
11.4 注释掉第8步在applicationContext.xml中添加的AOP注解的配置,这里也可以不注释,注解和xml方式同时使用,但是为了调试输出的清晰可读,此处进行了注释。最后,还需要在applicationContext.xml中开启AOP注解驱动,即加入配置: <aop:aspectj-autoproxy />
11.5 运行单元测试类SpringTest的SpringAOPTest方法,在开发工具IDEA的控制台能够成功打印注解切面类中横切逻辑代码中的打印输出内容,则注解方式AOP调试成功
11.6 在ProductServiceImpl类的queryAll方法中加入异常代码后,再次执行单元测试类SpringTest的SpringAOPTest方法
从上述运行结果可以看出after通知方法不管切面是否出错都会执行,afterThrowing通知方法在切面出错时执行,afterReturning通知方法在切面成功执行后执行,并且after通知方法会在afterReturning通知方法和afterThrowing通知方法执行之前执行,猜测after通知方法是使用finally技术实现的。
11.7 在AnnotationExecutionAdvice中添加环绕通知方法around,并使用@Around注解与切面声明方法关联
11.8 再次执行单元测试类SpringTest的SpringAOPTest方法
11.9 在ProductServiceImpl类的queryAll方法中删除异常代码,同时在applicationContext.xml去掉xml方式AOP的注释,
然后在单元测试类SpringTest的SpringAOPTest方法中获取IProductService类型对象,最后执行单元测试类SpringTest的SpringAOPTest方法
由上述执行结果可以看出,xml方式AOP处于调用栈的最外层,而注解方式的处于调用栈的内层,同时注解方式AOP中,环绕通知方法总是先于其他通知方法执行
11.10 上述在对11.9中的执行结果的分析中,提到了调用栈的内层和外层,关于这里的内层和外层,目前猜测Spring是通过责任链模式实现的,目前还不能确定。大致推测的实现流程为:
xml方式AOP与注解方式AOP同时存在的场景下,实际上会为委托对象产生两层代理对象,即先为委托对象产生一个注解方式的AOP代理对象,再以这个注解方式AOP代理对象做为委托对象,对他产生一个xml方式AOP代理对象,这样,注解方式AOP代理对象处于整个代理链的内层,xml方式AOP代理对象就处于代理链的外层,这种在代理对象上继续产生代理对象的开发方式就是责任链模式。(此处结论等研究过源代码之后再进行补充和纠正)
11.11 在11.10中提到了代理对象,此处主要对代理对象做一下解释和调试,在SpringAOP中,产生代理对象的方式由两种,一种是JDK提供的动态代理,这种情况下要求委托对象的类要实现接口,另外一种方式是借助第三方工具cglib提供的方法增强对象实现的,这种情况下不需要委托对象的类实现接口,SpringAOP在实现过程中会依据实际情况自动选择合适的方式生成代理对象。在演示项目中,ProductController类未实现接口,而ProductServiceImpl类实现了IProductService,下边调试分别从SpringIOC容器中获取ProductController对象和ProductServiceImpl对象,并通过debug模式观察获取到对象的实际类型演示此结论。
(1)在SpringTest的SpringAOPTest方法中获取ProductController对象,并添加断点后运行单元测试方法
从执行结果能看出,此时获取到的是使用cglib技术获取到的ProductController对象的方法增强对象
(2)在SpringTest的SpringAOPTest方法中获取IProductService类型的对象,并添加断点后运行单元测试方法
从运行结果来看,测试使用的是jdk提供的动态代理方式产生的代理对象
11.12 如果项目中需要强制统一使用cglib技术实现代理对象的获取,则需要在spring配置文件applicationContext.xml中的AOP注解驱动配置上加入属性配置 proxy-target-class=“true”
有以上运行结果看出,当加入proxy-target-class="true"配置后,从SpingIOC容器中获取到的IProductService对象是通过cglib技术生成的代理对象
至此,SpringAOP集成及调试完成