【问题标题】:Check if aspectjweaver (or any javaagent) is loaded检查是否加载了 aspectjweaver(或任何 javaagent)
【发布时间】:2015-01-21 19:34:03
【问题描述】:

是否有(首选便携式)方法来检查是否 JVM 已用特定的-javaagent?

声明

我特别想知道 aspectj 加载时间编织器是否已加载。 (我试图在启动不正确的情况下提供有用的错误消息)。

【问题讨论】:

    标签: java aspectj javaagents


    【解决方案1】:

    以下代码显示

    • 一种确定任何-javaagent:... JVM 参数的方法,
    • 一种检查 AspectJ 编织代理入口点类(在 aspectjweaver.jar 的清单条目 Premain-Class: 中提到的类)是否已加载的方法。

    前者只是证明参数是在命令行中给出的,而不是真正找到并启动了代理。

    后者只是证明编织器在类路径上可用,而不是它实际上是作为代理启动的。两者的结合应该让您对代理实际上处于活动状态充满信心。

    package de.scrum_master.app;
    
    import java.lang.management.ManagementFactory;
    import java.lang.management.RuntimeMXBean;
    import java.util.List;
    
    public class Application {
        public static void main(String[] args) {
            RuntimeMXBean runtimeMxBean = ManagementFactory.getRuntimeMXBean();
            List<String> arguments = runtimeMxBean.getInputArguments();
            for (String argument : arguments) {
                if (argument.startsWith("-javaagent:"))
                    System.out.println(argument);
            }
            try {
                Class.forName("org.aspectj.weaver.loadtime.Agent");
            } catch (ClassNotFoundException e) {
                System.err.println("WARNING: AspectJ weaving agent not loaded");
            }
        }
    }
    

    您还可能会发现问题Starting a Java agent after program start 及其一些答案很有帮助。


    更新:

    好的,这是我自己的解决方案和您的解决方案的组合,但即使织布工不可用,它也确实有效,这很重要,因为这是您首先要检查的:

    public static boolean isAspectJAgentLoaded() {
        try {
            Class<?> agentClass = Class.forName("org.aspectj.weaver.loadtime.Agent");
            Method method = agentClass.getMethod("getInstrumentation");
            method.invoke(null);
        } catch (Exception e) {
            //System.out.println(e);
            return false;
        }
        return true;
    }
    

    更新 2:

    在与 OP bacar 讨论后,我决定提供一个不使用反射但捕获 NoClassDefError 的解决方案:

    public static boolean isAspectJAgentLoaded() {
        try {
            org.aspectj.weaver.loadtime.Agent.getInstrumentation();
        } catch (NoClassDefFoundError | UnsupportedOperationException e) {
            System.out.println(e);
            return false;
        }
        return true;
    }
    

    现在两种主要错误类型

    • 编织代理在类路径上可用,但检测尚未启动,因为 aspectjweaver.jar 未作为 Java 代理启动,
    • 代理 aspectjweaver.jar 根本不在类路径中,因此类 org.aspectj.weaver.loadtime.Agent 不可用

    通过返回false警告消息(在这个简单的示例中只是清楚说明问题所在的异常)已在控制台上打印出来来优雅地处理。

    这两种情况可能的控制台输出是:

    • java.lang.UnsupportedOperationException: Java 5 was not started with preMain -javaagent for AspectJ
    • java.lang.NoClassDefFoundError: org/aspectj/weaver/loadtime/Agent

    【讨论】:

    • 谢谢。您引用的问题是我最初的起点(我更喜欢自动加载而不是出错),但是动态加载需要 tools.jar,而且我还没有找到一种以足够便携的方式引用 tools.jar 的好方法,以便它在各种 IDE 和 maven cmd 行、开发工作站和我们的构建服务器上运行良好(即使所有服务器都是各种风格的 windows+oracle jdk)
    • 那么,如果我的回答看起来足够,您会接受并投票吗?或者您还有什么想知道的吗?
    • 顺便说一句,我对反射路线没有原则性的反对意见。我更喜欢我的静态方式,但仍然值得将另一种方式作为答案——不同的情况需要不同的解决方案。
    • 以防万一您有兴趣,我尝试了更多内容并修补了 AspectJ,如果-javaagent:... 部分在命令行中被遗忘/省略,则可以帮助我动态附加编织代理。我创建了一个AspectJ bug ticket 以便在上游获得该功能。
    • 最初我还考虑在 aspectjweaver.jar 周围创建一个薄的附加层,就像你指给我的那个。但后来我认为最终将该功能引入上游会更有意义。
    【解决方案2】:

    我发现了以下作品(针对 1.8.4 进行了测试),尽管它依赖于未记录的 aspectjweaver 功能,因此可能无法跨版本运行。

    public static boolean isAspectJAgentLoaded() {
        try {
            org.aspectj.weaver.loadtime.Agent.getInstrumentation();
            return true;
        } catch (UnsupportedOperationException e) { 
            return false;
        }
    }
    

    解释:当aspectj作为代理加载时,JVM会调用org.aspectj.weaver.loadtime.Agent.premain(...)静态方法。这有一个我们可以测试的副作用。调用 getInstrumentation 要么抛出 UnsupportedOperationException(如果它没有被初始化为代理),或者如果它是成功返回。

    【讨论】:

    • 我不认为这是一种特别实用的方法,因为如果未加载编织代理,可能 aspectjweaver.jar 也不会在类路径中,并且您的代码也不会运行,但发出异常:Exception in thread "main" java.lang.NoClassDefFoundError: org/aspectj/weaver/loadtime/Agent。我宁愿推荐使用Class.forName(..),就像我在回答中所做的那样,然后(仅在成功的情况下)通过反射调用getInstrumentation()。查看我的答案的更新。
    • 简单解决方案:将其放在类路径中。这是进行此检查的(通常是无关紧要的)成本。不在CP上的情况下的错误是清晰透明的,很容易修复。您的建议会将限定类或方法名称中的拼写错误(或跨版本更改)视为“未加载代理”。我的会很快失败,我更喜欢。其次,我不清楚-javaagent 加载的 jar 是否可用于默认类加载器——你测试过你的提议吗? (如果您有自定义 ClassLoaders,我认为这两种解决方案的所有赌注都没有)
    • 查看我的 updated 解决方案,如果您愿意,可以为多种错误类型设置多个 catch 子句。我认为让整个应用程序退出并在实用程序方法中出现未处理的错误是不好的,即使这个错误发生在您想要优雅处理的情况下。如果你把编织器放在类路径上,这并不意味着 LTW 处于活动状态,因为它仍然没有作为代理加载。在命令行上指定两次是违反直觉的。恕我直言,我的解决方案非常出色。
    • 好的,我的第二次更新没有反射,但处理NoClassDefFoundError
    • 这是我最初的想法,虽然我觉得捕捉错误是邪恶的?
    猜你喜欢
    • 2016-06-07
    • 1970-01-01
    • 1970-01-01
    • 2014-09-26
    • 1970-01-01
    • 2011-05-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多