【问题标题】:How to weave AspectJ java project into Spring boot project如何将AspectJ java项目编织到Spring boot项目中
【发布时间】:2021-06-18 18:44:48
【问题描述】:

我有一个名为 Test 的 java 项目,其中包含所有方面。我想在另一个 Spring Boot 项目中使用这些方面。我正在开发一个 poc,将 Test 项目中的各个方面编织到 Spring Boot 应用程序中。这样做的有效方法是什么以及如何做到这一点,请实施过的人提出建议。

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>

【问题讨论】:

  • 您在 Spring 上下文中工作,而 Spring 有自己的切面框架。请解释为什么你不想使用它。它可以按照您希望的方式开箱即用。您对使用原生 AspectJ 有哪些特殊要求?
  • @kriegaex 我想开发一个原生方面框架,我想在三个不同的模块中使用它,一个是springboot模块,另一个只是java模块,第三个是struts模块。从设计的角度来看,我可能是正确的,有什么好的方法可以做到这一点。
  • AspectJ Maven 插件文档包含关于multi-module projects 的一章。在我自己的也适用于更新的 Java 版本的 fork 中,我有fixed the the faulty graphics。我之前也在这里回答了几个相关的问题。如果您在阅读本文后仍有疑问或找不到我的相关答案,请告诉我。
  • @kriegaex 我有一个spring boot 应用程序A,还有其他非spring 应用程序B 用作spring boot 应用程序A 中的依赖项。我在非spring 应用程序C 中有所有与日志记录相关的注释和方面。我想让我的注释在 Spring 相关类和非 Spring 相关类中都有效。这是最好的方法,我不能在这里发布代码,因为它受到限制。如果您有任何类似的示例,请提供任何类似的示例。
  • @kriegaex 我不明白如何使用您在我的用例中提到的文档。它适用于我的场景还是我应该采用不同的方法。我正在尝试这个一个多月仍然在我的脑海中旋转,无法弄清楚并使其工作。请发布您拥有的示例 ID。

标签: spring-boot logging aspectj load-time-weaving compile-time-weaving


【解决方案1】:

在克隆项目之前浏览您的项目时,我得到了一些快速的第一印象:

  • 您不应在编译时编织场景中同时使用 Lombok + 本机 AspectJ,请参阅 my answer here
  • 可能的解决方法是多阶段编译,即首先使用 Java + Lombok,然后使用 AspectJ 进行编译后编织,请参阅 my answer here。或者,使用delombok 以首先生成将 Lombok 注释扩展为等效源代码的源代码,然后使用 AspectJ 编译生成的源代码。
  • 再看一遍,您的示例方面模块似乎没有使用任何 Lombok,因此您不需要该依赖项。但可能你真正的项目确实如此,因此我在上面发表了评论。
  • 当然,您可以按照您的建议使用编译时编织来编织您的方面。但是,在这种情况下,您需要正确配置 AspectJ Maven 插件,而您没有这样做。除了主应用程序之外,您还需要在 Spring Boot 模块中告诉它在哪里可以找到方面库以及要编织哪些依赖项。您是否阅读过插件文档,例如章节Weaving classes in jarsUsing aspect librariesMulti-module use of AspectJ?我也已经在这里回答了大量相关问题。
  • 但实际上,将方面编织到 Spring 应用程序中的首选方法是使用加载时编织 (LTW),如 Spring manual 中所述。这应该更容易配置,并且您不需要任何 AspectJ Maven 插件,并且在编译期间也可能不会有任何 Lombok 问题。

从您的示例项目以及您的 AspectJ Maven 配置中没有任何 aspectLibrariesweaveDependencies 部分来看,您根本没有费心阅读任何文档,这让我想知道您在一个月内做了什么试图让它工作。

我想发表评论,解释为什么要使用 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 或 Web 浏览器来调用本地 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())
(...)

【讨论】:

  • 我在原始项目中使用 lombok,而不是在这些项目中。我将通过你对龙目岛的建议。我浏览了很多次文档,我无法在我的项目中实施,因为我不了解其中的大部分内容,我是开发新手,也是 AspectJ 的新手。
  • 如果您是一般的开发新手,在使用 Spring 等复杂框架、Lombok 等魔术工具和 AOP 等概念之前,可能先学习编程基础知识 - 特别是,如果您想将所有这些结合起来. Spring 文档非常好。如果你不理解它,这是一个明显的迹象,表明你正在尝试使用你不理解的东西。给自己一个学习和成长为开发人员的机会。没有基础,你的事业就会像纸牌屋一样摇摇欲坠,最终分崩离析。现在,不要绝望,我在这里为您提供帮助。
  • Lombok + AspectJ 问题是一个普遍问题,但不是没有将任何方面编织到您的应用程序中的原因。所以再一次,就像我在回答中所说的那样:我会发表评论,解释为什么要使用 CTW 而不是 LTW。 我现在明白为什么 Spring AOP 不是你的选择,如果你想将方面应用于非 Spring 组件。美好的。但为什么是 CTW,而不是 LTW?在我提出解决方案之前,我想知道。
  • 好的,我更新了我的答案,向你解释如何使用LTW,因为它比CTW更简单。要使 CTW 工作,您需要在所有 3 个模块中使用 AspectJ Maven。
  • 出于这个原因,我正在尝试使用 CTW 而不是 LTW,如果我想在 spring 组件和普通 java 类中同时使用它,则如果我使用 CTW,则注释不起作用。如果我在方面类上使用 Component,它仅在不是在普通 java 类中创建 bean 的地方工作,反之亦然,如果不是使用 @component 并在 aop.xml 中定义方面,它在普通 java 类中工作,而不是在 spring 组件中。我知道CTW会解决这个问题,我可能错了。如果我使用 LTW,请提出解决此问题的任何解决方案。
猜你喜欢
  • 2016-02-15
  • 1970-01-01
  • 1970-01-01
  • 2019-11-24
  • 2018-11-01
  • 1970-01-01
  • 1970-01-01
  • 2017-10-03
  • 2018-11-12
相关资源
最近更新 更多