1.前置通知

在目标方法执行之前执行执行的通知。

前置通知方法,可以没有参数,也可以额外接收一个JoinPoint,Spring会自动将该对象传入,代表当前的连接点,通过该对象可以获取目标对象 和 目标方法相关的信息。

注意,如果接收JoinPoint,必须保证其为方法的第一个参数,否则报错。

配置方式:

<?xml version="1.0" encoding="UTF-8"?>
<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 http://www.springframework.org/schema/beans/spring-beans-3.2.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context-3.2.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-3.2.xsd"><context:annotation-config></context:annotation-config><context:component-scan base-package="cn.tedu.service,cn.tedu.aop"></context:component-scan><aop:config proxy-target-class="true"><!-- 配置切入点  --><aop:pointcut expression="execution(* cn.tedu.service.UserServiceImpl.addUser(..))" id="pc01"/><!-- 配置切面 --><aop:aspect ref="firstAspect">       <<!-- 前置通知 --><aop:before method="before" pointcut-ref="pc01"/></aop:aspect></aop:config>
</beans>

package cn.tedu.service;import org.springframework.stereotype.Service;
/*** UserServiceImple:目标对象*/
@Service("userService")
public class UserServiceImple implements UserService {@Overridepublic void addUser(String name) {System.out.println("增加用户。。");}@Overridepublic void updateUser() {System.out.println("修改用户。。");}@Overridepublic void deleteUser() {System.out.println("删除用户。。");}@Overridepublic void query() {System.out.println("查询用户。。");}
}

package cn.tedu.aop;import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.springframework.stereotype.Component;
/*** FirstAspect:切面代码*/
@Component
public class FirstAspect {public void before(JoinPoint jp){ // 可以选择额外的传入一个JoinPoint连接点对象,必须用方法的第一个参数接收。Class clz = jp.getTarget().getClass();Signature signature = jp.getSignature(); // 通过JoinPoint对象获取更多信息String name = signature.getName();System.out.println("1 -- before...["+clz+"]...["+name+"]...");}
}

package cn.tedu.test;import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;import cn.tedu.service.UserService;
/*** AOPTest:测试代码*/
public class AOPTest {@Testpublic void test01(){ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");UserService userService = (UserService) context.getBean("userService");userService.addUser("cjj"); // 一个连接点
    }
}

执行结果:

1 -- before...[class cn.tedu.service.UserServiceImple]...[addUser]...
增加用户。。

2.环绕通知

在目标方法执行之前和之后都可以执行额外代码的通知。

在环绕通知中必须显式的调用目标方法,目标方法才会执行,这个显式调用时通过ProceedingJoinPoint来实现的,可以在环绕通知中接收一个此类型的形参,spring容器会自动将该对象传入,注意这个参数必须处在环绕通知的第一个形参位置。

**要注意,只有环绕通知可以接收ProceedingJoinPoint,而其他通知只能接收JoinPoint。

环绕通知需要返回返回值,否则真正调用者将拿不到返回值,只能得到一个null。

环绕通知有控制目标方法是否执行、有控制是否返回值、有改变返回值的能力。

环绕通知虽然有这样的能力,但一定要慎用,不是技术上不可行,而是要小心不要破坏了软件分层的“高内聚 低耦合”的目标。

配置方式:

 <!-- 环绕通知 --><aop:around method="around" pointcut-ref="pc1"/>

    public Object around(ProceedingJoinPoint jp) throws Throwable{System.out.println("1 -- around before...");Object obj = jp.proceed(); //--显式的调用目标方法System.out.println("1 -- around after...");return obj;}

运行结果:

1 -- around before...
增加用户。。
1 -- around after...

3.后置通知

在目标方法执行之后执行的通知。

在后置通知中也可以选择性的接收一个JoinPoint来获取连接点的额外信息,但是这个参数必须处在参数列表的第一个。

配置方式:

<!-- 后置通知 -->
<aop:after-returning method="afterReturn" pointcut-ref="pc1"/>

    public void afterReturn(JoinPoint jp){Class clz = jp.getTarget().getClass();Signature signature = jp.getSignature(); String name = signature.getName();System.out.println("1 -- afterReturn...["+clz+"]...["+name+"]...");}

执行结果:

1 -- before...[class cn.tedu.service.UserServiceImple]...[addUser]...
1 -- around before...
增加用户。。
1 -- around after...
1 -- afterReturn...[class cn.tedu.service.UserServiceImple]...[addUser]...

在后置通知中,还可以通过配置获取返回值

一定要保证JoinPoint处在参数列表的第一位,否则抛异常

配置方式:

<!-- 后置通知 -->
<aop:after-returning method="afterReturn" pointcut-ref="pc1" returning="msg"/>

    public void afterReturn(JoinPoint jp, Object msg){Class clz = jp.getTarget().getClass();Signature signature = jp.getSignature(); String name = signature.getName();System.out.println("1 -- afterReturn...["+clz+"]...["+name+"]...["+msg+"]...");}

执行结果:

1 -- before...[class cn.tedu.service.UserServiceImple]...[addUser]...
1 -- around before...
增加用户。。
1 -- around after...
1 -- afterReturn...[class cn.tedu.service.UserServiceImple]...[addUser]...[cjj]...

4.异常通知

在目标方法抛出异常时执行的通知

可以配置传入JoinPoint获取目标对象和目标方法相关信息,但必须处在参数列表第一位

另外,还可以配置参数,让异常通知可以接收到目标方法抛出的异常对象。

配置方法:

<!-- 异常通知 -->
<aop:after-throwing method="afterThrow" pointcut-ref="pc1" throwing="e"/>

    public void afterThrow(JoinPoint jp,Throwable e){Class clz = jp.getTarget().getClass();String name = jp.getSignature().getName();System.out.println("1afterThrow..["+clz+"]..["+name+"].."+e.getMessage());}

代码报异常后

执行结果:

1 -- before...[class cn.tedu.service.UserServiceImple]...[addUser]...
1 -- around before...
1 -- afterThrow..[class cn.tedu.service.UserServiceImple]..[addUser]../ by zero

5.最终通知

是在目标方法执行之后执行的通知。

和后置通知不同之处在于,后置通知是在方法正常返回后执行的通知,如果方法没有正常返-例如抛出异常,则后置通知不会执行。

而最终通知无论如何都会在目标方法调用过后执行,即使目标方法没有正常的执行完成。

另外,后置通知可以通过配置得到返回值,而最终通知无法得到。

最终通知也可以额外接收一个JoinPoint参数,来获取目标对象和目标方法相关信息,但一定要保证必须是第一个参数。

配置方式:

<!-- 最终通知 -->
<aop:after method="after" pointcut-ref="pc1" />

    public void after(JoinPoint jp){Class clz = jp.getTarget().getClass();String name = jp.getSignature().getName();System.out.println("1 -- after..["+clz+"]..["+name+"]...");}

执行结果:

1 -- before...[class cn.tedu.service.UserServiceImple]...[addUser]...
1 -- around before...
增加用户。。
1 -- around after...
1 -- afterReturn...[class cn.tedu.service.UserServiceImple]...[addUser]...[cjj]...
1 -- after..[class cn.tedu.service.UserServiceImple]..[addUser]...
cjj

源码

<?xml version="1.0" encoding="UTF-8"?>
<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 http://www.springframework.org/schema/beans/spring-beans-3.2.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd "><context:annotation-config></context:annotation-config><context:component-scan base-package="cn.tedu.service,cn.tedu.aop"></context:component-scan><!-- proxy-target-class属性值决定是基于接口的还是基于类的代理被创建 --><aop:config proxy-target-class="true"> <!-- 配置切入点 --><aop:pointcut expression="execution(* cn.tedu.service.UserServiceImple.addUser(..))" id="pc1"/>        <!-- 配置切入面 --><aop:aspect ref="firstAspect"><!-- 前置通知 -->    <aop:before method="before" pointcut-ref="pc1"/><!-- 环绕通知 --><aop:around method="around" pointcut-ref="pc1"/><!-- 后置通知 --><!-- <aop:after-returning method="afterReturn" pointcut-ref="pc1"/> --><aop:after-returning method="afterReturn" pointcut-ref="pc1" returning="msg"/><!-- 异常通知 --><aop:after-throwing method="afterThrow" pointcut-ref="pc1" throwing="e"/><!-- 最终通知 --><aop:after method="after" pointcut-ref="pc1" /></aop:aspect></aop:config></beans>

applicationContext.xml

package cn.tedu.service;
/*** 接口*/
public interface UserService {public String addUser(String name);public void updateUser();public void deleteUser();public void query();
}

UserService.java

package cn.tedu.service;import org.springframework.stereotype.Service;
/*** UserServiceImple:目标对象*/
@Service("userService")
public class UserServiceImple implements UserService {@Overridepublic  String addUser(String name) {// int i = 1/0;System.out.println("增加用户。。");return "cjj";}@Overridepublic void updateUser() {System.out.println("修改用户。。");}@Overridepublic void deleteUser() {System.out.println("删除用户。。");}@Overridepublic void query() {System.out.println("查询用户。。");}
}

UserServiceImple.java

package cn.tedu.aop;import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.springframework.stereotype.Component;
/*** FirstAspect:切面代码*/
@Component
public class FirstAspect {public void before(JoinPoint jp){ // 可以选择额外的传入一个JoinPoint连接点对象,必须用方法的第一个参数接收。Class clz = jp.getTarget().getClass();Signature signature = jp.getSignature(); // 通过JoinPoint对象获取更多信息String name = signature.getName();System.out.println("1 -- before...["+clz+"]...["+name+"]...");}public Object around(ProceedingJoinPoint jp) throws Throwable{System.out.println("1 -- around before...");Object obj = jp.proceed(); //--显式的调用目标方法System.out.println("1 -- around after...");return obj;}public void afterReturn(JoinPoint jp, Object msg){Class clz = jp.getTarget().getClass();Signature signature = jp.getSignature(); String name = signature.getName();System.out.println("1 -- afterReturn...["+clz+"]...["+name+"]...["+msg+"]...");}public void afterThrow(JoinPoint jp,Throwable e){Class clz = jp.getTarget().getClass();String name = jp.getSignature().getName();System.out.println("1 -- afterThrow..["+clz+"]..["+name+"].."+e.getMessage());}public void after(JoinPoint jp){Class clz = jp.getTarget().getClass();String name = jp.getSignature().getName();System.out.println("1 -- after..["+clz+"]..["+name+"]...");}
}

FirstAspect.java

package cn.tedu.test;import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;import cn.tedu.service.UserService;
/*** AOPTest:测试代码*/
public class AOPTest {@Testpublic void test01(){ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");UserService userService = (UserService) context.getBean("userService");String result = userService.addUser("cjj"); // 一个连接点
        System.out.println(result);}
}

AOPTes.java

五种通知的执行顺序

1.在目标方法没有抛出异常的情况下

前置通知

环绕通知的调用目标方法之前的代码

目标方法

环绕通知的调用目标方法之后的代码

后置通知

最终通知

2.在目标方法抛出异常的情况下

前置通知

环绕通知的调用目标方法之前的代码

目标方法 抛出异常 异常通知

最终通知

3.如果存在多个切面

多切面执行时,采用了责任链设计模式。

切面的配置顺序决定了切面的执行顺序,多个切面执行的过程,类似于方法调用的过程,在环绕通知的proceed()执行时,去执行下一个切面或如果没有下一个切面执行目标方法,从而达成了如下的执行过程:

如果目标方法抛出异常:

五种通知的常见使用场景

环绕通知

控制事务 权限控制

后置通知

记录日志(方法已经成功调用)

异常通知

异常处理 控制事务

最终通知

记录日志(方法已经调用,但不一定成功)

转载于:https://www.cnblogs.com/chuijingjing/p/9806651.html

Spring AOP 五大通知类型相关推荐

  1. Spring 中的AOP的通知类型的示例(xml)

    个人博客:https://suveng.github.io/blog/​​​​​​​ Spring 中的AOP的通知类型的示例 AOP中的通知类型(advice)一共有五中: around advic ...

  2. Spring AOP之通知类别

    什么是通知类别 通知在切入点中执行的位置就是通知类别.关于通知和通知类别具体概念请移步我的另一篇博客Spring AOP之HelloWorld与概念介绍 5种通知类别介绍 在Spring AOP 中通 ...

  3. AOP五大通知注解详解

    这里写目录标题 一.五大通知注解的概念 1.前置通知@Before 2.后置通知 @AfterReturning 3.异常通知 @AfterThrowing 4.最终通知 @After 5 环绕通知@ ...

  4. Spring AOP(通知、连接点、切点、切面)

    一.AOP术语 通知(Advice)   切面的工作被称为通知.通知定义了切面是什么以及何时使用.除了描述切面要完成的工作,通知还解决了何时执行这个工作的问题. 5种通知类型: 前置通知(Before ...

  5. Spring AOP之通知类别执行顺序

    以下内容来源:https://jinnianshilongnian.iteye.com/blog/1423489 如果我们有多个通知想要在同一连接点执行,那执行顺序如何确定呢?Spring AOP使用 ...

  6. Spring AOP 之 通知、连接点、切点、切面。

    1:知识背景 软件系统可以看成是由一组关注点组成的,其中,直接的业务关注点,是直切关注点.而为直切关注点提供服务的,就是横切关注点. 2:面向切面的基本原理 什么是面向切面编程 横切关注点:影响应用多 ...

  7. spring aop 环绕通知around和其他通知的区别

    前言: spring 的环绕通知和前置通知,后置通知有着很大的区别,主要有两个重要的区别: 1) 目标方法的调用由环绕通知决定,即你可以决定是否调用目标方法,而前置和后置通知   是不能决定的,他们只 ...

  8. AOP 详解 、AOP 中通知类型 、AOP 两种实现方式(Schema-base 和 AspectJ)

    一.AOP AOP:中文名称面向切面编程 英文名称:(Aspect Oriented Programming) 正常程序执行流程都是纵向执行流程 3.1 又叫面向切面编程,在原有纵向执行流程中添加横切 ...

  9. Spring AOP 前置通知

    我们使用AspectJ对Spring进行AOP操作,有两种方式,注解和XML配置方式,先在pom.xml中声明jar包 <dependencies><dependency>&l ...

最新文章

  1. 还在用肉眼找不同吗?这个技术轻松搞定
  2. jieba分词中cut和lcut的区别
  3. VB 金额大小写转换(两种方法)
  4. 【2040】反向输出序列
  5. 网易智慧企业 Node.js 实践(2)| 平滑发布和前端代码
  6. MongoDB 语法和mysql语法对比学习
  7. vue-router 在项目中的使用
  8. ffmpeg命令 抓屏_使用FFmpeg从视频中截图的命令 | 学步园
  9. 机器学习核心概念、常用术语整理(建议收藏)
  10. admin后台菜单栏的实现
  11. “读心术”再升级,数字识别准确率可达90%
  12. 微信机器人控制linux,微信运维交互机器人的示例代码
  13. Linux下WebLogic漏洞补丁包安装过程
  14. ios苹果手机 uniapp长按识别二维码图片白色背景问题
  15. 【Laravel系列6.3】框架启动与服务容器源码
  16. mysql 索引 insert_Mysql 大批量数据insert or update与UQ索引
  17. elasticsearch7.6 词频统计
  18. 「机密计算-隐私计算」科普
  19. Uber无模式数据存储
  20. linux 判断u盘 硬盘坏道,u盘怎么检测硬盘坏道

热门文章

  1. hadoop fs 运维常用的几个命令
  2. Android onLoadFinished与onLoaderReset
  3. 重启php-fpm的方法
  4. 判断 iframe 是否加载完成的完美方法(转)
  5. 在MacOSX上重新安装Python (10.8) python 自然语言处理的前戏
  6. javascript之Partial Application
  7. SQL Server 数据库管理常用的SQL和T-SQL语句
  8. 浅谈三种特殊进程:孤儿进程,僵尸进程和守护进程
  9. 吃饭、睡觉、打星星之“打星星”!
  10. [转载]SYSCALL_DEFINE宏定义