当前位置:   article > 正文

Spring——AOP中5大通知注解的理解与使用_aop后置通知

aop后置通知

文章目录:

1.引子

1.1 大致步骤 

1.2 pom.xml文件中加入maven依赖

1.2.1 spring依赖 

1.2.2 spring-aspects依赖

1.2.3 单元测试依赖 

1.3 在Spring配置文件中声明AspectJ的自动代理生成器

2.AOP中的5大通知注解

2.1 @Before:前置通知

2.2 @AfterReturning:后置通知

2.3 @Around:环绕通知

2.4 @AfterThrowing:异常通知

2.5 @After:最终通知

3.@Pointcut 定义切入点


1.引子

承接了上一篇文章:Spring——AOP基本概念的理解_spring aop概念-CSDN博客

这篇文章中,主要介绍一下通过 AspectJ 框架更好的理解和使用 AOP 的5大通知注解。

在 AspectJ 实现 AOP 时,要引入 AOP 的约束。配置文件中使用的 AOP 约束中的标签,均是 AspectJ 框架使用的,而非 Spring 框架本身在实现 AOP 时使用的。AspectJ 对于 AOP 的实现有注解和配置文件两种方式,常用是注解方式。

1.1 大致步骤 

使用apsectj框架的注解,实现前置通知,步骤如下:
1.新建Maven项目

2.修改pom.xml,加入依赖
    spring-context依赖、spring-aspects依赖、junit

3.创建业务接口和实现类

4.创建一个切面类(普通类)
    1) 在类的上面加入@Aspect
    2) 在类中定义方法,方法表示切面的功能。在方法的上面加入AspectJ框架中的通知注解
       例如:@Before(value="切入点表达式")

5.创建spring配置文件
    1) 声明目标对象
    2) 声明切面类对象
    3) 声明自动代理生成器

6.创建测试类,测试目标方法执行时,增加切面的功能

1.2 pom.xml文件中加入maven依赖

1.2.1 spring依赖 

  1. <dependency>
  2. <groupId>org.springframework</groupId>
  3. <artifactId>spring-context</artifactId>
  4. <version>5.2.5.RELEASE</version>
  5. </dependency>

1.2.2 spring-aspects依赖

  1. <dependency>
  2. <groupId>org.springframework</groupId>
  3. <artifactId>spring-aspects</artifactId>
  4. <version>5.2.5.RELEASE</version>
  5. </dependency>

1.2.3 单元测试依赖 

  1. <dependency>
  2. <groupId>junit</groupId>
  3. <artifactId>junit</artifactId>
  4. <version>4.11</version>
  5. <scope>test</scope>
  6. </dependency>

1.3 在Spring配置文件中声明AspectJ的自动代理生成器

  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" xmlns:aop="http://www.springframework.org/schema/aop"
  4. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
  5. <!-- 声明自动代理生成器,目的是创建目标对象的代理 -->
  6. <aop:aspectj-autoproxy />
  7. </beans>

2.AOP中的5大通知注解

2.1 @Before:前置通知

  1. /**
  2. * 前置通知方法的定义
  3. * 1) 方法是public
  4. * 2) 返回值是void
  5. * 3) 方法名称自定义
  6. * 4) 可以有参数,也可以无参数。如果有,参数是JoinPoint
  7. *
  8. * @Before: 前置通知
  9. * 属性: value 切入点表达式,表示切面的执行位置。在这个方法执行时,会同时执行切面的功能
  10. * 位置: 放在方法的上面
  11. * 特点: 1) 执行时间在目标方法之前先执行
  12. * 2) 不会影响目标方法的执行
  13. * 3) 不会修改目标方法的执行结果
  14. * 切面类中的通知方法,可以有参数。必须是JoinPoint
  15. * JoinPoint: 表示正在执行的业务方法,相当于反射中的Method
  16. * 使用要求: 必须是参数列表的第一个
  17. * 作用: 获取方法执行时的信息,例如方法名称、方法的参数集合
  18. */
  1. package com.bjpowernode.service;
  2. /**
  3. *
  4. */
  5. public interface SomeService {
  6. void doSome(String name,Integer age);
  7. void doOther();
  8. }
  1. package com.bjpowernode.service.impl;
  2. import com.bjpowernode.service.SomeService;
  3. public class SomeServiceImpl implements SomeService {
  4. @Override
  5. public void doSome(String name, Integer age) {
  6. System.out.println("业务方法doSome(),创建商品的订单");
  7. }
  8. @Override
  9. public void doOther() {
  10. System.out.println("业务方法doOther()");
  11. }
  12. }
  1. package com.bjpowernode.handle;
  2. import org.aspectj.lang.JoinPoint;
  3. import org.aspectj.lang.annotation.Aspect;
  4. import org.aspectj.lang.annotation.Before;
  5. import java.util.Date;
  6. /**
  7. * @Aspect: 切面类的注解
  8. * 位置: 放在某个类的上面
  9. * 作用: 表示当前类是切面类
  10. */
  11. @Aspect
  12. public class MyAspect {
  13. @Before(value = "execution(public void com.bjpowernode.service.impl.SomeServiceImpl.do*(..))")
  14. public void myBefore(JoinPoint joinPoint) {
  15. //获取方法的完整定义
  16. System.out.println("前置通知,获取目标方法的定义: " + joinPoint.getSignature());
  17. //获取方法的名称
  18. System.out.println("前置通知,获取目标方法的名称: " + joinPoint.getSignature().getName());
  19. //获取方法执行时的参数
  20. Object[] args=joinPoint.getArgs();
  21. for(Object obj : args) {
  22. System.out.println("前置通知,获取目标方法的参数: " + obj);
  23. }
  24. String methodName=joinPoint.getSignature().getName();
  25. if(methodName.equals("doSome")) {
  26. //切面的代码
  27. System.out.println("doSome输出日志=========前置通知,切面的功能,在目标方法之前先执行: " + new Date());
  28. }else if(methodName.equals("doOther")) {
  29. //切面的代码
  30. System.out.println("doOther输出日志========前置通知,作为方法名称、参数的记录");
  31. }
  32. }
  33. }
  1. <!-- 声明目标对象 -->
  2. <bean id="someService" class="com.bjpowernode.service.impl.SomeServiceImpl"/>
  3. <!-- 声明切面类对象 -->
  4. <bean id="myAspect" class="com.bjpowernode.handle.MyAspect"/>
  5. <!-- 声明自动代理生成器,目的是创建目标对象的代理 -->
  6. <aop:aspectj-autoproxy />
  1. @Test
  2. public void test01() {
  3. String config="applicationContext.xml";
  4. ApplicationContext ctx=new ClassPathXmlApplicationContext(config);
  5. SomeService service= (SomeService) ctx.getBean("someService");
  6. System.out.println("service === " + service.getClass().getName());
  7. service.doSome("张起灵",20);
  8. //service.doOther();
  9. }

2.2 @AfterReturning:后置通知

  1. /**
  2. * 后置通知方法的定义
  3. * 1) 方法是public
  4. * 2) 返回值是void
  5. * 3) 方法名称自定义
  6. * 4) 方法有参数,推荐使用Object
  7. *
  8. * @AfterReturning: 后置通知
  9. * 属性: value 切入点表达式
  10. * returning 自定义的变量,表示目标方法的返回值的返回值。
  11. * 自定义变量的名称必须和通知方法的形参名一样
  12. * 位置: 放在方法的上面
  13. * 特点: 1) 在目标方法之后执行
  14. * 2) 能获取到目标方法的执行结果
  15. * 3) 不会影响目标方法的执行
  16. * 方法的参数Object res,表示目标方法的返回值,使用res接收doOther的调用结果
  17. */
  1. package com.bjpowernode.service;
  2. /**
  3. *
  4. */
  5. public interface SomeService {
  6. String doOther(String name,Integer age);
  7. }
  1. package com.bjpowernode.service.impl;
  2. import com.bjpowernode.service.SomeService;
  3. public class SomeServiceImpl implements SomeService {
  4. @Override
  5. public String doOther(String name, Integer age) {
  6. System.out.println("执行业务方法doOther(),处理库存");
  7. return "abcd";
  8. }
  9. }
  1. package com.bjpowernode.handle;
  2. import org.aspectj.lang.annotation.AfterReturning;
  3. import org.aspectj.lang.annotation.Aspect;
  4. /**
  5. * @Aspect: 切面类的注解
  6. * 位置: 放在某个类的上面
  7. * 作用: 表示当前类是切面类
  8. */
  9. @Aspect
  10. public class MyAspect {
  11. @AfterReturning(value = "execution(* *..SomeServiceImpl.doOther(..))",
  12. returning = "res")
  13. public void myAfterReturning(Object res) {
  14. System.out.println("后置通知,在目标方法之后执行的,可以拿到执行结果" + res);
  15. }
  16. }
  1. <!-- 声明目标对象 -->
  2. <bean id="someService" class="com.bjpowernode.service.impl.SomeServiceImpl"/>
  3. <!-- 声明切面类对象 -->
  4. <bean id="myAspect" class="com.bjpowernode.handle.MyAspect"/>
  5. <!-- 声明自动代理生成器,目的是创建目标对象的代理 -->
  6. <aop:aspectj-autoproxy />
  1. @Test
  2. public void test01() {
  3. String config="applicationContext.xml";
  4. ApplicationContext ctx=new ClassPathXmlApplicationContext(config);
  5. SomeService service= (SomeService) ctx.getBean("someService");
  6. System.out.println("service === " + service.getClass().getName());
  7. service.doOther("张三",25);
  8. }

2.3 @Around:环绕通知

  1. /**
  2. * 环绕通知方法的定义
  3. * 1) 方法是public
  4. * 2) 必须有返回值,推荐使用Object类型
  5. * 3) 方法名称自定义
  6. * 4) 必须有ProceedingJoinPoint参数
  7. *
  8. * @Around: 环绕通知
  9. * 属性: value 切入点表达式
  10. * 位置: 在方法定义的上面
  11. * 测试方法中,调用目标方法doFirst(String name)去执行,
  12. * 实际上目标方法doFirst(String name)并未执行,而是执行了myAround(ProceedingJoinPoint proceedingJoinPoint)
  13. * 特点: 1) 在目标方法的前和后都能增强功能
  14. * 2) 控制目标方法是否执行
  15. * 3) 修改目标方法的执行结果
  16. */
  1. package com.bjpowernode.service;
  2. /**
  3. *
  4. */
  5. public interface SomeService {
  6. String doFirst(String name);
  7. }
  1. package com.bjpowernode.service.impl;
  2. import com.bjpowernode.service.SomeService;
  3. public class SomeServiceImpl implements SomeService {
  4. @Override
  5. public String doFirst(String name) {
  6. System.out.println("执行业务方法doFirst(),处理库存");
  7. return "doFirst";
  8. }
  9. }
  1. package com.bjpowernode.handle;
  2. import org.aspectj.lang.ProceedingJoinPoint;
  3. import org.aspectj.lang.annotation.Around;
  4. import org.aspectj.lang.annotation.Aspect;
  5. import java.util.Date;
  6. /**
  7. * @Aspect: 切面类的注解
  8. * 位置: 放在某个类的上面
  9. * 作用: 表示当前类是切面类
  10. */
  11. @Aspect
  12. public class MyAspect {
  13. @Around(value = "execution(* *..SomeServiceImpl.doFirst(..))")
  14. public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
  15. //获取方法执行时的参数值
  16. String name="";
  17. Object[] args=proceedingJoinPoint.getArgs();
  18. if(args!=null && args.length>0) {
  19. Object arg=args[0];
  20. if(arg!=null) {
  21. name=(String)arg;
  22. }
  23. }
  24. System.out.println("执行了环绕通知,在目标方法之前,输出日志时间=== " + new Date());
  25. Object methodReturn=null;
  26. if(name.equals("李四")) {
  27. //相当于执行目标方法doFirst(String name)
  28. methodReturn = proceedingJoinPoint.proceed();
  29. }
  30. System.out.println("执行了环绕通知,在目标方法之后,增加了事务功能");
  31. return methodReturn;
  32. }
  33. }
  1. <!-- 声明目标对象 -->
  2. <bean id="someService" class="com.bjpowernode.service.impl.SomeServiceImpl"/>
  3. <!-- 声明切面类对象 -->
  4. <bean id="myAspect" class="com.bjpowernode.handle.MyAspect"/>
  5. <!-- 声明自动代理生成器,目的是创建目标对象的代理 -->
  6. <aop:aspectj-autoproxy />
  1. @Test
  2. public void test01() {
  3. String config="applicationContext.xml";
  4. ApplicationContext ctx=new ClassPathXmlApplicationContext(config);
  5. SomeService service= (SomeService) ctx.getBean("someService");
  6. System.out.println("service === " + service.getClass().getName());
  7. String ret=service.doFirst("李四");
  8. System.out.println("ret调用目标方法的结果 === " + ret);
  9. }

2.4 @AfterThrowing:异常通知

  1. /**
  2. * 异常通知方法的定义
  3. * 1) 方法是public
  4. * 2) 返回值是void
  5. * 3) 方法名称自定义
  6. * 4) 方法有参数Exception
  7. *
  8. * @AfterThrowing: 异常通知
  9. * 属性: value 切入点表达式
  10. * throwing 自定义变量,表示目标方法抛出的异常。变量名必须和通知方法的形参名一样
  11. * 位置: 在方法上面
  12. * 特点: 1) 在目标方法抛出异常之后执行的,若没有异常 则不执行
  13. * 2) 能获取到目标方法的异常信息
  14. * 3) 不是异常处理程序,可以得到发生异常的通知,可以发送邮件、短信通知开发人员
  15. * 可以看作是目标方法的监控程序
  16. * 可以看作以下语句块:
  17. * try {
  18. * SomeServiceImpl.doSecond(..)
  19. * }catch(Exception ex) {
  20. * myAfterThrowing(Exception ex)
  21. * }
  22. */
  1. package com.bjpowernode.service;
  2. /**
  3. *
  4. */
  5. public interface SomeService {
  6. void doSecond(String name);
  7. }
  1. package com.bjpowernode.service.impl;
  2. import com.bjpowernode.service.SomeService;
  3. public class SomeServiceImpl implements SomeService {
  4. @Override
  5. public void doSecond(String name) {
  6. System.out.println("执行业务方法doSecond(),处理库存" + (10/0));
  7. }
  8. }
  1. package com.bjpowernode.handle;
  2. import org.aspectj.lang.annotation.AfterThrowing;
  3. import org.aspectj.lang.annotation.Aspect;
  4. /**
  5. * @Aspect: 切面类的注解
  6. * 位置: 放在某个类的上面
  7. * 作用: 表示当前类是切面类
  8. */
  9. @Aspect
  10. public class MyAspect {
  11. @AfterThrowing(value = "execution(* *..SomeServiceImpl.doSecond(..))",throwing = "ex")
  12. public void myAfterThrowing(Exception ex) {
  13. System.out.println("异常通知,在目标方法抛出异常时执行的,异常原因是:" + ex.getMessage());
  14. }
  15. }
  1. <!-- 声明目标对象 -->
  2. <bean id="someService" class="com.bjpowernode.service.impl.SomeServiceImpl"/>
  3. <!-- 声明切面类对象 -->
  4. <bean id="myAspect" class="com.bjpowernode.handle.MyAspect"/>
  5. <!-- 声明自动代理生成器,目的是创建目标对象的代理 -->
  6. <aop:aspectj-autoproxy />

2.5 @After:最终通知

  1. /**
  2. * 最终通知方法的定义
  3. * 1) 方法是public
  4. * 2) 返回值是void
  5. * 3) 方法名称自定义
  6. * 4) 方法无参数
  7. *
  8. * @After: 最终通知
  9. * 属性: value 切入点表达式
  10. * 位置: 在方法上面
  11. * 特点: 1) 在目标方法之后执行的
  12. * 2) 总是会被执行
  13. * 3) 可以用来做程序最后的收尾工作,例如清除临时数据、变量,清理内存
  14. * 可以看作如下语句块:
  15. * try {
  16. * SomeServiceImpl.doThird(..)
  17. * }finally {
  18. * myAfter()
  19. * }
  20. */
  1. package com.bjpowernode.service;
  2. /**
  3. *
  4. */
  5. public interface SomeService {
  6. void doThird();
  7. }
  1. package com.bjpowernode.service.impl;
  2. import com.bjpowernode.service.SomeService;
  3. public class SomeServiceImpl implements SomeService {
  4. @Override
  5. public void doThird() {
  6. System.out.println("执行业务方法doThird()");
  7. }
  8. }
  1. package com.bjpowernode.handle;
  2. import org.aspectj.lang.annotation.After;
  3. import org.aspectj.lang.annotation.Aspect;
  4. /**
  5. * @Aspect: 切面类的注解
  6. * 位置: 放在某个类的上面
  7. * 作用: 表示当前类是切面类
  8. */
  9. @Aspect
  10. public class MyAspect {
  11. @After(value = "execution(* *..SomeServiceImpl.doThird(..))")
  12. public void myAfter() {
  13. System.out.println("最终通知,总是会被执行的");
  14. }
  15. }
  1. <!-- 声明目标对象 -->
  2. <bean id="someService" class="com.bjpowernode.service.impl.SomeServiceImpl"/>
  3. <!-- 声明切面类对象 -->
  4. <bean id="myAspect" class="com.bjpowernode.handle.MyAspect"/>
  5. <!-- 声明自动代理生成器,目的是创建目标对象的代理 -->
  6. <aop:aspectj-autoproxy />
  1. @Test
  2. public void test01() {
  3. String config="applicationContext.xml";
  4. ApplicationContext ctx=new ClassPathXmlApplicationContext(config);
  5. SomeService service= (SomeService) ctx.getBean("someService");
  6. System.out.println("service === " + service.getClass().getName());
  7. service.doThird();
  8. }


3.@Pointcut 定义切入点

当较多的通知增强方法使用相同的 execution 切入点表达式时,编写、维护均较为麻烦。AspectJ 提供了 @Pointcut 注解,用于定义 execution 切入点表达式。

其用法是,将 @Pointcut 注解在一个方法之上,以后所有的 execution 的 value 属性值均可使用该方法名作为切入点。代表的就是@Pointcut 定义的切入点。这个使用 @Pointcut 注解的方法一般使用 private 的标识方法,即没有实际作用的方法。

下面的代码只给出切面类:

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/人工智能uu/article/detail/911062
推荐阅读
相关标签