在本教程中,我们了解到 AspectJ 中的切入点是一个强大的工具,可以精确指定方面建议的应用位置(例如方法、类或字段)。
使用 AspectJ 可以轻松创建切入点以定位主包或特定包中的所有方法。我们还可以根据需要排除某些包。
此方法对于在多个类和方法中应用相同的逻辑(例如日志记录或安全检查)非常有用,而无需重复代码。通过为所需的包定义切入点,我们可以让您的代码保持干净且易于维护。
AspectJ 是一款功能强大的工具,可用于处理 Java 应用程序中的日志记录、安全性和事务管理等横切关注点。一种常见用例是将一个方面应用于特定包中的所有方法。在本教程中,我们将学习如何在 AspectJ 中创建一个与包中的所有方法匹配的切入点,并提供分步代码示例。
要了解有关 AspectJ 的更多信息,请查看我们全面的 AspectJ 教程。
Maven 依赖项
运行 AspectJ 程序时,类路径应包含类和方面,以及 AspectJ 运行时库 aspectsjrt:
<dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>1.9.22.1</version> </dependency>
|
除了 AspectJ 运行时依赖项之外,我们还需要包含 aspectsjweaver 库,以便在加载时向 Java 类引入建议:
<dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.22.1</version> </dependency>
|
什么是切入点?
AspectJ 中的切入点是一个核心概念,它定义了方面应在代码中的何处应用。方面管理横切关注点,如日志记录、安全或事务管理。切入点指定程序执行过程中应运行方面建议(或操作)的特定点(称为连接点)。可以使用不同的表达式来识别这些连接点,包括方法签名、类名或特定包。
1. 与切入点相关的关键概念
连接点是程序执行中可以应用方面的具体时刻。这包括方法调用、执行、对象实例化和字段访问。建议是方面在连接点采取的操作。它可以发生在连接点之前 ( @Before )、之后 ( @After ) 或周围 ( @Around )。切入点表达式是一种定义应匹配哪些连接点的声明。此表达式遵循特定语法,使其能够定位方法执行、字段访问等。
3.2. 切入点语法
切入点表达式通常有两个关键组成部分:连接点类型和签名模式。连接点类型定义事件,包括方法调用、方法执行或构造函数执行。签名模式使用类、包、参数或返回类型标准来标识特定方法或字段。
4.切入点表达式
要创建与特定包中的所有方法匹配的切入点,我们可以使用以下表达式:
execution(* com.jdon.aspectj..*(..))
|
以下是此表达式的详细说明:
- execution:切入点指示符,指定我们的目标是方法执行。
- *:表示任意返回类型的通配符。
- com.jdon.aspectj.. *:匹配com.jdon.aspectj包及其子包中的任何类。
*:匹配任何方法参数。
4.1. 包中所有方法的日志记录方面
让我们创建一个示例方面,记录名为com.jdon.aspectj 的包中所有方法的执行情况:
@Before("execution(* com.jdon.aspectj..*(..))") public void pointcutInsideAspectjPackage(JoinPoint joinPoint) { String methodName = joinPoint.getSignature().getName(); String className = joinPoint.getTarget().getClass().getSimpleName(); System.out.println( "Executing method inside aspectj package: " + className + "." + methodName ); }
|
@Before中的切入点表达式针对com.jdon.aspectj包及其子包内的所有方法。
我们在服务包中创建UserService :
@Service public class UserService { public void createUser(String name, int age) { System.out.println("Request to create user: " + name + " | age: " + age); } public void deleteUser(String name) { System.out.println("Request to delete user: " + name); } }
|
当UserService方法运行时,切面pointcutInsideAspectjPackage()将记录这两个方法。现在,让我们测试一下代码:
@Test void testUserService() { userService.createUser("create new user john", 21); userService.deleteUser("john"); }
|
切面pointcutInsideAspectjPackage()应该在UserService类中的createdUser()和deleteUser()执行之前被调用:
Executing method inside aspectj package: UserService.createUser Request to create user: create new user john | age: 21 Executing method inside aspectj package: UserService.deleteUser Request to delete user: john
|
接下来,让我们 在名为存储库包的另一个包中创建另一个名为UserRepository 的类:
@Repository public class UserRepository { public void createUser(String name, int age) { System.out.println("User: " + name + ", age:" + age + " is created."); } public void deleteUser(String name) { System.out.println("User: " + name + " is deleted."); } }
|
当执行UserRepository类中的方法时,切面pointcutInsideAspectjPackage()将记录这两种方法。现在,让我们测试一下代码:
@Test void testUserRepository() { userRepository.createUser("john", 21); userRepository.deleteUser("john"); }
|
方面pointcutInsideAspectjPackage()应该在UserService类中的createdUser()和deleteUser()方法执行之前调用:
Executing method inside aspectj package: UserRepository.createUser User: john, age:21 is created Executing method inside aspectj package: UserRepository.deleteUser User: john is deleted.
|
4.2. 子包中所有方法的日志记录方面
让我们创建一个示例方面,记录名为com.jdon.aspectj.service 的包中所有方法的执行情况:
@Before("execution(* com.jdon.aspectj.service..*(..))") public void pointcutInsideServicePackage(JoinPoint joinPoint) { String methodName = joinPoint.getSignature().getName(); String className = joinPoint.getTarget().getClass().getSimpleName(); System.out.println( "Executing method inside service package: " + className + "." + methodName ); }
|
@Before注释内的切入点表达式(execution(* com.jdon.aspectj.service..*(..)))与com.jdon.aspectj.service包内的所有方法匹配。
接下来,我们在服务包中创建另一个名为MessageService的类来提供额外的测试用例:
@Service public class MessageService { public void sendMessage(String message) { System.out.println("sending message: " + message); } public void receiveMessage(String message) { System.out.println("receiving message: " + message); } }
|
当执行MessageService的任何方法时,切面pointcutInsideServicePackage()将记录这两种方法。现在,让我们测试一下代码:
@Test void testMessageService() { messageService.sendMessage("send message from user john"); messageService.receiveMessage("receive message from user john"); }
|
先前的方面pointcutInsideAspectjPackage()和pointcutInsideServicePackage()应该在调用MessageService类中的方法sendMessage()和acceptMessage()之前调用:
Executing method inside aspectj package: MessageService.sendMessage Executing method inside service package: MessageService.sendMessage sending message: send message from user john Executing method inside aspectj package: MessageService.receiveMessage Executing method inside service package: MessageService.receiveMessage receiving message: receive message from user john
|
4.3. 通过排除特定包来记录方面
让我们创建一个示例方面,它将排除名为com.jdon.aspectj.service的特定包的执行:
@Before("execution(* com.jdon.aspectj..*(..)) && !execution(* com.jdon.aspectj.repository..*(..))") public void pointcutWithoutSubPackageRepository(JoinPoint joinPoint) { String methodName = joinPoint.getSignature().getName(); String className = joinPoint.getTarget().getClass().getSimpleName(); System.out.println( "Executing method without sub-package repository: " + className + "." + methodName ); }
|
@Before注释内的切入点表达式(execution(* com.jdon.aspectj..*(..)) && !execution(* com.jdon.aspectj.repository..*(..)))匹配com.jdon.aspectj包及其子包内的所有方法,但不包括子包存储库。
现在,让我们重新运行之前的单元测试。名为pointcutWithoutSubPackageRepository()的方面应该在 aspectsj 包中的所有方法之前调用,同时排除存储库子包:
Executing method inside aspectj package: UserService.createUser Executing method inside service package: UserService.createUser Executing method without sub-package repository: UserService.createUser Request to create user: create new user john | age: 21 Executing method inside aspectj package: UserService.deleteUser Executing method inside service package: UserService.deleteUser Executing method without sub-package repository: UserService.deleteUser Request to delete user: john Executing method inside aspectj package: UserRepository.createUser User: john, age:21 is created. Executing method inside aspectj package: UserRepository.deleteUser User: john is deleted. Executing method inside aspectj package: MessageService.sendMessage Executing method inside service package: MessageService.sendMessage Executing method without sub-package repository: MessageService.sendMessage sending message: send message from user john Executing method inside aspectj package: MessageService.receiveMessage Executing method inside service package: MessageService.receiveMessage Executing method without sub-package repository: MessageService.receiveMessage receiving message: receive message from user john
|