Spring 的关键组件之一是 Aspect-面向编程(AOP)框架。虽然 Spring IoC 容器不依赖于 AOP,这意味着您不需要使用 AOP,但 AOP 补充了 Spring IoC 以提供非常强大的中间件解决方案。就像 OOP 中模块化的关键单元是类一样,在 AOP 中模块化的单元是方面。方面支持关注点的模块化(您也可以将其理解为横切关注点),例如跨越多个类型和对象的事务管理。
AspectJ 已经成长为一个完整且流行的 AOP 框架,Spring 支持使用 AspectJ 注解编写的 POJO 切面在其 AOP 框架中。由于越来越多的 AOP 框架支持 AspectJ 注释,因此您的 AspectJ 样式方面更有可能在其他支持 AspectJ 的 AOP 框架中重用。
不幸的是,AOP 术语不是很直观,所以我将从创建示例应用程序开始,然后将术语与示例中的用法联系起来。
在编写任何代码之前,您需要将 Spring AOP 依赖项导入到您的项目中。
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.1.4.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>4.1.4.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>4.1.4.RELEASE</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>1.6.11</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.6.11</version> </dependency>
在这个例子中,我采用了 EmployeeManager
类的一个非常基本的实现,它有一些方法,这些方法基本上应该选择/修改 EmployeeDTO
对象到数据库中。
EmployeeDTO.java
public class EmployeeDTO { private Integer id; private String firstName; private String lastName; //Setters and Getters }
EmployeeManager.java
import java.util.ArrayList; import java.util.List; import org.springframework.stereotype.Component; @Component public class EmployeeManager { public EmployeeDTO getEmployeeById(Integer employeeId) { System.out.println("Method getEmployeeById() called"); return new EmployeeDTO(); } public List<EmployeeDTO> getAllEmployee() { System.out.println("Method getAllEmployee() called"); return new ArrayList<EmployeeDTO>(); } public void createEmployee(EmployeeDTO employee) { System.out.println("Method createEmployee() called"); } public void deleteEmployee(Integer employeeId) { System.out.println("Method deleteEmployee() called"); } public void updateEmployee(EmployeeDTO employee) { System.out.println("Method updateEmployee() called"); } }
然后我创建了一个日志记录方面,它将记录执行了哪个方法。
EmployeeCRUDAspect.java
@Aspect public class EmployeeCRUDAspect { @Before("execution(* EmployeeManager.getEmployeeById(..))") public void logBeforeV1(JoinPoint joinPoint) { System.out.println("EmployeeCRUDAspect.logBeforeV1() : " + joinPoint.getSignature().getName()); } @Before("execution(* EmployeeManager.*(..))") public void logBeforeV2(JoinPoint joinPoint) { System.out.println("EmployeeCRUDAspect.logBeforeV2() : " + joinPoint.getSignature().getName()); } @After("execution(* EmployeeManager.getEmployeeById(..))") public void logAfterV1(JoinPoint joinPoint) { System.out.println("EmployeeCRUDAspect.logAfterV1() : " + joinPoint.getSignature().getName()); } @After("execution(* EmployeeManager.*(..))") public void logAfterV2(JoinPoint joinPoint) { System.out.println("EmployeeCRUDAspect.logAfterV2() : " + joinPoint.getSignature().getName()); } }
applicationContext.xml
文件具有以下配置,以使用注解配置启用 AOP 和 IoC 容器。
<aop:aspectj-autoproxy /> <context:component-scan base-package="com.cundage.demo.aop" /> <bean id="loggingAspect" class="com.cundage.demo.aop.EmployeeCRUDAspect" />
现在测试 AOP 配置和其他东西。
import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class TestAOP { @SuppressWarnings("resource") public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("com/cundage/demo/aop/applicationContext.xml"); EmployeeManager manager = context.getBean(EmployeeManager.class); manager.getEmployeeById(1); manager.createEmployee(new EmployeeDTO()); } } Output: EmployeeCRUDAspect.logBeforeV1() : getEmployeeById EmployeeCRUDAspect.logBeforeV2() : getEmployeeById Method getEmployeeById() called EmployeeCRUDAspect.logAfterV1() : getEmployeeById EmployeeCRUDAspect.logAfterV2() : getEmployeeById EmployeeCRUDAspect.logBeforeV2() : createEmployee Method createEmployee() called EmployeeCRUDAspect.logAfterV2() : createEmployee
很好,AOP 配置成功。现在继续学习 AOP 术语。
现在让我们定义一些核心 AOP 概念和术语,并与上面的示例相关联。
1) 方面: 一个关注点的模块化,跨越多个类。事务管理是企业 Java 应用程序中横切关注点的一个很好的例子。
在我们的示例中,我们创建了一个日志方面。要创建切面,您需要在切面类上应用@Aspect 注解并将其注册到 applicationContext.xml
文件中。
@Aspect public class EmployeeCRUDAspect { ... }
这就是您将方面注册到上下文中的方式。
<bean id="loggingAspect" class="com.cundage.demo.aop.EmployeeCRUDAspect" />
请记住首先使用“
2) Join point :程序执行过程中的一个点,比如方法的执行或者异常的处理。在 Spring AOP 中,一个连接点总是代表一个方法执行。
在我们的示例中,EmployeeManager
中定义的所有方法都是关节点。
3) 建议: 方面在特定连接点采取的行动。不同类型的通知包括“around”、“before”和“after”通知。许多 AOP 框架,包括 Spring,将建议建模为拦截器,在连接点周围维护一个拦截器链。
在我们的示例中,所有 logBefore()
和 logAfter()
方法都是建议。
4) 切入点: 匹配连接点的谓词。通知与切入点表达式相关联,并在切入点匹配的任何连接点运行(例如,执行具有特定名称的方法)。切入点表达式匹配的连接点概念是 AOP 的核心,Spring 默认使用 AspectJ 切入点表达式语言。
在我们的示例中,在 @Before
和 @After
注释中传递的表达式(即“execution(* EmployeeManager.getEmployeeById(..))
”)是切入点。
5) 简介:代表一个类型声明额外的方法或字段。 Spring AOP 允许您向任何建议的对象引入新接口(和相应的实现)。例如,您可以使用引入使 bean 实现 IsModified 接口,以简化缓存。
我将为此创建一个单独的示例。
6) 目标对象: 被一个或多个方面建议的对象。也称为建议对象。由于 Spring AOP 是使用运行时代理实现的,因此该对象将始终是代理对象。
在我们的示例中,EmployeeManager
是建议对象,因此它是目标对象。
7) AOP 代理: AOP 框架创建的对象,用于实现方面契约(建议方法执行等)。在 Spring Framework 中,AOP 代理将是 JDK 动态代理或 CGLIB 代理。
在我们的示例中,当我们请求 EmployeeManager
类的 bean 引用时,会创建一个代理对象。您可以在下图中看到代理类,这是运行时调试器的屏幕截图。
所以我们现在很好,可以将 spring AOP 的关键术语与实际代码片段联系起来。现在更进一步,让我们列出可在 Spring AOP 中使用的通知类型。
Around Advice 可以在方法调用前后执行自定义行为。它还负责选择是继续到连接点还是通过返回自己的返回值或抛出异常来简化建议的方法执行。环绕建议可以写成如下。
@Around("execution(* EmployeeManager.getEmployeeById(..))") public void logAround(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("Write code for before advise"); joinPoint.proceed(); //continue to called method i.e. EmployeeManager.getEmployeeById() System.out.println("Write code for after advise"); }
这就是本介绍性教程的全部内容。我将发布更多教程,其中包含有关 spring aspectj aop 概念 的更多详细信息。
快乐学习!!
参考:Spring文档
标签2: Spring AOP地址:https://www.cundage.com/article/spring-aop-aspectj-example-tutorial-using-annotation-config.html