0

我有一个名为 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>
4

1 回答 1

1

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

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

我使用的非必需化妆品是:

  • 从 Spring Boot 应用程序的 main 方法调用context.getBean(TestController.class).mainRequest(),以便自动产生某些方面的输出,以避免不得不使用curlWeb 浏览器来调用本地 URL。
  • 通过 try-with-resources 将 Spring 应用程序用作可自动关闭的应用程序,以便在创建所需的日志输出后关闭应用程序。
  • 更改方面以同时记录连接点,以便在日志中查看它实际拦截的方法。否则所有日志行看起来都一样,这不是很有帮助。
  • 用于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())
(...)
于 2021-07-12T03:26:04.587 回答