Spring AOP 入门






4.91/5 (9投票s)
本文将通过使用 Eclipse、Maven、Spring AOP 2.5 等工具,轻松展示 Spring AOP 的基础知识和用法。
- 下载 auth-aop-after.zip - 10.04 KB
- 下载 cracking-aop-around.zip - 10.39 KB
- 下载 search-aop-params.zip - 10.45 KB
- 下载 hardware-aop-before.zip - 13.15 KB
目录
引言
这是我关于 Spring 面向切面编程的第二篇文章。本文将通过使用 Eclipse、Maven、Spring AOP 2.5 等工具,轻松展示 Spring AOP 的基础知识和用法。
等等!为什么选择 Spring AOP 而不是完整的 AspectJ?
“使用能工作的最简单的东西。Spring AOP 比使用完整的 AspectJ 更简单,因为它不需要将 AspectJ 编译器/织入器引入您的开发和构建过程。如果您只需要通知 Spring bean 上的操作执行,那么 Spring AOP 是正确的选择。如果您需要通知 Spring 容器未管理的(例如,通常的领域对象)对象,那么您需要使用 AspectJ。如果您希望通知除简单方法执行之外的连接点(例如,字段获取或设置连接点等),那么您也需要使用 AspectJ。” - 来自 http://static.springsource.org/spring/docs/2.5.x/reference/aop.html。
阅读上述内容后,您将知道这个问题的答案!我们只需要一个非常简单的框架来开始 AOP。最佳选择是流行的那个(这样更容易找到可以帮助我们的人 ^_^”)。
让我们开始面向切面编程!但您需要知道,我只会使用 Spring XML 配置 – 这可能是困难的方式,但很有趣!
什么是面向切面编程
“面向切面编程包括将程序逻辑分解为不同的部分(所谓的关注点,功能的内聚区域)。所有编程范式都支持一定程度的通过提供抽象(例如,过程、模块、类、方法)将关注点分组和封装到独立、独立的实体中,这些抽象可用于实现、抽象和组合这些关注点。但有些关注点会逃避这些实现形式,它们被称为横切关注点,因为它们‘贯穿’程序中的多个抽象。” - 来自 http://en.wikipedia.org/wiki/Aspect-oriented_programming#Overview
不同的部分?抽象,多个抽象?横切关注点?现在您的头疼了,您难以理解 AOP – 别担心 – 这可以简化
AOP 就像 Perl、.NET、Java 等编程语言中的触发器。
这很简单,对吧?在 AOP 之路上看到一点模糊的微光了吗?
也许您已经学过一些 SQL,并且听说过过程和触发器
- 在 SQL 中,您为表或视图创建触发器。
- 您的触发器将针对特定操作(插入、更新等)。
- 并在选定的时间执行(之前、之后等)
AOP 也有类似的东西
- 您创建称为“切面”的东西。
- 您的“切面”将通过检查称为“切入点”的内容来执行。
- 也在选定的时刻执行(之前、之后、环绕等)。
这就是 AOP,但您需要了解 AOP 的术语和定义,例如 OO 风格(包、类、方法等)。
术语与定义
正如每个概念一样,AO 风格也有其术语,在开始之前您需要了解一些。
- 关注点:这是一个战场,任何感兴趣的领域,任何用例(在 UML 中),任何让您感兴趣并希望改变的东西/区域,就像经典的用例“支付”、“用户管理”以及许多其他一样。
- 横切关注点:这是一个可能影响整个应用程序的关注点,应尽可能集中在代码的一个位置,例如“身份验证”或“日志记录”。
- 连接点:程序执行过程中的一个点,例如方法的执行或异常的处理。
- 切面:一个封装关注点的模块。一个切面由切入点、通知体和类型间声明组成。在某些方法中,切面也可能包含类和方法。
- 切入点:匹配连接点的谓词。
- 通知:切面在特定连接点的期望时刻采取的行动——“之前”、“之后”等。
这些不是 AO 风格的所有术语,只是我们开始时需要的。有关更多 Spring AOP 术语,您可以查阅 http://static.springsource.org/spring/docs/2.5.x/reference/aop.html。
切入点!它是什么?
切入点是匹配连接点的谓词。这与模式匹配(对于 Perl 程序员)或正则表达式非常相似。这个谓词或表达式匹配,例如,一个特定名称的方法。示例(匹配所有名称以“list”开头的方法的切入点)
execution ( * adrabi.codeproject.*.GamesImpl.list*( .. ) )
类 GameImpl
中所有名称以“list”开头且带有任何类型参数的方法。
/!\ 噢!我忘了告诉您,Spring AOP 仅限于 bean 中的公共方法,与 AspectJ 不同。您对此没有问题,对吗?
说真的,切入点是切面中重要的一部分。没有匹配正确的切入点,通知就无法触发或执行,否则您将得不到期望的结果。
但是,等等!你为什么一开始就写 execution( )
?你能给我们一个真实的例子吗?
首先,这是一个真实的例子
<aop:config>
<aop:pointcut id="gamesService"
expression="execution(* com.xyz.myapp.service.*.*(..))" />
</aop:config>
其次,在我们的情况下,execution()
是必要的。它是一种切入点类型或(切入点指示器)。此外,切入点不限于单个 execution()。您可以使用 AND “&&”,OR “||”,和 Negated “!” 来组合多个。惊讶吗?
现在是时候为开始 Spring AOP 做准备,配置您的环境了。
环境配置
终于!真实的例子开始生效了。尝试准备一杯美味的咖啡回来!
您需要按照这篇文章安装 Maven 和 SpringIDE 在 Eclipse 中:“Pivot 1.4, Spring and Hibernate is my RPG Game” - https://codeproject.org.cn/KB/java/PivotSpringHibernateGame.aspx(您只需要阅读环境配置部分,然后获胜归来)。
现在您知道如何在 Eclipse 中安装任何东西了 – 非常好 – 您需要安装 Eclipse TDTD 用于监控和视图执行流程。它不是必需的,但理解它的最佳方法是查看“对象”如何交互。
这一点也不难。您只需在您的大脑中打开配置文件,并将变量“use_imagination = 0”更改为“use_imagination = 1” :D 开玩笑。
现在,开始编码。
通知!之前、之后和环绕
配置完成后,我们将开始编写示例代码。对于每个示例,我们创建一个唯一的项目,并使用“execute()
”切入点类型。我们稍后将看到其他类型。通过使用 Maven,我们不需要为每个项目进行特定配置;相同的配置可以用于所有项目。
此外,您需要知道如何创建 Maven 项目并将其添加为 Spring 项目类型(https://codeproject.org.cn/KB/java/PivotSpringHibernateGame.aspx)。
这是所有项目的 POM 配置
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<target>1.5</target>
<source>1.5</source>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>2.5.6</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>2.5.6</version>
</dependency>
<dependency>
<groupId>aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.5.4</version>
<scope>compile</scope>
</dependency>
</dependencies>
前置通知
我们做什么?
我们使用以下方法模拟一个简单的硬件 CRUD 应用程序:
- 一个用于服务的接口:HardwareService.java
- 一个用于实现接口的类:HardwareImpl.java
- 一个用于实体类的简单类:Hardware.java
- 一个用于创建切入点方法的类:HardwareServicePointcuts.java
- 以及一个用于执行应用程序的类:Main.java
- 加上 Spring 配置文件:application.xml
目的是对 Hardware 服务(HardwareImpl.java)应用三个“前置”通知的切面。我们研究结果,每个切面都有一个唯一的切入点,使用 Hardware 服务切入点(HardwareServicePointcuts.java)中的一个唯一方法。切入点的目标是:
- 切入点,用于在 Hardware 服务中的任何方法开始执行时触发名为“
beforeAnyMethodStartedWithList
”的方法,并且方法名以“list”开头。 - 切入点,用于在方法“
remove
”开始执行时触发名为“beforeRemove
”的方法。 - 切入点,用于在 Hardware 服务中任何未命名为“
save
”的方法开始执行时触发名为“beforeIfNotSaveMethod
”的方法。
目标清楚了吗?让我们研究结果和输出,但在我们这样做之前,我只粘贴感兴趣的部分来源。
public interface HardwareService
{
/**
* Save new Hardware
* @param hw
* @return
*/
public Hardware save( Hardware hw );
/**
* Remove Hardware
* @param hw
*/
public void remove( Hardware hw );
/**
* Get Hardware by id
* @param id
* @return
*/
public Hardware get(int id);
/**
* Get list of all Hardware
* @return
*/
public List<Hardware> listAll();
/**
* Get list of Hardware by vendor
* @param vendor
* @return
*/
public List<Hardware> listByVendor(String vendor);
}
这是 Hardware 服务(HardwareService.java)
public class HardwareServicePointcuts
{
/**
* Pointcut for any method in bean Hardware service his name started with 'list'
*/
public void beforeAnyMethodStartedWithList()
{
System.out.println("*** Some methods started with 'list' has " +
"begin executing now! ***");
}
/**
* Pointcut for method remove only in bean Hardware serive
*/
public void beforeRemove()
{
System.out.println("*** Method 'remove' has begin executing now! ***");
}
/**
* Pointcut for any method not named 'save' in bean hardware service
*/
public void beforeIfNotSaveMethod()
{
System.out.println( "*** Method 'save' hasn't begin executing yet! ***" );
}
}
这是当切入点匹配时将触发的方法。
<aop:config>
<aop:aspect id="anyMethodStartedWithList" ref="hardwareServicePointcuts">
<aop:before method="beforeAnyMethodStartedWithList"
pointcut="execution(* adrabi.codeproject.*.*.*.HardwareImpl.list*(..) )" />
</aop:aspect>
<aop:aspect id="MethodRemove" ref="hardwareServicePointcuts">
<aop:before method="beforeRemove"
pointcut="execution(* adrabi.codeproject.*.*.*.
HardwareImpl.remove(adrabi.*.*.*.*.Hardware) )" />
</aop:aspect>
<aop:aspect id="notMethodSave" ref="hardwareServicePointcuts">
<aop:before method="beforeIfNotSaveMethod"
pointcut="!execution(* adrabi.codeproject.*.*.*.HardwareImpl.save(..) )" />
</aop:aspect>
</aop:config>
这是 Spring AOP 的配置。您会注意到三个切面。每个切面都有一个“前置”通知,每个通知都有一个切入点。每个切入点都试图匹配某些内容。
pointcut="!execution(*adrabi.codeproject.*.*.*.HardwareImpl.save(..) )"
您注意到“execution(..)
”前面的否定号“!”了吗?这证实了我在前几章中提到的内容。
现在我们剖析应用程序 main(在 Main
类中,右键单击 > 在上下文菜单中 > 单击 Profile As > Java Application)。
public static void main(String[]$)
{
//init Spring configuration
ApplicationContext context =
new ClassPathXmlApplicationContext("application.xml");
HardwareService hds = (HardwareService) context.getBean("hardwareService");
/*###############[ -- any method started with 'list' -- ]#################*/
System.out.println("\n\n###############[ -- any method " +
"started with 'list' -- ]#################");
//~
List<Hardware> list = hds.listAll();
for( Hardware h : list )
{
System.out.println( h );
}
//~
/*###############[ -- method named 'remove' -- ]#################*/
System.out.println("\n\n###############[ -- method " +
"named 'remove' -- ]#################");
//~
hds.remove( hds.get(2) );
//~
/*###############[ -- method named 'save' -- ]#################*/
System.out.println("\n\n###############[ -- method " +
"named 'save' -- ]#################");
//~
hds.save( new Hardware(4, 5454477877213l, "vendor #4") );
for( Hardware h : list )
{
System.out.println( h );
}
//~
}
剖析成功完成后,我们会得到一个很好的输出,我们可以与类交互进行比较。
###############[ -- any method started with 'list' -- ]#################
*** Some methods started with 'list' has begin executing now! ***
*** Method 'save' hasn't begin executing yet! ***
Hardware :: Identify [1], Serial number [1234567890254] and Vendor [vendor #1]
Hardware :: Identify [2], Serial number [6545665661545] and Vendor [vendor #2]
Hardware :: Identify [3], Serial number [1545422984548] and Vendor [vendor #3]
###############[ -- method named 'remove' -- ]#################
*** Method 'save' hasn't begin executing yet! ***
*** Method 'remove' has begin executing now! ***
*** Method 'save' hasn't begin executing yet! ***
###############[ -- method named 'save' -- ]#################
Hardware :: Identify [1], Serial number [1234567890254] and Vendor [vendor #1]
Hardware :: Identify [3], Serial number [1545422984548] and Vendor [vendor #3]
Hardware :: Identify [4], Serial number [5454477877213] and Vendor [vendor #4]
现在我们用“类交互”打开剖析报告。
我们比较第一个切面“任何以 'list' 开头的方法”,这是序列图的一部分。
从图表中,我们得出结论,匹配率为 100.1% 的输出结果。您可以看到“beforeAnyMethodStartedWithList
”和“beforeIfNotSaveMethod
”这两个方法在 HardwareImpl
类中的“listAll
”方法之前执行。
请记住,我们为 bean/类 HardwareImpl
(HardwareImpl.java)创建了切面。
/!\ 噢!我忘了告诉您,Spring 按配置文件中的顺序触发和/或执行切面。
如果我们有以下配置,结果将与上次剖析不同。试试看,您会看到不同之处。
<aop:config>
<aop:aspect id="MethodRemove" ref="hardwareServicePointcuts">
<aop:before method="beforeRemove"
pointcut="execution(* adrabi.codeproject.*.*.*.
HardwareImpl.remove(adrabi.*.*.*.*.Hardware) )" />
</aop:aspect>
<aop:aspect id="notMethodSave" ref="hardwareServicePointcuts">
<aop:before method="beforeIfNotSaveMethod"
pointcut="!execution(* adrabi.codeproject.*.*.*.HardwareImpl.save(..) )" />
</aop:aspect>
<aop:aspect id="anyMethodStartedWithList" ref="hardwareServicePointcuts">
<aop:before method="beforeAnyMethodStartedWithList"
pointcut="execution(* adrabi.codeproject.*.*.*.HardwareImpl.list*(..) )" />
</aop:aspect>
</aop:config>
也许,其余的都很好很清楚?我只想澄清为什么我们两次为名为“remove”的方法使用切面。
###############[ -- method named 'remove' -- ]#################
*** Method 'save' hasn't begin executing yet! ***
*** Method 'remove' has begin executing now! ***
*** Method 'save' hasn't begin executing yet! ***
正如您在 Main
类中看到的
/*###############[ -- method named 'remove' -- ]#################*/
System.out.println("\n\n###############[ -- method " +
"named 'remove' -- ]#################");
//~
hds.remove( hds.get(2) );
//~
请仔细而缓慢地阅读这段代码:“hds.remove( hds.get(2) );
”。这里我们有两个方法:第一个是“get
”,第二个是“remove
”。“get
”被触发是因为它不叫“save
”和“remove
”,触发了切面 n°3。
下一个通知与前一个相反,我们称之为“after
”。
后置通知
我们做什么?
我们使用以下方法模拟一个简单的身份验证系统:
- 一个用于服务的接口:AuthenticationService.java
- 一个用于实现接口的类:AuthenticationImpl.java
- 一个用于创建切入点方法的类:AuthenticationServicePointcuts.java
- 以及一个用于执行应用程序的类:Main.java
- 加上 Spring 配置文件:application.xml
目标与“前置”通知类似,我们只有两个切面和两个切入点。
- 切入点,用于在方法“
userName
”或“password
”完成执行后触发名为“afterUsernameOrPassword
”的方法。 - 切入点,用于在 Authentication 服务中的任何名为“
logout
”的方法完成执行后触发名为“afterLogout
”的方法。
我们执行一些步骤,就像在“前置”通知中一样。
public interface AuthenticationService
{
/**
* Setting a user name for authentication
* @param userName
*/
public void userName(String userName);
/**
* Setting a password for authentication
* @param password
*/
public void password(String password);
/**
* Try to log-in
* @return
*/
public boolean login();
/**
* Try to log-out
* @return
*/
public boolean logout();
}
这是 Authentication 服务
public class AuthenticationServicePointcuts
{
/**
* Pointcut for methods userName() or password()
*/
public void afterUsernameOrPassword()
{
System.out.println("*** username or password method has exected ***");
}
/**
* Pointcut for method logout
*/
public void afterLogout()
{
System.out.println( "*** you've logout now! see you later, bye! ***" );
}
}
这是当切入点匹配时将触发的方法。
<aop:config>
<aop:pointcut id="p_usernameOrPassword"
expression="execution(* userName(..)) or execution(* password(..))" />
<aop:pointcut id="p_logout" expression="execution(* logout())" />
<aop:aspect id="afterUsernameOrPassword" ref="authServicePointcuts">
<aop:after method="afterUsernameOrPassword" pointcut-ref="p_usernameOrPassword"/>
</aop:aspect>
<aop:aspect id="afterLogout" ref="authServicePointcuts">
<aop:after method="afterLogout" pointcut-ref="p_logout"/>
</aop:aspect>
</aop:config>
您可以看到两件事发生了变化。
- 首先,我们创建了一个单独的切入点。
- 其次,在“
after
”通知中,我们不使用“pointcut”参数,而是用“pointcut-ref”替换它,就像在“前置”通知中一样。
另外,您会注意到在切入点 n°1“usernameOrPassword
”中,我使用了“or”。
expression="execution(* userName(..)) or execution(* password(..))"
Spring AOP 支持“OR”和“||”表达式;您可以选择。
我们剖析主应用程序。
public static void main(String[]$)
{
//init Spring configuration
ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
AuthenticationService auth = (AuthenticationService)context.getBean("authService");
auth.userName("adrabi");
auth.password("no-password");
System.out.println( "Login results is : " + auth.login());
System.out.println( "Logout results is : " + auth.logout());
}
我们得到一个很好的输出。
*** username or password method has exected ***
*** username or password method has exected ***
Login results is : true
*** you've logout now! see you later, bye! ***
Logout results is : true
现在,我们用“UML 2 类交互”打开剖析报告,我们可以看到序列图的这一部分。
完美,与输出非常相似。我们可以看到在“userName
”和“password
”这两个方法中,“afterUsernameOrPassword”方法在执行完成后被触发。
我们跳到最后一个通知,但不是最后一个。
环绕通知
我们做什么?
我们使用以下方法模拟一个简单的串行系统破解:
- 一个用于服务的接口:SerialService.java
- 一个用于实现接口的类:SerialImpl.java
- 一个用于创建切入点方法的类:SerialServicePointcuts.java
- 以及一个用于执行应用程序的类:Main.java
- 加上 Spring 配置文件:application.xml
目标与“前置”通知类似,我们使用两个切入点来破解序列号系统。
- 切入点,用于触发名为“
aroundCheckSerialNumber
”的方法来替换名为“checkSerialNumber
”的方法。 - 切入点,用于触发名为“
aroundSendHasActived
”的方法来替换名为“sendSerialHasActived
”的方法。
我们执行一些步骤,就像在“前置”通知中一样。
public interface SerialService
{
/**
* Check serial number is valid
* @param serial
* @return
*/
public boolean checkSerialNumber(String serial);
/**
* Send serial is actived to vendor
*/
public void sendSerialHasActived();
}
这是 Serial 服务。
public class SerialServicePointcuts
{
/**
* Around and Stop "CheckSerialNumber" method to be executed
*
* @param joinPoint
*/
public boolean aroundCheckSerialNumber( ProceedingJoinPoint joinPoint )
{
System.out.println( "*** The method [" + joinPoint.getSignature().getName() +
"] has been cracked ^_^ ***" );
return true;
}
/**
* Around and Stop "SendIsHasActived" method to be executed
*
* @param joinPoint
*/
public void aroundSendHasActived(ProceedingJoinPoint joinPoint)
{
System.out.println( "*** The method [" +
joinPoint.getSignature().getName() +
"] has been cracked ^_^ ***" );
}
}
这是当切入点匹配时将触发的方法。
<aop:config>
<aop:aspect id="aroundCheckSerialNumber" ref="serialServicePointcuts">
<aop:around method="aroundCheckSerialNumber"
pointcut="execution(* checkSerialNumber(..))"/>
</aop:aspect>
<aop:aspect id="aroundSendHasActived" ref="serialServicePointcuts">
<aop:around method="aroundSendHasActived"
pointcut="execution(* sendSerialHasActived(..))"/>
</aop:aspect>
</aop:config>
这是我们邪恶的 Spring AOP 配置 ^_^,用于破解序列号系统并阻止序列号发送给供应商。
您也可以注释掉它,看看结果会带来什么惊喜。
现在我们剖析 Main
类。
public static void main(String[]$)
{
//init Spring configuration
ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
SerialService serial = (SerialService) context.getBean("serialService");
//serial for test 1893-5714-6364
if( serial.checkSerialNumber(null) )
{
serial.sendSerialHasActived();
}
}
我们得到一个非常好的输出。
*** The method [checkSerialNumber] has been cracked ^_^ ***
*** The method [sendSerialHasActived] has been cracked ^_^ ***
赶快查看类交互。
您可以看到在序列图中,“checkSerialNumber
”和“sendSerialHasActived
”这两个方法已被“aroundCheckSerialNumber
”和“aroundSendHasActived
”替换。很酷,对吧?我个人更喜欢“环绕”通知。
我希望您喜欢经典的“前置”、“后置”和“环绕”通知。
其他通知
我太累了,无法为最后两个通知“返回后”和“抛出后”创建更多项目。您现在知道如何创建自己的切面,现在您也了解了 Spring AOP。我只会介绍它们。
- “返回后”通知:一个连接点正常完成(例如,方法在不抛出异常的情况下返回)后触发/执行的通知。
- “抛出后”通知:方法因抛出异常而退出时触发/执行的通知。
这很容易理解:“返回后”表示方法成功“返回”之后,“抛出后”表示方法因任何异常退出后。但是,“后”通知放在它们之间有什么意义?“后”通知是一个完整完成的方法;换句话说,
“后”通知 = “返回后”通知 + “抛出后”通知。
现在您可以利用前面的项目创建自己的示例,请参考文档。
战场上的参数
到目前为止,我们从未介绍过 AOP 中的参数,现在是时候稍微讨论一下它们了。我们可以将参数从连接点传递到被触发/执行的方法吗?是的,我们可以轻松做到!我将向您展示两种传递参数的方法。
我们是通过 Spring XML 配置传递它们吗?通常,我们使用 Spring XML 配置。我们该怎么做?
我们使用以下方法模拟一个简单的漫画和动画剧集搜索引擎,并保存查询:
- 一个用于服务的接口:SearchService.java
- 一个用于实现接口的类:SearchImpl.java
- 一个用于创建切入点方法的类:SearchServicePointcuts.java
- 以及一个用于执行应用程序的类:Main.java
- 加上 Spring 配置文件:application.xml
使用两个链接到“后”通知的切入点,将参数从连接点传递到被触发/执行的方法。
- 切入点,用于在方法“
bingSearch
”完成执行后触发名为“afterBingSearch
”的方法。 - 切入点,用于在 Authentication 服务中的任何名为“
googleSearch
”的方法完成执行后触发名为“afterGoogleSearch
”的方法。
public interface SearchService
{
/**
* Bing search
*
* @param query
* @param cookie
*/
public void bingSearch(String query, String cookie);
/**
* Google search
*
* @param query
* @param cookie
* @param userId
*/
public void googleSearch(String query, String cookie, String userId);
}
这是 Search 服务源代码。
/**
* Save Bing search query
*
* @param jp
*/
public void afterBingSearch(JoinPoint jp)
{
System.out.println( "*** Save Bing search : [query : " + jp.getArgs()[0] +
", cookie : " + jp.getArgs()[1] + "] ***" );
}
/**
* Save Google search query
*
* @param query
* @param cookie
* @param userId
*/
public void afterGoogleSearch(String query, String cookie, String userId)
{
System.out.println( "*** Save Bing search : [query : " + query +
", cookie : " + cookie + ", userid : " + userId + "] ***" );
}
这是切入点的源代码。
<aop:config>
<aop:aspect id="afterBingSearch" ref="seachServicePointcuts">
<aop:after method="afterBingSearch" pointcut="execution(* bingSearch(..))"/>
</aop:aspect>
<aop:aspect id="afterGoogleSearch" ref="seachServicePointcuts">
<aop:after method="afterGoogleSearch"
pointcut="execution(* googleSearch(..)) and args(query,cookie,userId)"/>
</aop:aspect>
</aop:config>
我们尝试以两种方式传递参数。
- 首先,通过使用“
bingSearch
”方法的参数类型“JoinPoint”。 - 其次,通过在“
googleSearch
”方法中使用 `args(..)` 绑定参数。
这次没有剖析,我们只需要运行我们的 Main
类。
public static void main(String[] $)
{
// init Spring configuration
ApplicationContext context = new ClassPathXmlApplicationContext(
"application.xml");
SearchService search = (SearchService) context.getBean("searchService");
search.bingSearch("ONE PIECE", "some-cookie");
search.googleSearch("Bleach", "some-cookie", "no-userId");
}
最后,我们得到一个很好的输出,您可以看到参数确实已从连接点传递到被触发的方法。
One Piece 452
One Piece 451
*** Save Bing search : [query : ONE PIECE, cookie : some-cookie] ***
Bleach Episode 271
Bleach Episode 270
*** Save Bing search : [query : Bleach, cookie : some-cookie, userid : no-userId] ***
我将通过向您展示其他切入点类型来结束本文。
其他切入点类型列表
这里是您可以在 XML 配置中使用切入点的其余列表。
- within:将匹配的连接点限制在特定类型内(在使用 Spring AOP 时,仅限于在匹配类型中声明的方法的执行)。
- this:限制匹配的连接点(在使用 Spring AOP 时的方法执行),其中 bean 引用(Spring AOP 代理)是给定类型的实例。
- target:限制匹配的连接点(在使用 Spring AOP 时的方法执行),其中目标对象(被代理的应用程序对象)是给定类型的实例。
结论
本文的目的是向您展示如何使用 Spring AOP 并让您熟悉它。Spring AOP 还可以使用“注解”并支持 AspectJ。它不限于 XML 配置。我只是想只使用 XML。
- AOP 只是触发器。
- Spring AOP 可以使用五种类型的通知(前置、后置、环绕、返回后和抛出后)。
- Spring AOP 不限于 XML 配置。
我将让您自己探索 Spring AOP 注解。感谢您热情阅读我的文章,并感谢任何更正和建议。