如何访问 WEB-INF/lib/ 罐子?
How to get access to WEB-INF/lib/ jars?
我有 Gradle 项目 Spring Boot 和 AspectJ.
想要直接从 WEB-INF/libs 动态加载 aspectjweaver
和 spring-instrument
javaagents(其中 Spring 启动定位所有依赖项)
Gradle 依赖关系:
AgentLoader:
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()
的 return 值
处理此问题的最佳解决方案是什么?
尼基塔,今天是你的幸运日。我只是有片刻时间,很好奇如何从 https://www.eclipse.org/aspectj/doc/released/README-187.html 制作我的代码片段,这显然是您之前发现的,可以在 Spring Boot 的上下文中工作。我刚刚使用了我的 Maven Spring Boot playground 项目。根据您使用的 Java 版本,您需要确保 JDK 8 中的 tools.jar 被定义为系统范围的依赖项并且也被复制到可执行文件 Spring uber JAR,或者你需要确保 Java attach 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>
<includeSystemScope>
选项是必需的,否则 Boot 不知道如何找到附加 API classes。只需在 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
无法找到自己的内部classAj.WeaverContainer
。这表明它们在不同的时间点和不同的 classloader 中加载。当从可执行 JAR 开始远程调试我的示例 Boot 应用程序时,我看到应用程序 classloader 实际上是 LaunchedURLClassLoader
的父级,即在父级中加载的 class 是试图访问另一个 class 仅可用于其子 classloader,这在 Java 中是不可能的。反之亦然。
也许不从代理加载程序内部导入和引用 AspectJ 编织器 classes 会有所帮助。尝试注释掉 loadJavaAgent()
和 isAspectJAgentLoaded()
方法并删除 import org.aspectj.weaver.loadtime.Agent;
。然后在您的应用程序中直接调用 AgentLoader.dynamicallyLoadAspectJAgent()
并查看是否有帮助。关于代理加载,我有更多的技巧,但我们先让它尽可能简单。
我有 Gradle 项目 Spring Boot 和 AspectJ.
想要直接从 WEB-INF/libs 动态加载 aspectjweaver
和 spring-instrument
javaagents(其中 Spring 启动定位所有依赖项)
Gradle 依赖关系:
AgentLoader:
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()
的 return 值
处理此问题的最佳解决方案是什么?
尼基塔,今天是你的幸运日。我只是有片刻时间,很好奇如何从 https://www.eclipse.org/aspectj/doc/released/README-187.html 制作我的代码片段,这显然是您之前发现的,可以在 Spring Boot 的上下文中工作。我刚刚使用了我的 Maven Spring Boot playground 项目。根据您使用的 Java 版本,您需要确保 JDK 8 中的 tools.jar 被定义为系统范围的依赖项并且也被复制到可执行文件 Spring uber JAR,或者你需要确保 Java attach 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>
<includeSystemScope>
选项是必需的,否则 Boot 不知道如何找到附加 API classes。只需在 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
无法找到自己的内部classAj.WeaverContainer
。这表明它们在不同的时间点和不同的 classloader 中加载。当从可执行 JAR 开始远程调试我的示例 Boot 应用程序时,我看到应用程序 classloader 实际上是 LaunchedURLClassLoader
的父级,即在父级中加载的 class 是试图访问另一个 class 仅可用于其子 classloader,这在 Java 中是不可能的。反之亦然。
也许不从代理加载程序内部导入和引用 AspectJ 编织器 classes 会有所帮助。尝试注释掉 loadJavaAgent()
和 isAspectJAgentLoaded()
方法并删除 import org.aspectj.weaver.loadtime.Agent;
。然后在您的应用程序中直接调用 AgentLoader.dynamicallyLoadAspectJAgent()
并查看是否有帮助。关于代理加载,我有更多的技巧,但我们先让它尽可能简单。