问题描述
我有一个名为 Test 的 Java 项目,其中包含所有方面。我想在另一个 Spring Boot 项目中使用这些方面。我正在研究一个 poc,将测试项目中的方面编织到一个 spring 启动应用程序中。这样做的有效方法是什么以及如何完成,请实施的人提出建议。
Java 项目代码使用方面进行测试
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Secured {
public boolean isLocked() default false;
}
@Aspect
public class SecuredMethodAspect {
@pointcut("@annotation(secured)")
public void callAt(Secured secured) {}
@Around("callAt(secured)")
public Object around(ProceedingJoinPoint pjp,Secured secured) throws Throwable {
if (secured.isLocked()) {
System.out.println(pjp.getSignature().toLongString() + " is locked");
return pjp.proceed();
} else {
return pjp.proceed();
}
}
}
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>aspect</groupId>
<artifactId>test</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>AspectJ-POC</name>
<url>http://maven.apache.org</url>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<java.version>11</java.version>
</properties>
<!-- nickwongdev aspectj maven plugin,this is for weaving of aspects -->
<build>
<plugins>
<plugin>
<groupId>com.nickwongdev</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.12.6</version>
<configuration>
<source>11</source>
<target>11</target>
<complianceLevel>11</complianceLevel>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<!-- aspectj runtime dependency -->
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.6</version>
</dependency>
</dependencies>
</project>
解决方法
在克隆之前浏览您的项目时,我获得了一些快速的第一印象:
- 您不应在编译时编织场景中同时使用 Lombok 和本机 AspectJ,请参阅 my answer here。
- 可能的解决方法是多阶段编译,即首先使用 Java + Lombok,然后使用 AspectJ 进行编译后编织,请参阅 my answer here。或者,使用
delombok
以首先生成将 Lombok 注释扩展为等效源代码的源代码,然后使用 AspectJ 编译生成的源。 - 再看一遍,您的示例方面模块似乎没有使用任何 Lombok,因此您不需要该依赖项。但可能你的真实项目确实如此,因此我在上面发表了评论。
- 当然,您可以按照您的建议使用编译时编织来编织您的切面。但是,在这种情况下,您需要正确配置 AspectJ Maven 插件,而您没有这样做。除了主应用程序之外,您需要在 Spring Boot 模块中告诉它在哪里可以找到方面库以及要编织哪些依赖项。你有没有读过插件文档,例如章节 Weaving classes in jars、Using aspect libraries 和 Multi-module use of AspectJ?我还在这里回答了大量相关问题。
- 但实际上,将方面编织到 Spring 应用程序中的首选方法是使用加载时编织 (LTW),如Spring manual 中所述。这应该更容易配置,并且您不需要任何 AspectJ Maven 插件,并且在编译期间可能也不会出现任何 Lombok 问题。
从您的示例项目以及您的 AspectJ Maven 配置中没有任何 aspectLibraries
和 weaveDependencies
部分来看,您根本没有费心阅读任何文档,这让我想知道您在你试图让它发挥作用的一个月。
我想发表评论,解释您为什么要使用 CTW 而不是 LTW。我能想到的唯一原因是除了您的本机方面之外,您还希望在您的 Spring 应用程序中使用 Spring AOP 方面。但是如果你激活了原生的 AspectJ LTW,你就不能同时使用 Spring AOP。如果这不是问题,我推荐 LTW,因为它在 Spring 中有很好的记录和测试。 CTW 也没有问题,但在 POM 中更难理解和管理。
更新: 好的,我不想因为您使用 CTW 的原因而再等,而是决定向您展示一个 LTW 解决方案。我所做的是:
- 从您的 Spring 项目中删除 AspectJ Maven 插件和 AspectJ 工具
- 添加
src/main/resources/org/aspectj/aop.xml
,将其配置为使用您的方面并定位您的基础包 + 子包 - 修复切入点以也使用您的基础包 + 子包,因为在您的基础包中没有类。这是典型的初学者错误。您需要将
execution(* com.ak..*(..))
与..
一起使用,而不仅仅是com.ak.*
。
我使用的非必需化妆品是:
- 从 Spring Boot 应用程序的 main 方法调用
context.getBean(TestController.class).mainRequest()
以自动生成某些方面输出,以避免必须使用curl
或网络浏览器来调用本地 URL。 - 将 Spring 应用程序用作通过 try-with-resources 自动关闭的应用程序,以便在创建所需的日志输出后关闭应用程序。
- 更改方面以同时记录连接点,以便在日志中查看它实际拦截了哪些方法。否则所有日志行看起来都一样,这不是很有帮助。
- 使用
try-finally
以确保继续执行原始方法后的日志消息在出现异常时也被记录。
差异看起来像这样(我希望你能阅读差异):
diff --git a/aspect-test-project/src/main/java/com/ak/aspect/MethodLogAspect.java b/aspect-test-project/src/main/java/com/ak/aspect/MethodLogAspect.java
--- a/aspect-test-project/src/main/java/com/ak/aspect/MethodLogAspect.java (revision Staged)
+++ b/aspect-test-project/src/main/java/com/ak/aspect/MethodLogAspect.java (date 1626233247623)
@@ -7,14 +7,14 @@
@Aspect
public class MethodLogAspect {
- @Around("@annotation( MethodLog) && (execution(* com.ak.*(..)))")
+ @Around("@annotation(MethodLog) && execution(* com.ak..*(..))")
public Object wrap(final ProceedingJoinPoint joinPoint) throws Throwable {
- System.out.println("***Aspect invoked before calling method***");
-
- final Object obj = joinPoint.proceed();
-
- System.out.println("***Aspect invoked after calling method***");
-
- return obj;
+ System.out.println("[BEFORE] " + joinPoint);
+ try {
+ return joinPoint.proceed();
+ }
+ finally {
+ System.out.println("[AFTER] " + joinPoint);
+ }
}
}
diff --git a/src/main/java/com/ak/ParentprojectSpringbootApplication.java b/src/main/java/com/ak/ParentprojectSpringbootApplication.java
--- a/src/main/java/com/ak/ParentprojectSpringbootApplication.java (revision Staged)
+++ b/src/main/java/com/ak/ParentprojectSpringbootApplication.java (date 1626233555873)
@@ -1,14 +1,17 @@
package com.ak;
+import com.ak.controller.TestController;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.ConfigurableApplicationContext;
@SpringBootApplication(scanBasePackages = { "com.ak.*" })
-//@SpringBootApplication
public class ParentprojectSpringbootApplication {
public static void main(String[] args) {
- SpringApplication.run(ParentprojectSpringbootApplication.class,args);
+ try (ConfigurableApplicationContext context = SpringApplication.run(ParentprojectSpringbootApplication.class,args)) {
+ context.getBean(TestController.class).mainRequest();
+ }
}
}
diff --git a/parentproject-springboot/pom.xml b/parentproject-springboot/pom.xml
--- a/parentproject-springboot/pom.xml (revision Staged)
+++ b/parentproject-springboot/pom.xml (date 1626232421474)
@@ -71,13 +71,6 @@
<version>1.9.6</version>
</dependency>
- <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjtools -->
- <dependency>
- <groupId>org.aspectj</groupId>
- <artifactId>aspectjtools</artifactId>
- <version>1.9.6</version>
- </dependency>
-
<dependency>
<groupId>com.ak.aspect</groupId>
<artifactId>aspect-test-project</artifactId>
@@ -100,24 +93,6 @@
</configuration>
</plugin>
- <plugin>
- <groupId>com.nickwongdev</groupId>
- <artifactId>aspectj-maven-plugin</artifactId>
- <version>1.12.6</version>
- <configuration>
- <source>11</source>
- <target>11</target>
- <complianceLevel>11</complianceLevel>
- </configuration>
- <executions>
- <execution>
- <goals>
- <goal>compile</goal>
- <goal>test-compile</goal>
- </goals>
- </execution>
- </executions>
- </plugin>
</plugins>
</build>
为方便起见,以下是完整的更改文件:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.2</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>com.ak</groupId>
<artifactId>parentproject-springboot</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>parentproject-springboot</name>
<description>Test project for Spring Boot</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.ak.dependency</groupId>
<artifactId>dependencyprojet</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<!-- aspectj runtime dependency -->
<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>
<dependency>
<groupId>com.ak.aspect</groupId>
<artifactId>aspect-test-project</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
<!DOCTYPE aspectj PUBLIC "-//AspectJ//DTD//EN" "http://www.eclipse.org/aspectj/dtd/aspectj.dtd">
<aspectj>
<weaver options="-verbose -showWeaveInfo">
<!-- only weave classes in our application-specific packages -->
<include within="com.ak..*"/>
</weaver>
<aspects>
<aspect name="com.ak.aspect.MethodLogAspect"/>
</aspects>
</aspectj>
package com.ak.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
@Aspect
public class MethodLogAspect {
@Around("@annotation(MethodLog) && execution(* com.ak..*(..))")
public Object wrap(final ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("[BEFORE] " + joinPoint);
try {
return joinPoint.proceed();
}
finally {
System.out.println("[AFTER] " + joinPoint);
}
}
}
package com.ak;
import com.ak.controller.TestController;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
@SpringBootApplication(scanBasePackages = { "com.ak.*" })
public class ParentprojectSpringbootApplication {
public static void main(String[] args) {
try (ConfigurableApplicationContext context = SpringApplication.run(ParentprojectSpringbootApplication.class,args)) {
context.getBean(TestController.class).mainRequest();
}
}
}
现在您只需确保将 JVM 参数 -javaagent:/path/to/aspectjweaver-1.9.6.jar
添加到您的 Java 命令行以激活本机 AspectJ LTW。有关如何在 Spring 中以更复杂的方式配置 LTW 的更多详细信息,请阅读 Spring 手册的 Using AspectJ with Spring Applications 部分。
控制台日志应如下所示:
[AppClassLoader@3764951d] info AspectJ Weaver Version 1.9.6 built on Tuesday Jul 21,2020 at 13:30:08 PDT
[AppClassLoader@3764951d] info register classloader jdk.internal.loader.ClassLoaders$AppClassLoader@3764951d
[AppClassLoader@3764951d] info using configuration /C:/Users/alexa/Documents/java-src/SO_AJ_SpringCTWMultiModule_68040124/parentproject-springboot/target/classes/org/aspectj/aop.xml
[AppClassLoader@3764951d] info register aspect com.ak.aspect.MethodLogAspect
[AppClassLoader@3764951d] warning javax.* types are not being woven because the weaver option '-Xset:weaveJavaxPackages=true' has not been specified
[RestartClassLoader@1f385e10] info AspectJ Weaver Version 1.9.6 built on Tuesday Jul 21,2020 at 13:30:08 PDT
[RestartClassLoader@1f385e10] info register classloader org.springframework.boot.devtools.restart.classloader.RestartClassLoader@1f385e10
[RestartClassLoader@1f385e10] info using configuration /C:/Users/alexa/Documents/java-src/SO_AJ_SpringCTWMultiModule_68040124/parentproject-springboot/target/classes/org/aspectj/aop.xml
[RestartClassLoader@1f385e10] info register aspect com.ak.aspect.MethodLogAspect
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__,| / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.5.2)
(...)
[RestartClassLoader@1f385e10] weaveinfo Join point 'method-execution(com.ak.dependency.model.AccountInfo com.ak.service.TestService.incomingRequest())' in Type 'com.ak.service.TestService' (TestService.java:20) advised by around advice from 'com.ak.aspect.MethodLogAspect' (MethodLogAspect.java)
[RestartClassLoader@1f385e10] weaveinfo Join point 'method-execution(com.ak.dependency.model.AccountInfo com.ak.dependency.Route.accountInfo())' in Type 'com.ak.dependency.Route' (Route.java:12) advised by around advice from 'com.ak.aspect.MethodLogAspect' (MethodLogAspect.java)
[RestartClassLoader@1f385e10] weaveinfo Join point 'method-execution(com.ak.dependency.model.BalanceInfo com.ak.dependency.Pipeline.balanceInfo())' in Type 'com.ak.dependency.Pipeline' (Pipeline.java:11) advised by around advice from 'com.ak.aspect.MethodLogAspect' (MethodLogAspect.java)
(...)
Controller
[BEFORE] execution(AccountInfo com.ak.service.TestService.incomingRequest())
[BEFORE] execution(AccountInfo com.ak.dependency.Route.accountInfo())
[BEFORE] execution(BalanceInfo com.ak.dependency.Pipeline.balanceInfo())
[AFTER] execution(BalanceInfo com.ak.dependency.Pipeline.balanceInfo())
[AFTER] execution(AccountInfo com.ak.dependency.Route.accountInfo())
[AFTER] execution(AccountInfo com.ak.service.TestService.incomingRequest())
(...)