Spring AOP 五大通知类型

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/464663.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

转载CSDN博文精选:Android系列开发博客资源汇总

CSDN博客本期热文推荐&#xff0c;为您介绍有关Android应用开发的10个博客&#xff0c;分享他们的日积月累的宝贵经验&#xff0c;希望这些文章对Android开发者们能有所启发和帮助。 [1] 张国威&#xff1a;Android从入门到提高系列 前面写了十四篇关于界面的入门文章&#xff…

简单工厂模式+工厂方法模式

在面向对象编程中, 最通常的方法是一个new操作符产生一个对象实例,new操作符就是用来构造对象实例的。但是在一些情况下, new操作符直接生成对象会带来一些问题。举例来说, 许多类型对象的创造需要一系列的步骤: 你可能需要计算或取得对象的初始设置; 选择生成哪个子对象实例; …

Windows下搭建ESP-IDF开发环境,适合ESP32/S2/C3/S3系列模组二次开发

前言本教程适用于以下两种用户&#xff1a;①无Linux环境搭建经验或搭建Linux开发环境不成功&#xff1b;②使用安信可windows一体化环境IDE V1.5开发环境搭建不成功&#xff1b;本教程提供了windows下搭建 ESP-IDF 开发环境的方法。适用系统&#xff1a;Windows 10 64 位版本、…

Lync Server 2010的部署系列_第六章 安装配置拓扑生成器、前端Server、前端池

一、安装 Lync Server 2010 管理工具&#xff08;包括拓扑生成器&#xff09; 1) 登录Front.Gianthard.com&#xff08;192.168.1.21&#xff09;。在“Microsoft Lync Server 2010 - 部署向导”页上&#xff0c;单击“安装拓扑生成器”。 2) 进行SQL方面的客户端。 3) 安装成功…

被 HR 直接怼:估计你一辈子就是个程序员

今天看到一个非常扯蛋的事情。事情来自网络&#xff0c;不是作者本人。我一直认为程序员是可以做一辈子的事情&#xff0c;程序员是一种做得越久技术越熟练的工作。但是有的人并不这样认为。---有程序员因为能力很强&#xff0c;公司非常满意&#xff0c;结果派了一位 HR 与其谈…

一起谈.NET技术,在MVC2.0使用Lodop为WEB打印提出完美解决方案

通过好友CallHot介绍Lodopweb打印控件。由于是国人开发的&#xff0c;故这两天认真了研究下&#xff0c;打算在未来的项目中使用。现将学习成果与园友分享。如果存在不足的地方&#xff0c;希望您指出。 具体的实现步骤如下&#xff1a; 一、准备工作 1.MVC2.0 jQuery1.4.1 开…

(转)python调取C/C++的dll生成方法

本文针对Windows平台下&#xff0c;python调取C/C的dll文件。 1.如果使用C语言&#xff0c;代码如下&#xff0c;文件名为test.c。 __declspec(dllexport) int sum(int a,int b) {return (a b); } 如果使用C语言&#xff0c;代码如下&#xff0c;文件名为test_cpp.cpp。在Wind…

生产者-消费者模式

生产者/消费者问题的多种Java实现方式 实质上&#xff0c;很多后台服务程序并发控制的基本原理都可以归纳为生产者/消费者模式&#xff0c;而这是恰恰是在本科操作系统课堂上老师反复讲解&#xff0c;而我们却视而不见不以为然的。在博文《一种面向作业流(工作流)的轻量级可复用…

周末,说下我喜欢的篮球

我应该有很久没有看NBA比赛了&#xff0c;没有其他原因&#xff0c;确实是因为工作太忙了&#xff0c;即使是在带薪上厕所&#xff0c;也没有足够的时间看下NBA比赛。如果说忙是一个比较好的托词&#xff0c;那还有一个原因&#xff0c;我现在更多的喜欢野球圈的新闻。刚毕业那…

没有串口,如何打印单片机调试信息?

输出调试信息是嵌入式开发中必不可少的调试利器&#xff0c;嵌入式开发的一个特点是很多时候没有操作系统&#xff0c;或者没有文件系统&#xff0c;常规的打印log到文件的方法基本不适用。最常用的是通过串口输出uart log&#xff0c;例如51单片机&#xff0c;只要实现串口驱动…

Linux驱动小技巧 | 利用DRIVER_ATTR实现调用内核函数

1. 前言 很多朋友在调试驱动的时候&#xff0c;都会遇到这样一个场景&#xff1a;修改一个参数&#xff0c;然后调用某个内核中的函数。比如将某个gpio的值拉高/拉低&#xff0c;修改某个寄存器的值等等。如果每一个参数都通过字符设备的ioctl接口&#xff0c;增加对应的cmd&am…

myeclipse运行java项目_myeclipse运行自己从前的或其他人的javaweb项目

1.配置javaweb 连接部署好本地的tomcat服务器方法如下&#xff1a;1.运行Myecipse-->Window-->preference2.MyEclipse-->Servers-->Runtime Environments-->add-->Apache Tomcat v8.03.next->Browser(Tomcat的安装目录)-->finishMyeclipse 设置jdk版本…

MYSQL性能调优及架构设计学习笔记-影响MYSQL性能的相关因素之实例分析

为什么80%的码农都做不了架构师&#xff1f;>>> 需求概述 一个简单的讨论区系统&#xff0c;需要有用户&#xff0c;用户组&#xff0c;组讨论区这三部分基本功能 简要分析 1&#xff09; 须要存放用户数据的表&#xff1b; 2&#xff09; 须要存放分组信息和用户与…

软件测试第三次作业

一、开头 (1)合作者&#xff1a;201631062521&#xff0c;201631062421 (2)代码地址&#xff1a;https://gitee.com/h2503652646/WordCount.git (3)本次作业链接地址&#xff1a;https://edu.cnblogs.com/campus/xnsy/Test/homework/2203 二、正文 (1)互审代码情况 已经实现Wor…

Linux驱动开发中与设备树相关的6种debug方法

整理出了6种驱动开发时与设备注册、设备树相关的调试方法&#xff0c;彼此间没有优先级之分&#xff0c;每种方法不一定是最优解&#xff0c;但可以作为一种debug查找问题的手段&#xff0c;快速定位问题原因。例如在芯片验证时&#xff0c;不同时钟频率下系统启动情况摸底时&a…

DiscoveryService.getRemoteAdvertisements是否会获得本地通告?

从该方法名称上来说&#xff0c;应该是不能获得本地通告&#xff0c;本着探究的精神&#xff0c;首先做个小测试&#xff0c;再从源代码中寻找答案。----------------------------------------------------------------------------- 测试结果&#xff1a;本地通告确实取不到&a…

Linux的bc命令计算π的值预估系统算力

这是今天突然想到的一个事情&#xff0c;几年前我和一个朋友做一个开发板&#xff0c;然后我们需要完成的这个开发板有算力的要求&#xff0c;当时我们测试的时候就用了一个shell脚本来分析系统的算力。今天我突然想不起这个命令&#xff0c;然后就想写篇文章记录下&#xff0c…

面向对象的思想是什么?

我同事的回答&#xff0c;我觉得这句话可以读十遍说下我自己的理解你不是人&#xff0c;你是猪。解释一下我们在编写面向对象的代码时&#xff0c;一定要有抽象的思想&#xff0c;什么是抽象&#xff0c;抽象是一种概念的东西&#xff0c;不是实实在在的&#xff0c;看不见摸不…

python编程格式化输出_Python的三种格式化输出

今天刚学了python的三种格式化输出&#xff0c;以前没接触过这么有趣的输出方式&#xff0c;现在来分享一下。#!/user/bin/env python#coding:utf-8#三种格式化输出#第一种格式化输出nameinput("name:")ageinput("age:")jobinput("job:")salaryi…

多线程的异常捕捉

为什么要单独讲多线程的异常捕捉呢&#xff1f;先看个例子&#xff1a; public class ThreadException implements Runnable{Overridepublic void run() {throw new RuntimeException();}//现象&#xff1a;控制台打印出异常信息&#xff0c;并运行一段时间后才停止public stat…