读书笔记《building-restful-web-services-with-spring-5-second-edition》AOP和记录器控件
在本章中,我们将学习 Spring Aspect-Oriented Programming(AOP< /span>) 和记录器控件,包括它们的理论和实现。我们将把 Spring AOP 集成到我们现有的 REST API 中,并介绍 AOP 和记录器控件如何让我们的生活更轻松。
在本章中,我们将介绍以下主题:
- Spring AOP theory
- Implementation of Spring AOP
- Why do we need logger controls?
- How do we implement logger controls?
- Integrating Spring AOP and logger controls
面向方面的编程是一个概念,我们在不修改代码本身的情况下向现有代码添加新行为。当涉及到日志记录或方法身份验证时,AOP 概念非常有用。
在 Spring 中有很多方法可以使用 AOP。让我们不要太详细,因为这将是一个需要讨论的大话题。在这里,我们将只讨论 @Before
切入点以及如何在我们的业务逻辑中使用 @Before
。
AOP 中的术语执行意味着在 @Aspect
注释本身中有一个切入点,它不依赖于控制器 API。另一种方法是您必须在 API 调用中明确提及 注释。下个话题讲一下显式切入点:
在这个切入点中,我们使用了 @Before
注释,并且它使用 execution(* com.packtpub.restapp.HomeController.testAOPWithoutAnnotation())
,这意味着这个切入点将专注于一个特定的方法,
方法,在我们的例子中。HomeController
类中的 testAOPWithoutAnnotation
对于 AOP 相关的工作,我们可能需要将依赖添加到我们的 pom.xml
文件中,如下所述:
前面的依赖将带来所有面向方面的类来支持我们在本章中的 AOP 实现。
Note
@Aspect
:这个注解用来使类支持切面。在 Spring 中,方面可以使用 XML 配置或注解来实现,例如 @Aspect
。@Component
:这个注解将使根据 Spring 的组件扫描器规则可扫描的类。通过使用 @Component
和 @Aspect
提及这个类,我们告诉 Spring 扫描这个类并将其识别为一个方面。
HomeController
类的代码如下:
在这里,我们简单地创建一个新方法来测试我们的 AOP。您可能不需要创建新的 API 来测试我们的 AOP。只要您提供适当的方法名称,就可以了。为了方便读者,我们在 HomeContoller
类中创建了一个名为 testAOPExecution
的新方法。
到目前为止,我们已经看到了一种基于执行的 AOP 方法,它可以用于 one 或更多方法。但是,在某些地方,我们可能需要保持实现的简单性以提高可见性。这将帮助我们在需要的地方使用它,并且它不依赖于任何方法。我们称之为基于显式注解的 AOP。
为了使用这个 AOP 概念,我们可能需要创建一个接口来帮助我们实现我们需要实现的目标。
TokenRequired
只是我们的 Aspect
类的基本接口。它将提供给我们的 Aspect
类,如下所述:
Note
@Retention
:保留策略决定在什么时候应该丢弃注释。在我们的例子中,RetentionPolicy.RUNTIME
将在运行时为 JVM 保留。其他保留策略如下:SOURCE
:仅与源代码一起保留,编译时丢弃。代码一旦编译,注解就没有用了,所以不会写入字节码。CLASS
:会一直保留到编译时,运行时会被丢弃.@Target
:此注解适用于类级别并在运行时匹配。目标注解可用于收集目标对象。
下面的 tokenRequiredWithAnnotation
方法将为我们的切面实现业务逻辑。为了保持逻辑简单,我们刚刚提供了 System.out.println(..)
。稍后,我们将主要逻辑添加到方法中:
在前面的代码中,我们创建了一个名为 tokenRequiredWithAnnotation
的方法,并提供了 TokenRequired
接口作为该方法的参数。我们可以在 @annotation(tokenRequired)
方法的顶部看到名为 @Before
的注解。每次在任何方法中使用 @TokenRequired
注释时都会调用此方法。可以看到注解用法如下:
之前的 AOP 方法和 this 的主要区别是 @TokenRequired
。在旧的 API 调用程序中,我们没有明确提及任何 AOP 注释,但我们必须在此调用程序中提及 @TokenRequired
,因为它会调用适当的 AOP 方法。另外,在这个 AOP 方法中,我们不需要提及 execution
,就像我们在前面的 execution(* com.packtpub.restapp .HomeController.testAOPWithoutAnnotation())
方法。
假设您要限制 UserContoller
方法中的 deleteUser
选项。删除用户的人应该拥有正确的 JWT 令牌。如果他们没有令牌,我们不会让 他们 删除任何用户。在这里,我们将首先有一个 packt
主题以 create 令牌。
可以调用 http://localhost:8080/security/generate/token?subject=packt
生成的令牌 API 来生成令牌。
当我们在主题中使用 packt
时,它会生成 eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJwYWNrdCIsImV4cCI6MTUwOTk0NzY2Mn0.hIsVggbam0pRoLOnSe8L9GQS4IFFfFklborwJVthsmz0
令牌。
现在,我们必须创建一个 AOP 方法来限制用户,要求他们在 delete
调用的标头中包含令牌:
查看前面的代码,您可以看到 AOP 中的 JWT 集成。是的,我们已经将 JWT 令牌验证部分与 AOP 集成在一起。所以以后,如果有人调用@TokenRequired
-annotated API,它会首先来到AOP方法并检查token匹配。如果令牌为空、不匹配或过期,我们将收到错误消息。下面将讨论所有可能的错误。
现在,我们可以在 UserController
类的 API 调用中开始使用 @TokenRequired
注释。因此,每当调用此 deleteUser
方法时,它都会转到 JWT
,在执行 API 方法本身之前检查切入点。通过这样做,我们可以确保在没有令牌的情况下不会调用 deleteUser
方法。
UserController
类的代码如下:
如果token为空或null,则会抛出以下错误:
如果令牌匹配,它将显示结果而不会引发任何错误。您将看到以下结果:
如果我们不在标头中提供任何标记,则可能会引发以下错误:
如果令牌过期,您将收到以下错误:
当我们 需要跟踪特定进程的输出时,日志记录会很有帮助。在将应用程序部署到服务器后出现问题时,它将帮助我们验证过程或找到错误的根本原因。如果没有记录器,如果发生任何事情,将很难跟踪和找出问题。
我们可以在我们的应用程序中使用许多日志框架; Log4j 和 Logback 是大多数应用程序中使用的两个主要框架。
SLF4j 是一个 API,可帮助我们在部署期间选择 Log4j 或 Logback 或 any 其他 JDK 日志记录。 SLF4j 只是一个抽象层,它为使用我们的日志 API 的用户提供了自由。如果有人想在他们的实现中使用 JDK 日志记录或 Log4j,SLF4j 将帮助他们在运行时插入所需的框架。
如果我们创建了一个不能被某人用作库的最终产品,我们可以直接实现 Log4j 或 Logback。但是,如果 我们 有一个可以用作库的代码,那么使用 SLF4j 会更好,所以用户可以按照他们想要的任何日志记录。
Logback 是 Log4j 的更好替代方案,并为 SLF4j 提供原生支持。
前面我们提到Logback比Log4j更可取;这里我们将讨论如何实现 Logback 日志框架。
Logback 中包含三个模块:
logback-core
: Basic logginglogback-classic
: Improved logging and SLF4j supportlogback-access
: Servlet container support
logback-core
模块是 Log4j 框架中其他两个模块的基础。 logback-classic
模块是 Log4j 的改进版本,具有更多功能。此外,logback-classic
模块原生实现了 SLF4j API。由于这种原生支持,我们可以切换到不同的日志框架,例如 Java Util Logging (JUL ) 和 Log4j。
logback-access
模块为 servlet 容器 such 如 Tomcat/ Jetty,专门提供 HTTP 访问日志设施。
为了在我们的应用程序中使用 Logback,我们需要 logback-classic
依赖。但是,logback-classic
依赖已经在 spring-boot-starter
依赖中可用。我们可以使用项目文件夹中的依赖树 (mvn dependency:tree
) 来检查这一点:
在检查项目文件夹中的依赖树时,我们将获得所有依赖关系的整个树。下面是我们可以看到 spring-boot-starter
依赖下的 logback-classic
依赖的部分:
由于必要的依赖文件已经可用,我们不需要为 Logback 框架实现添加任何依赖项。
由于 SLF4j 定义了这些日志记录级别,因此实现 SLF4j 的人应该调整 SFL4j 的日志记录级别。日志记录级别如下:
TRACE
: Detailed comments that might not be used in all casesDEBUG
: Useful comments for debugging purposes in productionINFO
: General comments that might be helpful during developmentWARN
: Warning messages that might be helpful in specific scenarios such as deprecated methodsERROR
: Severe error messages to be watched out for by the developer
让我们将日志配置添加到 application.properties
文件中:
在前面的配置中,我们对 Spring Framework 和我们的应用程序都使用了日志记录配置。根据我们的配置,它将为 Spring Framework 打印 ERROR
,为我们的应用程序打印 INFO
。
让我们在类中添加一个 Logger
;在我们的例子中,我们可以使用UserController
。我们必须导入 org.slf4j.Logger
和 org.slf4j.LoggerFactory
。我们可以检查以下代码:
在前面的代码中,我们引入了 _logger
实例。我们使用 UserController
类作为 _logger
实例的参数。
现在,我们必须使用 _logger
实例来打印我们想要的消息。在这里,我们使用 _logger.info()
来打印消息:
在前面的代码中,我们使用了各种记录器来打印消息。当您重新启动服务器并调用 http://localhost:8080
REST API 时,您将在控制台中看到以下输出:
从日志中可以看出,类名会一直在日志中,以标识日志中的具体类。由于我们没有提到任何日志记录模式,记录器采用默认模式来打印类的输出。如果需要,我们可以更改配置文件中的模式以获取自定义日志记录。
在前面的代码中,我们使用了不同的日志记录级别来打印消息。日志级别是有限制的,所以根据业务需求和实现,我们必须配置我们的日志级别。
在我们的记录器配置中,我们只使用了控制台打印选项。我们还可以提供一个选项,以便在我们想要的任何地方打印到外部文件。