【问题标题】:How to get access to WEB-INF/lib/ jars?如何访问 WEB-INF/lib/jars?
【发布时间】:2021-06-30 00:42:12
【问题描述】:

我有带有 Spring BootAspectJ 的 Gradle 项目。

想要直接从 WEB-INF/libs(Spring Boot 定位所有依赖项)动态加载 aspectjweaverspring-instrument javaagents

Gradle 依赖项:

代理加载器:

public class AgentLoader {

private static final Logger LOGGER = LoggerFactory.getLogger(AgentLoader.class);

public static void loadJavaAgent() {
    if (!isAspectJAgentLoaded()) {
        LOGGER.warn("Aspect agent was not loaded!");
    }
}

public static boolean isAspectJAgentLoaded() {
    try {
        Agent.getInstrumentation();
    } catch (NoClassDefFoundError e) {
        return false;
    } catch (UnsupportedOperationException e) {
        LOGGER.info("Dynamically load AspectJAgent");
        return dynamicallyLoadAspectJAgent();
    }
    return true;
}

public static boolean dynamicallyLoadAspectJAgent() {
    String nameOfRunningVM = ManagementFactory.getRuntimeMXBean().getName();
    int p = nameOfRunningVM.indexOf('@');
    String pid = nameOfRunningVM.substring(0, p);
    try {
        VirtualMachine vm = VirtualMachine.attach(pid);
        String jarFilePath = AgentLoader.class.getClassLoader().getResource("WEB-INF/libs/aspectjweaver-1.9.6.jar").toString();
        vm.loadAgent(jarFilePath);
        jarFilePath = AgentLoader.class.getClassLoader().getResource("WEB-INF/libs/spring-instrument-5.3.2.jar").toString();
        vm.loadAgent(jarFilePath);
        vm.detach();
    } catch (Exception e) {
        LOGGER.error("Exception while attaching agent", e);
        return false;
    }
    return true;
 }
}

但是在null中发现getResource()的返回值

处理此问题的最佳解决方案是什么?

【问题讨论】:

  • 欢迎来到 SO。有趣的问题。请帮助我一点,这样我就可以帮助你。只需在 GitHub 上发布一点MCVE,这样我就可以重现该问题。我不是 Spring Boot 或 Gradle 用户,我是 Maven 人。但是,如果我有一个工作项目设置,我可以使用 AspectJ 查看您的类加载器问题。我需要查看 uber JAR 是如何打包的,在执行代理加载器类时使用了哪个类加载器,以及该类加载器是如何/何时启动的。

标签: java spring-boot gradle war aspectj


【解决方案1】:

尼基塔,今天是你的幸运日。我只是有一点时间,很好奇如何让我的代码从 https://www.eclipse.org/aspectj/doc/released/README-187.html 开始运行,显然你之前发现了,它在 Spring Boot 的上下文中工作。我刚刚使用了我的 Maven Spring Boot 游乐场项目。根据您使用的 Java 版本,您需要确保 JDK 8 中的 tools.jar 被定义为系统范围的依赖项并复制到可执行的 Spring uber JAR 中,或者您需要确保 Java 附加API 在 Java 9+ 中激活。这是我为 Java 8 所做的:

Maven:

<dependency>
  <groupId>com.sun</groupId>
  <artifactId>tools</artifactId>
  <version>1.8</version>
  <scope>system</scope>
  <systemPath>${java.home}/../lib/tools.jar</systemPath>
</dependency>
<!-- (...) -->
<plugin>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-maven-plugin</artifactId>
  <configuration>
    <mainClass>spring.aop.DemoApplication</mainClass>
    <!-- Important for tools.jar on Java 8 -->
    <includeSystemScope>true</includeSystemScope>
  </configuration>
</plugin>

&lt;includeSystemScope&gt; 选项是必需的,因为否则 Boot 不知道如何找到附加 API 类。只需在 Gradle 中做一些等效的事情就可以了。

Java:

您需要知道,为了附加代理,它必须是文件系统上的一个文件,而不仅仅是任何资源或输入流。这就是附加 API 的工作方式。所以不幸的是,您必须先将其从 uber JAR 复制到文件系统。以下是你的做法:

public static boolean dynamicallyLoadAspectJAgent() {
  String nameOfRunningVM = ManagementFactory.getRuntimeMXBean().getName();
  int p = nameOfRunningVM.indexOf('@');
  String pid = nameOfRunningVM.substring(0, p);
  try {
    VirtualMachine vm = VirtualMachine.attach(pid);
    ClassLoader classLoader = AgentLoader.class.getClassLoader();

    try (InputStream nestedJar = Objects.requireNonNull(classLoader.getResourceAsStream("BOOT-INF/lib/aspectjweaver-1.9.4.jar"))) {
      File targetFile = new File("aspectjweaver.jar");
      java.nio.file.Files.copy(nestedJar, targetFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
      vm.loadAgent(targetFile.getAbsolutePath());
    }
    try (InputStream nestedJar = Objects.requireNonNull(classLoader.getResourceAsStream("BOOT-INF/lib/spring-instrument-5.1.9.RELEASE.jar"))) {
      File targetFile = new File("spring-instrument.jar");
      java.nio.file.Files.copy(nestedJar, targetFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
      vm.loadAgent(targetFile.getAbsolutePath());
    }

    vm.detach();
  }
  catch (Exception e) {
    LOGGER.error("Exception while attaching agent", e);
    return false;
  }
  return true;
}

此外,在我的情况下,文件在BOOT-INF/lib 下,而不是WEB-INF/lib


更新:你说你在某个地方遇到了这个后续问题(重新格式化以提高可读性):

failed to access class
  org.aspectj.weaver.loadtime.Aj$WeaverContainer
from class
  org.aspectj.weaver.loadtime.Aj
(
  org.aspectj.weaver.loadtime.Aj$WeaverContainer is in
    unnamed module of
    loader 'app';
  org.aspectj.weaver.loadtime.Aj is in
    unnamed module of
    loader org.springframework.boot.loader.LaunchedURLClassLoader @3e9b1010
)
at org.aspectj.weaver.loadtime.Aj.preProcess(Aj.java:108)

这意味着Aj 无法找到自己的内部类Aj.WeaverContainer。这表明它们是在不同的时间点和不同的类加载器中加载的。当从可执行 JAR 开始远程调试到我的示例引导应用程序时,我看到应用程序类加载器实际上是 LaunchedURLClassLoader 的父类,即在父类中加载的类正试图访问另一个仅对其子类加载器可用的类,这在 Java 中是不可能的。它只能反过来工作。

也许不从代理加载器内部导入和引用 AspectJ 编织器类会有所帮助。尝试注释掉 loadJavaAgent()isAspectJAgentLoaded() 方法并删除 import org.aspectj.weaver.loadtime.Agent;。然后在您的应用程序中直接调用AgentLoader.dynamicallyLoadAspectJAgent() 看看这是否有帮助。关于代理加载,我还有一些优势,但让我们先让它尽可能简单。

【讨论】:

  • 非常感谢 kriegaex。很高兴我以与您提出的相同方式解决了我的问题。我们认为孤独:)
  • 它现在可以工作,但我开始收到奇怪的错误:failed to access class org.aspectj.weaver.loadtime.Aj$WeaverContainer from class org.aspectj.weaver.loadtime.Aj (org.aspectj.weaver.loadtime.Aj$WeaverContainer is in unnamed module of loader 'app'; org.aspectj.weaver.loadtime.Aj is in unnamed module of loader org.springframework.boot.loader.LaunchedURLClassLoader @3e9b1010) at org.aspectj.weaver.loadtime.Aj.preProcess(Aj.java:108)
  • 这是一个典型的类加载器问题。 AspectJ 编织器在类加载器 A 中加载,但由加载器 B 中的类引用。我也许可以帮助您,但不能没有 MCVE。所以现在轮到你了。提取一个最小的reprerer项目,发布到GitHub上,我可以看看。我不想推测或猜测。谢谢。
  • 碰巧又点了这个问题,才发现Aj找不到自己的内部类Aj.WeaverContainer。这表明它们是在不同的时间点和不同的类加载器中加载的。请参阅我将在几分钟后写的答案更新。
  • 删除 import org.aspectj.weaver.loadtime.Agent 确实有效,感谢您的帮助。但你指的是哪个 A? :)
猜你喜欢
  • 2012-01-24
  • 2018-02-24
  • 1970-01-01
  • 2018-01-26
  • 1970-01-01
  • 1970-01-01
  • 2011-06-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多