Spring AOP - AspectJ 注解配置示例

位置:首页>文章>详情   分类: Java教程 > 编程技术   阅读(310)   2023-06-26 07:54:18

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 + AspectJ 示例

在编写任何代码之前,您需要将 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 关键术语

现在让我们定义一些核心 AOP 概念和术语,并与上面的示例相关联。

spring-aop-diagram

1) 方面: 一个关注点的模块化,跨越多个类。事务管理是企业 Java 应用程序中横切关注点的一个很好的例子。

在我们的示例中,我们创建了一个日志方面。要创建切面,您需要在切面类上应用@Aspect 注解并将其注册到 applicationContext.xml 文件中。

@Aspect
public class EmployeeCRUDAspect {
	...
}

这就是您将方面注册到上下文中的方式。

<bean id="loggingAspect" class="com.cundage.demo.aop.EmployeeCRUDAspect" />

请记住首先使用“ ”将 AOP 支持添加到您的上下文中。

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-proxy-object

所以我们现在很好,可以将 spring AOP 的关键术语与实际代码片段联系起来。现在更进一步,让我们列出可在 Spring AOP 中使用的通知类型

  1. Before advice : 在连接点之前执行的建议,但无法阻止执行流继续到连接点(除非它抛出异常)。要使用此建议,请使用上面示例中使用的 @Before 注释。
  2. 返回通知后:在连接点正常完成后执行的通知:例如,如果方法返回且未抛出异常。要使用此建议,请使用 @AfterReturning 注释。
  3. After throw advice : 如果方法因抛出异常而退出,则要执行的建议。要使用此建议,请使用 @AfterThrowing 注释。
  4. 建议之后:无论连接点退出的方式如何(正常或异常返回),都要执行的建议。要使用此建议,请使用上面示例中使用的 @After 注释。
  5. 环绕通知:环绕连接点的通知,例如方法调用。这是最有力的建议。要使用此建议,请使用 @Around 注释。

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

相关阅读

如果您是为数不多的仍然对 JDK 1.4 感兴趣的开发人员之一,或者您正在维护一个遗留的 spring 应用程序,其中 AOP 代码已经写在 XML 配置文件中,那么这篇文章适合您。在这里学习如...
Spring 的关键组件之一是 Aspect-面向编程(AOP)框架。虽然 Spring IoC 容器不依赖于 AOP,这意味着您不需要使用 AOP,但 AOP 补充了 Spring IoC 以...
在上一个教程中,我们了解了spring aop 关键术语和示例。这些我们创建了一个日志方面,然后应用于 UserManager 类。假设您的应用程序中有多个方面,并且它们可以应用于某个方法。当多...
在这个 Sprig boot rest 拦截器示例中,学习将 ClientHttpRequestInterceptor 与 Spring RestTemplate 一起使用,以在 中记录请求和响...
学习使用多文件附加器在spring boot 应用程序中创建多个日志文件。学习使用翻转策略、归档等配置所有文件附加程序。 wiih log4j2 和 logback配置。 1. logback的...
之前,我介绍了spring 3 + hibernate 集成 示例和struts 2 hello world 示例。在本教程中,我将讨论在将 spring 框架与 struts 与 hibern...
在这个 Spring 引导教程中,学习使用 Spring 重试模块 调用远程 API 并重试请求是否由于某种原因失败,例如网络中断、服务器停机、网络故障、死锁等。在这种情况下,我们通常会尝试重试...
工厂 bean 是用作在 IoC 容器 中创建其他 bean 的工厂的 bean。从概念上讲,工厂bean 与工厂方法非常相似,但它是Spring 特定的bean,在bean 构造期间可以被Sp...
学习Spring Boot有以下深度教程,涵盖从基本概念到打包、部署、监控等高级概念。 1. 开始 Spring Boot 简介 spring-boot-starter-parent 示例 sp...
在软件工程中,控制反转 (IoC) 是一种编程技术其中对象耦合在运行时由汇编程序对象绑定,并且通常在编译时使用静态分析是未知的。在这个 spring 教程中,通过示例了解 ioc 和 sprin...