一、切点表达式详解
在Spring AOP中,切点表达式用于定义在哪些连接点(Join Point)上应用通知(Advice)。常见的切点表达式是execution
表达式,其格式如下:
- 语法:
execution([修饰符] 返回类型 [包.类.方法] (参数))
修饰符
: 可选,指定方法的访问修饰符,如public
、private
等。返回类型
: 必填,指定方法的返回类型,可用*
通配任意类型。包.类.方法
: 可选,指定类和方法的全限定名。*
表示通配任意类或方法。参数
: 必填,指定方法的参数列表,..
表示任意参数。
此外,切点表达式还可以通过逻辑操作符进行组合,如&&
(与)、||
(或)、!
(非)等,使得切点表达式更加灵活和强大。
二、常用切点表达式
-
拦截某个包下所有类的所有方法
- 表达式:
execution(* com.example.pointcut.*.*(..))
- 说明: 拦截
com.example.pointcut
包下所有类的所有方法,方法的返回值、方法名和参数列表均可为任意类型。
- 表达式:
-
拦截所有
public
方法- 表达式:
execution(public * *(..))
- 说明: 拦截所有
public
修饰的方法,方法的返回值和参数列表可以是任意类型。
- 表达式:
-
拦截以
save
开头的方法- 表达式:
execution(* save*(..))
- 说明: 拦截所有方法名以
save
开头的方法,参数列表可以是任意类型。
- 表达式:
-
拦截指定类的指定方法
- 表达式:
execution(public * com.example.pointcut.OrderDao.save(..))
- 说明: 拦截
com.example.pointcut.OrderDao
类中的save
方法,且该方法必须为public
类型,参数列表可以是任意类型。
- 表达式:
-
拦截指定类的所有方法
- 表达式:
execution(* com.example.pointcut.UserDao.*(..))
- 说明: 拦截
com.example.pointcut.UserDao
类中的所有方法,方法的返回值和参数列表可以是任意类型。
- 表达式:
-
拦截指定包及其子包下所有类的所有方法
- 表达式:
execution(* com.example..*.*(..))
- 说明: 拦截
com.example
包及其所有子包下的所有类的所有方法,方法的返回值和参数列表可以是任意类型。
- 表达式:
-
多个表达式的组合使用
- 或 (OR):
execution(* com.example.pointcut.UserDao.save()) || execution(* com.example.pointcut.OrderDao.save())
- 与 (AND):
execution(* com.example.pointcut.UserDao.save()) && execution(* com.example.pointcut.OrderDao.save())
- 非 (NOT):
!execution(* com.example.pointcut.OrderDao.save())
- 或 (OR):
三、综合案例
为了全面演示切点表达式的使用,我们将结合一个综合案例展示如何在实际项目中应用上述表达式。
1. 项目背景
假设你正在开发一个包含用户管理和订单管理模块的业务系统,涉及到UserDao
和OrderDao
两个数据访问对象,以及UserService
和OrderService
两个业务逻辑对象。
UserDao
: 负责用户的增删改查操作。OrderDao
: 负责订单的增删改查操作。UserService
: 提供用户管理的业务逻辑。OrderService
: 提供订单管理的业务逻辑。
2. 实现代码
package com.example.pointcut;
public class UserDao {
public void saveUser() {
System.out.println("Saving user");
}
public void deleteUser() {
System.out.println("Deleting user");
}
}
public class OrderDao {
public void saveOrder() {
System.out.println("Saving order");
}
public void cancelOrder() {
System.out.println("Cancelling order");
}
}
public class UserService {
public void addUser() {
System.out.println("Adding a user");
}
public void deleteUser() {
System.out.println("Deleting a user");
}
}
public class OrderService {
public void placeOrder() {
System.out.println("Placing an order");
}
public void cancelOrder() {
System.out.println("Cancelling an order");
}
}
3. 切点表达式配置
<aop:config>
<!-- 拦截com.example.pointcut包下所有类的所有方法 -->
<aop:pointcut expression="execution(* com.example.pointcut.*.*(..))" id="allMethodsInPackage"/>
<!-- 拦截所有public方法 -->
<aop:pointcut expression="execution(public * *(..))" id="allPublicMethods"/>
<!-- 拦截以save开头的方法 -->
<aop:pointcut expression="execution(* save*(..))" id="saveMethods"/>
<!-- 拦截指定类的指定方法 -->
<aop:pointcut expression="execution(public * com.example.pointcut.OrderDao.save(..))" id="orderSaveMethod"/>
<!-- 拦截指定类的所有方法 -->
<aop:pointcut expression="execution(* com.example.pointcut.UserDao.*(..))" id="userDaoMethods"/>
<!-- 拦截指定包及其子包下所有类的所有方法 -->
<aop:pointcut expression="execution(* com.example..*.*(..))" id="allMethodsInPackageAndSubpackages"/>
<!-- 组合表达式:拦截UserDao和OrderDao中的save方法 -->
<aop:pointcut expression="execution(* com.example.pointcut.UserDao.save(..)) || execution(* com.example.pointcut.OrderDao.save(..))" id="saveMethodsInUserAndOrder"/>
<!-- 组合表达式:排除OrderDao中的save方法 -->
<aop:pointcut expression="!execution(* com.example.pointcut.OrderDao.save(..))" id="notOrderSave"/>
<!-- 为每个切点配置Advice -->
<aop:advisor advice-ref="loggingAdvice" pointcut-ref="allMethodsInPackage"/>
<aop:advisor advice-ref="loggingAdvice" pointcut-ref="allPublicMethods"/>
<aop:advisor advice-ref="loggingAdvice" pointcut-ref="saveMethods"/>
<aop:advisor advice-ref="loggingAdvice" pointcut-ref="orderSaveMethod"/>
<aop:advisor advice-ref="loggingAdvice" pointcut-ref="userDaoMethods"/>
<aop:advisor advice-ref="loggingAdvice" pointcut-ref="allMethodsInPackageAndSubpackages"/>
<aop:advisor advice-ref="loggingAdvice" pointcut-ref="saveMethodsInUserAndOrder"/>
<aop:advisor advice-ref="loggingAdvice" pointcut-ref="notOrderSave"/>
</aop:config>
4. 解释与运行结果
- 所有方法切入: 配置
allMethodsInPackage
会拦截com.example.pointcut
包下的UserDao
和OrderDao
中的所有方法,如saveUser
、deleteUser
、saveOrder
和cancelOrder
。 - 拦截public方法:
allPublicMethods
会拦截所有public
修饰的方法,这包括了所有的公开方法。 - 拦截以
save
开头的方法:saveMethods
拦截了saveUser
、saveOrder
等方法。 - 拦截指定类的指定方法:
orderSaveMethod
仅拦截OrderDao
中的saveOrder
方法。 - 拦截指定类的所有方法:
userDaoMethods
拦截了UserDao
类中的saveUser
和deleteUser
方法。 - 组合表达式:
saveMethodsInUserAndOrder
拦截了UserDao
和OrderDao
中的save
方法。notOrderSave
排除了OrderDao
中的saveOrder
方法,其余方法依然会被拦截。
四、总结
通过这个综合案例,我们详细展示了如何使用Spring AOP的切点表达式来控制应用程序中的方法拦截。理解这些表达式并灵活运用它们,可以极大地提高代码的可维护性和可扩展性。通过组合和排除规则,你可以精确地控制哪些方法需要增强,哪些不需要,从而实现如日志记录、权限校验、事务管理等典型的AOP应用场景。