Spring AOP 前置通知、返回通知、异常通知、后置通知、环绕通知

亦凉 2022-01-10 03:27 501阅读 0赞

前置技能:

Java、JDBC、Spring IOC快速入门 、 Spring IOC 注解方式注入

什么是AOP:

面向切口编程(Aspect Oriented Programming),AOP是OOP的延续,是Spring框架的一个重要内容。
AOP利用称为”横切”的技术,剖解开封装的对象内部,把多个类的公共行为封装到一个可重用模块中,便于减少重复代码,降低模块之间的耦合度,AOP符合开闭原则,提高了代码的可拓展性。

AOP概念名词:

通知(advice)
在已存在的业务方法的前、后、异常、最终处加入的方法。

切面(aspect)
通知advice 方法所在的类,一个 aspect 类中可以有多个通知方法。

连接点(joinPoint)
已存在的业务方法。

切入点(pointCut)
joinPoint的集合。(为要添加advice的方法划定范围。)

目标对象(target)``
joinPoint方法所在的对象。

织入(Weave)``
advice方法放在joinPoint的前、后、异常、最终处。(只有织入后,advice才会有效。)

ps.温馨提示:你可以先看完Getting Started,再回来理解这些。

Getting Started

0、目录

aspect切面类
service服务层
在这里插入图片描述

1、配置xml

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
  3. <!-- 一大堆都是标签头,这里只启动了两个注解而已。-->
  4. <!-- 启动基本的注解Controller,Service,Repository,Component,Autowired,Resource,Qulifer -->
  5. <context:component-scan base-package="com.aop"></context:component-scan>
  6. <!-- 开启spring-aop的注解识别-->
  7. <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
  8. </beans>

一大堆都是标签头,这里只启动了两个注解而已。

2、将目标对象的类纳入容器

  1. public interface IService {
  2. String update(String msg);
  3. }

业务方法update就是连接点 joinPoint ,我们要在它的前后插入其他的方法(通知 advice)。

  1. @Service
  2. public class IServiceImpl implements IService {
  3. @Override
  4. public String update(int uid, String msg) {
  5. System.out.println("serviceimpl运行了");
  6. return "更新了数据:"+msg;
  7. }
  8. }

3、将切面类纳入容器

Componen纳入容器
Aspect 配置为切面类

  1. @Component
  2. @Aspect
  3. public class DemoAspect {
  4. //配置切入点
  5. @Pointcut("execution(* com.aop.service.*.*(..))")
  6. public void myPointCut() { }
  7. //配置前置通知
  8. @Before("myPointCut()")
  9. public void myBefore() {
  10. System.out.println("——————前置通知————");
  11. }
3.1、解读@Pointcut

此处Pointcut注解有两个参数,result是返回值的类型 , range是指定范围来配置通知的类,两个参数用空格隔开。

  1. @Pointcut("execution("result range")

对照
在演示代码中,我是用的 * com.aop.service.*.*(..)),意义如下。

  1. @Pointcut("execution(任意返回值 com包.aop包.service包.所有类.所有方法(任意参数))")

其中返回值、包、类、方法的通配符为 *
参数的通配符为 ..

3.2、解读@Before

此处Before有一个注解,是指定此通知的切入点。
即:myBefore()通知的切入点为myPointCut()

  1. @Before("myPointCut()")
  2. public void myBefore() {

4、阶段测试

  1. public class Test {
  2. public static void main(String[] args) {
  3. ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");
  4. IService ser=ac.getBean(IService.class);
  5. //update方法只是返回了这段字符串,所以并不会打印。
  6. ser.update("测试字符串");

运行结果
service包下的所有类 的所有方法,都会被添加上此前置通知。
在这里插入图片描述
可以通过修改Pointcut的参数来选定范围。
例:只有IServiceImpl类的方法添加通知。

  1. @Pointcut("* com.aop.service.IServiceImpl.*(..))")

5、返回通知、异常通知、后置通知

5.1、返回通知

只要连接点正常返回,就会执行返回通知。

  1. @AfterReturning(pointcut="myPointCut()",returning="obj")
  2. public void myAfterReturning(JoinPoint point,Object obj) {
  3. System.out.println("——————返回通知————");
  4. //获取连接点方法传入的实参
  5. Object[] args=point.getArgs();
  6. //获取连接点方法的方法名
  7. String methodName=point.getSignature().getName();
  8. //获取连接点方法所在的对象
  9. Object targetObj=point.getTarget();
  10. String targetClassName=point.getClass().getName();
  11. }

通过JoinPoint对象 可以获取连接点接收的实参,连接点的方法名,连接点所在的对象。

5.1.1、解读@AfterReturning

此处AfterReturning注解有两个参数,pointcut切入点 , returning为连接点的返回值,命名须与myAfterReturning()方法的Object参数一致。

  1. @AfterReturning(pointcut="myPointCut()",returning="objeeeee")
  2. //注意那串eeeee了吗,那是为了引起你的注意,告诉你这两个命名须一致。
  3. public void myAfterReturning(JoinPoint point,Object objeeeee) {
5.2、异常通知

可以得到连接点信息和异常信息。

  1. @AfterThrowing(pointcut="myPointCut()",throwing="eeeee")
  2. public void myAfterThrowing(JoinPoint point,Exception eeeee) {
  3. //注意那串eeeee了吗,那是为了引起你的注意,告诉你这两个命名须一致。
  4. System.out.println("——————异常通知————"+eeeee.getMessage());
  5. }
5.2.1、解读@AfterThrowing

此处AfterThrowing注解有两个参数,pointcut切入点 , throwing为连接点的返回值,命名须与AfterThrowing()方法的Exception 参数一致。

5.3、最终通知

执行顺序在异常通知和后置通知 之前,得不到返回值和异常信息。

  1. @After("myPointCut()")
  2. public void myAfter(JoinPoint point) {
  3. System.out.println("——————最终通知————"+point.getSignature().getName());
  4. }
5.4、阶段测试

运行结果

这个顺序很迷,最终通知在返回通知的前面,等博主有空学习了Spring的源码再回来填坑。
在这里插入图片描述

6、环绕通知

@Around
环绕通知就是,包括以上以上所有通知。
环绕通知很强大,但不常用,通常我们只需要用到其中一两项通知。
环绕通知的返回值必须是Object,形参必须是ProceedingJoingPoint

  1. @Component
  2. @Aspect
  3. public class DemoAspect {
  4. //配置切入点
  5. @Pointcut("execution(* com.aop.service.*.*(..))")
  6. public void myPointCut() { }
  7. //环绕通知
  8. @Around("myPointCut()")
  9. public Object aroundAdvice(ProceedingJoinPoint proceeding) {
  10. //和JoinPoint一样,ProceedingJoinPoint也可以获取
  11. //连接点方法的实参
  12. Object[] args=proceeding.getArgs();
  13. //连接点方法的方法名
  14. String methodName=proceeding.getSignature().getName();
  15. //连接点方法所在的对象
  16. Object targetObj=proceeding.getTarget();
  17. String targetClassName=targetObj.getClass().getName();
  18. Object result=null;
  19. try {
  20. System.out.println("前置通知====");
  21. //执行连接点的方法 获取返回值
  22. result=proceeding.proceed(args);
  23. System.out.println("返回通知====");
  24. }catch (Throwable e) {
  25. System.out.println("异常通知===");
  26. }finally {
  27. System.out.println("最终通知===");
  28. }
  29. return result;
  30. }

运行结果
在这里插入图片描述

发表评论

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

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

相关阅读