Spring使用AspectJ开发AOP

AspectJ 是一个基于 Java 语言的 AOP 框架,它扩展了 Java 语言。Spring 2.0 以后,新增了对 AspectJ 方式的支持,新版本的 Spring 框架,建议使用 AspectJ 方式开发 AOP。

使用 AspectJ 开发 AOP 通常有两种方式:

	基于 XML 的声明式。基于 Annotation 的声明式。

基于XML的声明式

基于 XML 的声明式是指通过 Spring 配置文件的方式定义切面、切入点及声明通知,而所有的切面和通知都必须定义在 aop:config 元素中。

下面通过案例演示 Spring 中如何使用基于 XML 的声明式实现 AOP 的开发。

1. 导入 JAR 包
使用 AspectJ 除了需要导入 Spring AOP 的 JAR 包以外,还需要导入与 AspectJ 相关的 JAR 包,具体如下。

spring-aspects-3.2.13.RELEASE.jar:Spring 为 AspectJ 提供的实现,在 Spring 的包中已经提供。

com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar:是 AspectJ 提供的规范,可以在官方网址 https://repo.spring.io/webapp/#/search/quick/ 中搜索并下载。

2. 创建切面类 MyAspect
在 src 目录下创建一个名为 com.mengma.aspectj.xml 的包,在该包下创建切面类 MyAspect,编辑后如下所示。

package com.mengma.aspectj.xml;import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;//切面类
public class MyAspect {// 前置通知public void myBefore(JoinPoint joinPoint) {System.out.print("前置通知,目标:");System.out.print(joinPoint.getTarget() + "方法名称:");System.out.println(joinPoint.getSignature().getName());}// 后置通知public void myAfterReturning(JoinPoint joinPoint) {System.out.print("后置通知,方法名称:" + joinPoint.getSignature().getName());}// 环绕通知public Object myAround(ProceedingJoinPoint proceedingJoinPoint)throws Throwable {System.out.println("环绕开始"); // 开始Object obj = proceedingJoinPoint.proceed(); // 执行当前目标方法System.out.println("环绕结束"); // 结束return obj;}// 异常通知public void myAfterThrowing(JoinPoint joinPoint, Throwable e) {System.out.println("异常通知" + "出错了" + e.getMessage());}// 最终通知public void myAfter() {System.out.println("最终通知");}
}

上述代码中,分别定义了几种不同的通知类型方法,在这些方法中,通过 JoinPoint 参数可以获得目标对象的类名、目标方法名和目标方法参数等。需要注意的是,环绕通知必须接收一个类型为 ProceedingJoinPoint 的参数,返回值必须是 Object 类型,且必须抛出异常。异常通知中可以传入 Throwable 类型的参数,用于输出异常信息。

3. 创建 Spring 配置文件
在 com.mengma.aspectj.xml 包下创建 applicationContext.xml 的配置文件,如下所示。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="  http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-2.5.xsd  http://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-2.5.xsd"><!--目标类 --><bean id="customerDao" class="com.mengma.dao.CustomerDaoImpl" /><!--切面类 --><bean id="myAspect" class="com.mengma.aspectj.xml.MyAspect"></bean><!--AOP 编程 --><aop:config><aop:aspect ref="myAspect"><!-- 配置切入点,通知最后增强哪些方法 --><aop:pointcut expression="execution ( * com.mengma.dao.*.* (..))"id="myPointCut" /><!--前置通知,关联通知 Advice和切入点PointCut --><aop:before method="myBefore" pointeut-ref="myPointCut" /><!--后置通知,在方法返回之后执行,就可以获得返回值returning 属性 --><aop:after-returning method="myAfterReturning"pointcut-ref="myPointCut" returning="returnVal" /><!--环绕通知 --><aop:around method="myAround" pointcut-ref="myPointCut" /><!--抛出通知:用于处理程序发生异常,可以接收当前方法产生的异常 --><!-- *注意:如果程序没有异常,则不会执行增强 --><!-- * throwing属性:用于设置通知第二个参数的名称,类型Throwable --><aop:after-throwing method="myAfterThrowing"pointcut-ref="myPointCut" throwing="e" /><!--最终通知:无论程序发生任何事情,都将执行 --><aop:after method="myAfter" pointcut-ref="myPointCut" /></aop:aspect></aop:config>
</beans>

上述代码中,首先在第 4、7、8 行代码中分别导入了 AOP 的命名空间。第 12 行代码指定了切面类。

第 17、18 行代码配置了切入点,通知需要增强哪些方法,expression="execution(com.mengma.dao..*(…))的意思是增强 com.mengma.dao 包下所有的方法。

第 20~32 行代码用于关联通知(Advice)和切入点(PointCut)。以第 20 行代码前置通知为例,aop:before 标签的 method 属性用于指定通知,pointcut-ref 属性用于指定切入点,也就是要增强的方法。

4. 创建测试类
在 com.mengma.aspectj.xml 包下创建测试类 XMLTest,如下所示。

package com.mengma.aspectj.xml;import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.mengma.dao.CustomerDao;public class XMLTest {@Testpublic void test() {String xmlPath = "com/mengma/aspectj/xml/applicationContext.xml";ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);// 从spring容器获取实例CustomerDao customerDao = (CustomerDao) applicationContext.getBean("customerDao");// 执行方法customerDao.add();}
}

5. 运行项目并查看结果
使用 JUnit 测试运行 test() 方法,运行成功后,控制台的输出结果如下图所示。
在这里插入图片描述

为了更好地演示异常通知,接下来在 CustomerDaoImpl 类的 add() 方法中添加一行会抛出异常的代码,如“int i=1/0;”,重新运行 XMLTest 测试类,可以看到异常通知执行了。

基于 Annotation 的声明式

在 Spring 中,尽管使用 XML 配置文件可以实现 AOP 开发,但是如果所有的相关的配置都集中在配置文件中,势必会导致 XML 配置文件过于臃肿,从而给维护和升级带来一定的困难。

为此,AspectJ 框架为 AOP 开发提供了另一种开发方式——基于 Annotation 的声明式。AspectJ 允许使用注解定义切面、切入点和增强处理,而 Spring 框架则可以识别并根据这些注解生成 AOP 代理。

Annotation 注解介绍

名称说明
@Aspect用于定义一个切面。
@Before用于定义前置通知,相当于 BeforeAdvice。
@AfterReturning用于定义后置通知,相当于 AfterReturningAdvice。
@Around用于定义环绕通知,相当于MethodInterceptor。
@AfterThrowing用于定义抛出通知,相当于ThrowAdvice。
@After用于定义最终final通知,不管是否异常,该通知都会执行。
@DeclareParents用于定义引介通知,相当于IntroductionInterceptor。

下面使用注解的方式重新实现基于XML的声明式部分的功能。
1. 创建切面类 MyAspect
在 src 目录下创建一个名为 com.mengma.aspectj.annotation 的包,在该包下创建一个切面类 MyAspect,如下所示。

package com.mengma.aspectj.annotation;import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;//切面类
@Aspect
@Component
public class MyAspect {// 用于取代:<aop:pointcut// expression="execution(*com.mengma.dao..*.*(..))" id="myPointCut"/>// 要求:方法必须是private,没有值,名称自定义,没有参数@Pointcut("execution(*com.mengma.dao..*.*(..))")private void myPointCut() {}// 前置通知@Before("myPointCut()")public void myBefore(JoinPoint joinPoint) {System.out.print("前置通知,目标:");System.out.print(joinPoint.getTarget() + "方法名称:");System.out.println(joinPoint.getSignature().getName());}// 后置通知@AfterReturning(value = "myPointCut()")public void myAfterReturning(JoinPoint joinPoint) {System.out.print("后置通知,方法名称:" + joinPoint.getSignature().getName());}// 环绕通知@Around("myPointCut()")public Object myAround(ProceedingJoinPoint proceedingJoinPoint)throws Throwable {System.out.println("环绕开始"); // 开始Object obj = proceedingJoinPoint.proceed(); // 执行当前目标方法System.out.println("环绕结束"); // 结束return obj;}// 异常通知@AfterThrowing(value = "myPointCut()", throwing = "e")public void myAfterThrowing(JoinPoint joinPoint, Throwable e) {System.out.println("异常通知" + "出错了" + e.getMessage());}// 最终通知@After("myPointCut()")public void myAfter() {System.out.println("最终通知");}
}

上述代码中,第 13 行 @Aspect 注解用于声明这是一个切面类,该类作为组件使用,所以要添加 @Component 注解才能生效。第 19 行中 @Poincut 注解用于配置切入点,取代 XML 文件中配置切入点的代码。

在每个通知相应的方法上都添加了注解声明,并且将切入点方法名“myPointCut”作为参数传递给要执行的方法,如需其他参数(如异常通知的异常参数),可以根据代码提示传递相应的属性值。

2. 为目标类添加注解
在 com.mengma.dao.CustomerDaoImpl 目标类中添加注解 @Repository(“customerDao”)。

3. 创建Spring配置文件
在 com.mengma.aspectj.annotation 包下创建 applicationContext.xml 配置文件,如下所示。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:aop="http://www.springframework.org/schema/aop"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-2.5.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-2.5.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"><!--扫描含com.mengma包下的所有注解--><context:component-scan base-package="com.mengma"/><!-- 使切面开启自动代理 --><aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>

上述代码中,首先导入了 AOP 命名空间及其配套的约束,使切面类中的 @AspectJ 注解能够正常工作;第 13 行代码添加了扫描包,使注解生效。需要注意的是,这里还包括目标类 com.mengma.dao.CustomerDaoImpl 的注解,所以 base-package 的值为 com.mengma;第 15 行代码的作用是切面开启自动代理。

4. 创建测试类
在 com.mengma.aspectj.annotation 包下创建一个名为 AnnotationTest 的测试类,如下所示。

package com.mengma.aspectj.annotation;import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;import com.mengma.dao.CustomerDao;public class AnnotationTest {@Testpublic void test() {String xmlPath = "com/mengma/aspectj/xml/applicationContext.xml";ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);// 从spring容器获取实例CustomerDao customerDao = (CustomerDao) applicationContext.getBean("customerDao");// 执行方法customerDao.add();}
}

5. 运行项目并查看结果
使用 JUnit 测试运行 test() 方法,运行成功后,控制台的输出结果如下图所示。
在这里插入图片描述

删除 add() 方法中的“int i=1/0;”,重新运行 test() 方法,此时控制台的输出结果如下图所示。

在这里插入图片描述
从输出结果中可以看出,已成功使用 Annotation 的方式实现了 AOP 开发。与其他方式相比,基于 Annotation 方式实现 AOP 的效果是最方便的方式。

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

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

相关文章

matlab特征方程的根,MATLAB 求解特征方程的根轨迹图稳定性分析

原文&#xff1a;http://tecdat.cn/?p3871根轨迹分析在下文中&#xff0c;我们提供了用于根轨迹分析的强大MATLAB命令的简要描述。读者可能想知道为什么当强大的MATLAB命令可用时&#xff0c;教师强调学习手工计算。对于给定的一组开环极点和零点&#xff0c;MATLAB立即绘制根…

python如何把一张图像的所有像素点的值都显示出来_情人节,教你用 Python 向女神表白...

点击上方 “AirPython”&#xff0c;选择 “加为星标”第一时间关注 Python 技术干货&#xff01;2020年&#xff0c;这个看起来如此浪漫的年份&#xff0c;你还是一个人吗&#xff1f;难不成我还能是一条狗&#xff1f;提醒你一下&#xff0c;后天就是 2月14日了。什么&#x…

Spring事务管理接口

Spring 的事务管理是基于 AOP 实现的&#xff0c;而 AOP 是以方法为单位的。Spring 的事务属性分别为传播行为、隔离级别、只读和超时属性&#xff0c;这些属性提供了事务应用的方法和描述策略。 在 Java EE 开发经常采用的分层模式中&#xff0c;Spring 的事务处理位于业务逻…

倒计时小工具_送你3个倒数计日的小程序,让你不再遗忘重要事

每天我们忙于工作&#xff0c;忙于生活&#xff0c;在很多重要事情&#xff0c;重要人的生日&#xff0c;以及重要有意义的日子总会在忙碌中被遗忘&#xff0c;那么这该怎么办呢&#xff1f;别紧张&#xff0c;小编为你带来3个倒数计日的小程序&#xff0c;让你不再遗忘重要事情…

Spring声明式事务管理

Spring 的事务管理有两种方式&#xff1a;一种是传统的编程式事务管理&#xff0c;即通过编写代码实现的事务管理&#xff1b;另一种是基于 AOP 技术实现的声明式事务管理。由于在实际开发中&#xff0c;编程式事务管理很少使用&#xff0c;所以我们只对 Spring 的声明式事务管…

无法检查指定的位置是否位于cfs上_打印机知识普及:七大原因导致的打印机无法打印及解决方法...

打印机无法打印的原因有很多&#xff0c;如果我们遇到打印机无法打印应该首先从简单到复杂入手。首先必须排除一些最简单的问题&#xff0c;比如打印机是否正常安装。另外打印机内部是不是已经放置有墨盒以及打印纸等&#xff0c;这些基本问题必须排除&#xff0c;另外还有一个…

Spring基于Annotation实现事务管理

在 Spring 中&#xff0c;除了使用基于 XML 的方式可以实现声明式事务管理以外&#xff0c;还可以通过 Annotation 注解的方式实现声明式事务管理。 使用 Annotation 的方式非常简单&#xff0c;只需要在项目中做两件事&#xff0c;具体如下。 1 在 Spring 容器中注册驱动&…

python扩展,用python扩展列

我试图在python中扩展数据帧的某一列。在r中,我将使用这个函数:在python中,我发现df.pivot_table(),但我刚发现一个错误:pandas.pivot_table(df, values Value, index[Day, Money, Product],columns[Account])^更新结果:数据帧没有更改。它只返回相同的数据文件而不进行扩展。考…

基于matlab的pcm系统仿真_深入理解基于RISC-V ISS Spike的仿真系统:探索Spike,pk和fesrv...

Spike, the RISC-V ISA Simulator, implements a functional model of one or more RISC-V processors.Spike is named after the golden spike used to celebrate the completion of the US transcontinental railway.一些同学初接触RISC-V&#xff0c;总逃脱不了被Rocketchip…

单机安装oracle,单机安装oracle系统

一、安装oracle6.9操作系统1、硬盘划分为30G(以下分区皆强制为主分区)/boot 200Mswap 4G/ 剩余全部空间安装下一步的步骤就不说了勾选图形选项基本系统--》备份客户端&#xff0c;大系统性能&#xff0c;存储工具&#xff0c;安全工具&#xff0c;性能工具&#xff0c;目录客户…

redis timeout设置多少合适_热水器怎么调温度?一般热水器温度设置多少度比较合适?...

对于热水器的温度设置您知道怎么操作吗&#xff1f;一般情况下电热水器设置多少度比较合适呢&#xff1f;今天蜜罐蚁装修网小编给大家介绍下热水器如何调节温度&#xff0c;以及热水器调节温度在什么范围比较合适。下面请看小编以美的电热水器某个型号产品举例图文讲解热水器调…

用swing设计一个打地鼠小游戏_这7个风靡欧美的英语小游戏,学会胜过刷100道题!...

精彩导读小编为大家搜罗了一些在国外家喻户晓的语言类小游戏。好的方法胜过刷上100道题&#xff0c;真正让孩子觉得好玩&#xff0c;教学才会事半功倍&#xff01;01Would You Rather...最近牛津大学的面试考题惊天地爆出了一题&#xff1a;你想要变成吸血鬼还是想要变成僵尸&a…

oracle数据库9i安装,Oracle 9i数据库服务器的安装和辅助软件安装教程

安装数据库服务器以Oracle 9i数据库服务器软件的安装过程为例&#xff0c;介绍数据库服务器的安装过程。14.3.1 安装数据库服务器系统环境数据库服务器安装之前&#xff0c;一般都需要检测系统安装环境&#xff0c;以避免系统不支持、内存不够、硬盘空间不足等情况发生。下面从…

oracle 触发器 行级,oracle的行级触发器使用

行级触发器&#xff1a;当触发器被触发时&#xff0c;要使用被插入、更新或删除的记录中的列值&#xff0c;有时要使用操作前、后列的值.:NEW 修饰符访问操作完成后列的值:OLD 修饰符访问操作完成前列的值例1: 建立一个触发器, 当职工表 emp 表被删除一条记录时&#xff0c;把被…

司铭宇老师:如何让企业销售培训效果落地

如何让企业销售培训效果落地 在企业销售培训中&#xff0c;我们经常听到一个词&#xff0c;那就是“落地”。所谓的“落地”&#xff0c;简单来说就是将培训中所学到的知识和技能转化为实际的工作行动&#xff0c;从而提高销售业绩。但是&#xff0c;如何才能让销售培训效果真…

linux 宽字符串,C语言中的多字节字符与宽字符

C语言原本是在英文环境中设计的&#xff0c;主要的字符集是7位的ASCII码&#xff0c;8位的byte(字节)是最常见的字符编码单位。但是国际化软件必须能够表示不同的字符&#xff0c;而这些字符数量庞大&#xff0c;无法使用一个字节编码。C95标准化了两种表示大型字符集的方法&am…

C++函数编译原理和成员函数的实现

C函数的编译 C中的函数在编译时会根据命名空间、类、参数签名等信息进行重新命名&#xff0c;形成新的函数名。这个重命名的过程是通过一个特殊的算法来实现的&#xff0c;称为 名字编码&#xff08;Name Mangling&#xff09;。 Name Mangling 是一种可逆的算法&#xff0c;…

linux 运行选择哪个cpu核,判断Linux进程在哪个CPU核运行的方法

问题&#xff1a;有一个Linux进程运行在多核处理器系统上&#xff0c;如何查看该进程运行在哪个CPU上&#xff1f;方法一&#xff1a;ps 命令可以告诉你每个进程/线程目前分配到的(在“PSR”列)CPU ID。ps -o pid,psr,comm -p 运行结果&#xff1a;PID PSR COMM5357 10 prog输…

C++对象数组

对象数组是什么 数组对象就是大批量实例化对象的一种方法&#xff0c;例如&#xff1a;Student stu 实例化对象&#xff0c;如果有好几百个对象应该怎么办&#xff1f;这时候就用到了对象数组&#xff0c;顾名思义&#xff0c;就是吧所有要实例化的对象都放到一个组里面&#…

镜像安装linux选择内核版本,在CentOS和Ubuntu中安装Linux Kernel 4.13.10

Linus Torvalds 在 10 月 17 日星期五正式发布了稳定版 Linux Kernel 4.13.10&#xff0c;这个最新版本发布了新功能&#xff0c;进行了诸多修复和问题改进。下面将向大家介绍在 CentOS 和 Ubuntu 中手动安装、更新 Linux Kernel 4.13.10 的方法&#xff0c;当然&#xff0c;这…