【校招VIP】什么是面向切面编程(Aspect-Oriented Programming)

1天前 收藏 0 评论 0 java开发

【校招VIP】什么是面向切面编程(Aspect-Oriented Programming)

转载声明:文章来源https://blog.csdn.net/kljyrx/article/details/143706496 

前言

面向切面编程(Aspect-Oriented Programming)的缩写是AOP 。在 Spring Framework 中,AOP 是一个常用的功能,特别适合于分离关注点、简化业务逻辑。

一、了解AOP

1. 解释

切面(Aspect) 是 AOP 的核心概念之一,指的是一组影响多个类的功能,比如日志记录、事务管理、安全控制等。这些功能通常分散在多个模块中,面向切面编程允许将这些交叉关注点(Cross-Cutting Concerns)集中管理。

AOP 的目的是通过将这些功能从业务逻辑中分离出来,减少代码的耦合性和重复性。例如,使用 AOP 可以把日志记录逻辑统一定义为一个切面,而不是在每个方法里都手动写日志。

2. 工作方式

AOP 通过 切入点(Pointcut) 和 通知(Advice) 来实现:

切入点:指定在哪些位置应用切面逻辑,比如某个方法执行之前或之后。

通知:在切入点处执行的具体动作,比如在方法调用前打印日志。

3. 使用场景

日志记录:自动记录方法的输入参数和返回值。
事务管理:在方法执行过程中自动管理事务的开始和提交/回滚。
性能监控:统计方法执行的时间。

二、代码示例

下面是一个简单的 Spring Boot 项目中使用 AOP 实现自动日志记录的示例:

1. 添加依赖

确保你的 pom.xml 文件包含 Spring AOP 依赖:

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>

2. 创建日志切面类

这是实现日志记录的核心部分,定义一个切面类来记录方法的调用信息。

package com.example.demo.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class LoggingAspect {

private static final Logger logger = LoggerFactory.getLogger(LoggingAspect.class);

// 在方法执行之前记录日志
@Before("execution(* com.example.demo.service.*.*(..))")
public void logBeforeMethod(JoinPoint joinPoint) {
logger.info("调用方法: " + joinPoint.getSignature().getName());
logger.info("参数: " + java.util.Arrays.toString(joinPoint.getArgs()));
}

// 在方法成功返回后记录日志
@AfterReturning(pointcut = "execution(* com.example.demo.service.*.*(..))", returning = "result")
public void logAfterMethod(JoinPoint joinPoint, Object result) {
logger.info("方法执行完成: " + joinPoint.getSignature().getName());
logger.info("返回值: " + result);
}
}

3. 创建一个简单的服务类

示例中的服务类包含一些方法,供切面类拦截和记录日志。

package com.example.demo.service;

import org.springframework.stereotype.Service;

@Service
public class SampleService {

public String sayHello(String name) {
return "Hello, " + name;
}

public int add(int a, int b) {
return a + b;
}
}

4. 创建一个控制器类

为了测试 AOP 日志记录,可以创建一个简单的控制器类。

package com.example.demo.controller;

import com.example.demo.service.SampleService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class SampleController {

private final SampleService sampleService;

public SampleController(SampleService sampleService) {
this.sampleService = sampleService;
}

@GetMapping("/hello")
public String sayHello(@RequestParam String name) {
return sampleService.sayHello(name);
}

@GetMapping("/add")
public int add(@RequestParam int a, @RequestParam int b) {
return sampleService.add(a, b);
}
}

5. 运行项目并测试

启动 Spring Boot 应用程序,并访问以下端点:

·http://localhost:8080/hello?name=John
·http://localhost:8080/add?a=5&b=10

6. 观察控制台日志

你会在控制台看到自动生成的日志,包含方法名称、参数和返回值。例如:

调用方法: sayHello
参数: [John]
方法执行完成: sayHello
返回值: Hello, John

解释

@Aspect: 指定一个类为切面类。
@Before: 在方法执行之前执行的通知。
@AfterReturning: 在方法执行成功返回后执行的通知。
JoinPoint: 提供对目标方法的详细描述,如方法名称和参数。

这个简单的 AOP 配置实现了基本的自动日志记录,可以根据需要扩展到更复杂的应用场景。

三、 好处

是的,使用 AOP(面向切面编程)的一个主要好处就是可以集中管理像日志记录这样的功能,而不用在每个类中重复编写相同的代码。AOP 提供了一种在应用程序中横切关注点(如日志记录、事务管理、权限控制等)的简洁方式,帮助你分离核心业务逻辑和附加功能。

主要优点包括:

减少代码冗余:无需在每个类中手动编写日志记录代码,AOP 可以自动为指定的方法执行日志记录。
易于维护:如果需要修改日志记录逻辑,只需在一个地方更改,而不是逐个修改每个类中的代码。
提高可读性:将核心业务逻辑与横切关注点(如日志记录)分离,使代码更易于理解和维护。
通过 AOP,你可以将一些公共功能(例如日志记录、性能监控或安全检查)从业务代码中抽离出来,从而使代码更加模块化和清晰。

四、实现切面的静态化

上面的代码引入了spring-boot-starter-aop,基于代理模式来实现的切片,可能会有性能问题。AspectJ 可以用来实现切面的静态化。与 Spring AOP 在运行时动态生成代理对象不同,AspectJ 是一个支持 编译时织入 和 类加载时织入 的 AOP 框架。它能够在编译阶段就将切面代码织入目标代码,因此生成的字节码是直接包含切面逻辑的,而不需要在运行时依赖代理机制。

1. AspectJ 的静态织入方式:

编译时织入(Compile-time Weaving):通过在编译过程中,AspectJ 会自动将切面逻辑织入到目标代码中。这种方式是最常见的静态织入方式,能够在编译阶段处理。
类加载时织入(Load-time Weaving):通过在类加载时,将切面逻辑织入到字节码中。这种方式适用于某些场景,通常与 类加载器 配合使用。

2. 与 Spring AOP 的区别:

Spring AOP 是基于代理模式的,它使用动态代理来在运行时拦截方法调用,通常需要依赖于运行时的代理对象。
AspectJ 则允许在编译时、类加载时就将切面代码织入到目标对象,生成的字节码已经包含了切面的逻辑,不需要代理。

3. 使用 AspectJ 静态化 AOP 的好处:

性能优势:由于没有动态代理的开销,静态织入的 AOP 可以在性能上有所提升,特别是在高并发的场景下。
功能增强:AspectJ 能够支持更复杂的 AOP 需求,例如 方法参数注入、全局切点定义 等,超越了 Spring AOP 的功能范围。

4. 使用 AspectJ 的典型步骤:

添加 AspectJ 依赖。
在代码中定义 Aspect 和 Pointcut。
配置编译时织入工具,如 Maven 插件或者 AspectJ 编译器。
编译代码时,切面会被织入到目标类的字节码中。

五、切面的静态化demo

下面是一个简单的 AspectJ 示例,展示了如何使用 AspectJ 进行编译时 AOP。

1. 添加依赖

首先,需要在 Maven 项目中加入 AspectJ 的依赖。你可以使用下面的依赖配置:

<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.6</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.13.0</version>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>

2. 创建切面类

在切面类中,我们定义了需要切入的逻辑,使用 @Aspect 注解标识它是一个切面类,使用 @Before 注解来指定在方法执行前执行的操作。

package com.example.demo;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class LoggingAspect {

// 这里定义了一个切点,匹配所有以 'service' 结尾的方法
@Before("execution(* com.example.demo.service.*Service.*(..))")
public void logBefore() {
System.out.println("方法执行前的日志");
}
}

3. 创建目标类

这是目标类,Service 类中包含我们要切入的方法:

package com.example.demo.service;

import org.springframework.stereotype.Service;

@Service
public class MyService {

public void myMethod() {
System.out.println("执行 myMethod()");
}
}

4. 配置 Spring 启动类

确保启用了 AspectJ 的支持。可以通过 @EnableAspectJAutoProxy 注解启用 AspectJ 代理:

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@SpringBootApplication
@EnableAspectJAutoProxy
public class DemoApplication {

public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}

5. 编译和运行

使用 mvn clean install 编译项目。
启动 Spring Boot 应用。
调用 MyService 中的 myMethod() 方法时,切面的 logBefore() 方法会在目标方法执行之前输出日志。

输出结果:

方法执行前的日志
执行 myMethod()

6. 总结

·上面的示例演示了如何在 Spring Boot 项目中使用 AspectJ 进行 AOP,编译时织入切面代码。可以看出只有引入的依赖不同,代码层面并无不同。
·AspectJ 通过静态编织切面,可以在编译时将切面逻辑直接嵌入到目标类的字节码中,而不需要在运行时生成代理对象

C 0条回复 评论

帖子还没人回复快来抢沙发