使用 Java 和 SpringBoot 进行面向切面编程
编程范式
为什么选择AOP?
Maven依赖项
建议
切点
方面
启用AOP
结论
面向切面编程演示
延伸阅读
由 Mux 赞助的 DEV 全球展示挑战赛:展示你的项目!
这篇博客涵盖了面向切面编程 (AOP)的基础知识 。我将展示一个简单的AOP示例:像记录REST调用这样的常见任务可以用一个 Advice来通用地定义,然后可以通过 切面 将其应用到代码中的各种目标位置(即所谓的 切入点 ) 。
心急的朋友:你们可以在我的Github仓库里找到最终结果🏎️
编程范式
早在 1996 年 Java 1.0 发布时,Java 开发人员都对 面向对象编程 (也称为 OOP)充满热情 。虽然 OOP 是 Java 的基础驱动力之一,但自那时以来,这门编程语言本身已经取得了长足的进步,现在支持多种 编程范式 。
以下是 Java 支持的主要编程范式列表(按历史顺序排列):
程序化编程
面向对象编程 (OOP)
函数式编程
面向切面编程 (AOP)
本文将重点介绍面向 切面编程 (AOP) ,并展示如何创建切面。您将学习 AOP 的基础知识以及如何将其与 SpringBoot 结合使用。
为什么选择AOP?
大多数大公司都有编程规范,我们公司也不例外。其中一条规范规定,每次 REST 端点执行都必须记录日志(Java 方法名 + 参数)。
以下是解决方法:
@RestController
public class MyRestController {
@GetMapping ( path = "/api/hello/" )
public String hello () {
System . out . println ( "Method [hello] gets called with 0 parameters" );
return "Hello world!" ;
}
}
Enter fullscreen mode
Exit fullscreen mode
上面的代码片段执行以下操作:
@RestController确保 SpringBoot 知道此类包含 REST 端点。
@GetMapping:一种响应 HTTP GET 请求的方法
System.out.println(...)请遵守上述编码准则
return值:该方法仅返回一个字符串类型的问候消息。
在实际应用中,你会在许多不同的类中遇到许多这样的 REST 调用。 在所有这些方法中都使用 完全相同的日志记录方式会 非常繁琐 。此外,如果编码规范稍有变化,你还需要在每个方法中修改日志消息。
这时AOP就派上用场了:借助AOP,我们可以 在代码的不同位置轻松添加通用功能,而不会干扰现有代码 。用学术术语来说, AOP的 核心在于分离横切关注点。🤓 更通俗易懂地说,AOP实现了跨对象通用任务的模块化。😎
Maven依赖项
要在SpringBoot中使用 AspectJ 注解 进行 AOP 开发 ,我们需要在代码中导入以下依赖项 pom.xml:
<dependency>
<groupId> org.springframework.boot</groupId>
<artifactId> spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId> org.aspectj</groupId>
<artifactId> aspectjweaver</artifactId>
</dependency>
Enter fullscreen mode
Exit fullscreen mode
建议
让我们创建一个函数,以通用的方式执行所需的日志记录:
public void logMethodExecution ( JoinPoint joinPoint ) {
String method = joinPoint . getSignature (). getName ();
String params = Arrays . toString ( joinPoint . getArgs ());
System . out . println ( "Method [" + method + "] gets called with parameters " + params );
}
Enter fullscreen mode
Exit fullscreen mode
这个通用函数被称为 Advice(通知) 。请注意,它可以记录 任何 方法的名称和参数 ;让我们逐步分析 Advice:
JoinPoint此对象包含有关连接点的 所有信息 ,也就是我们将要插入切面的“位置”。在本例中,这将是 我们要为其创建日志消息的 REST 方法。
joinPoint.getSignature()并 joinPoint.getArgs()提取方法签名以及调用参数
System.out.println(...)执行必要的日志记录
切点
那么,我们应该把上面的 Advise 方法插入到哪里呢?我们希望跟踪每个 REST 端点。虽然有很多方法可以标记 REST 端点,但我们选择使用 自定义注解 来定义 切入点 :
@Before ( "@annotation(LogMethod)" )
public void logMethodExecution ( JoinPoint joinPoint ) {...}
Enter fullscreen mode
Exit fullscreen mode
如您所见,切入点定义只有一行代码:
@Before我们在 REST 调用得到响应之前运行 Advice。
@annotation我们通过注解来标记切入点。
LogMethod这是我们自定义注解的名称
现在我们可以用自定义注解来标记我们的 REST 方法了:
@LogMethod
@GetMapping ( path = "/api/hello/" )
public String hello () {
return "Hello world!" ;
}
Enter fullscreen mode
Exit fullscreen mode
请注意,我们在 REST 方法前添加了注解 @LogMethod。此外,我们移除了方法内部的日志记录,现在由切面(Aspect)来完成。
方面
一个 方面(Aspect) 是由一个切入点(Pointcut)加上一个建议(Advice)构成的。所以,让我们把这两者结合起来,得到:
@Aspect
@Component
public class LoggingAspect {
@Before ( "@annotation(LogMethod)" )
public void logMethodName ( JoinPoint joinPoint ) {
String method = joinPoint . getSignature (). getName ();
String params = Arrays . toString ( joinPoint . getArgs ());
System . out . println ( "Method [" + method + "] gets called with parameters " + params );
}
}
Enter fullscreen mode
Exit fullscreen mode
我们目前掌握的情况如下:
@AspectSpringBoot 要求所有切面都位于带有 @aspect注解的类中。
@Before(...): 切入点
logMethodName(...){...}建议
所以我们在这里所做的,就是将之前展示的 Pointcut 表达式和 Advice 结合起来,然后把所有内容封装到一个类中。开香槟庆祝吧,我们的 Aspect 已经完成并可以正常工作了🥂
启用AOP
最后,我们需要 为 Spring 配置启用 AspectJ :
@Configuration
@EnableAspectJAutoProxy
public class AspectConfig {
}
Enter fullscreen mode
Exit fullscreen mode
请记住,在使用 Spring 时,我们需要能够操作 Bean。目前,我们的 @RestController类只包含 REST 调用逻辑,而不包含 Advice。Spring 可以 为这类 Bean 创建包含此附加逻辑(Advice)的 代理 @EnableAspectJAutoProxy,这是通过以下方式实现的:
结论
就这样!你现在就拥有了一个完整的AOP示例😀💪🍾
我们实现了一个通知机制,当带有注解的方法 @LogMethod执行时就会运行。得益于我们的面向切面编程(AOP)方法,我们可以将此注解添加到未来的 REST 方法中,这些方法随后也会收到相同切面的通知!
请查看Github上的完整示例:
面向切面编程演示
这是一个使用面向切面编程 (AOP) 的简单 Web 服务。REST 方法由多个切面提供建议。
依赖关系
Java JDK 15
Maven:
spring-boot-starter-aop
aspectjweaver
REST 调用方面
用法
mvn clean install
mvn spring-boot:run
Enter fullscreen mode
Exit fullscreen mode
GET http://localhost:8080/api/greeting/{name}
GET http://localhost:8080/api/order/{menu}
Enter fullscreen mode
Exit fullscreen mode
示例宽高比输出
Method [greeting] gets called with parameters [John]
Exeution took [21ms]
Enter fullscreen mode
Exit fullscreen mode
这个 GitHub 仓库还包含第二个建议 @Around:每次调用 REST 方法时,我们都会记录执行时间。这对于衡量、监控和比较不同 REST 端点的性能非常有用。
感谢阅读,如果您有任何反馈或疑问,请留言!😀
延伸阅读
如果你感兴趣,这里还有一些相关阅读材料:
AOP入门和通知类型 : 在我们的示例中,我们使用了@Before 类型的通知 。或者,您可以使用 @After 、 @Around 或 @AfterThrowing 。
切入点 是与一组连接点匹配的谓词。我们使用了注解驱动的切入点,但 Spring 支持更多指示符类型。例如,Spring 支持 execution方法名必须匹配给定模式的切入点。
AOP 代理 :Spring 官方文档中的解释。
文章来源:https://dev.to/pmgysel/aspect-oriented-programming-with-java-and-springboot-2nlg