在java命令行中没有指定javaagent选项的情况下,有没有关于加载时间字节码转换的解决方案



作为一个开源生命周期框架API提供商,我想尽我所能以隐式的方式隐藏内部设计,以提供生命周期API,从而为API客户端带来更多便利。

预计可以避免对Core Java应用程序和Java EE应用程序进行配置,但实际情况是,我正在使用Java命令javaagent:${path}/Lifecycle.jar选项在类加载时启用我自己的ClassFileTransformer。

经过一番搜索,发现了一些不清楚的方向。我需要一些Java Guy来总结和指导我们。

  1. agentmain与premain
  2. 与指定的运行时环境集成,例如Glassfish的ByteCodePreprocessor,它具有以下方法来执行字节码转换:

    public byte[] preprocess(String classname, byte[] classBytes);

我对这些方向的困惑:

  1. 对于核心Java应用程序,我们似乎可以修改启动类的main方法来适应agentmain解决方案。还有其他选择吗
  2. 对于使用JavaEE容器,如Glassfish,我可以使用ByteCodePreprocessor来修改类字节码,但我需要创建一些新的类,但我不知道将这些新的类文件存储在哪里,也不知道如何在预处理类文件时设计或应用新的ClassLoader来加载新创建的类文件

(BTW生命周期API将遵循元驱动的风格,这与没有EntityManager接口的JPA非常接近,目前大多数只是Annotations和CallbackContext接口以及LifecycleEvent接口。)

好吧,我能想到的唯一其他方法是使用自定义类加载器,您可以在运行时注册它。这就是像Powermock这样的框架如何完成繁重的工作。然而,这也需要一些设置,但可以通过程序进行。

只要你的框架有定义良好的入口点,只要所有代码都在你的应用程序中运行,你就可以应用一个自定义的类加载器,它可以检测所有加载的类。

但是,这对已经加载的类不起作用。(您可以打破父级优先模式,但这可能会在框架外的实例上引发ClassCastException。)

要避免这种情况,需要重写同样冗长的系统类加载器。为了完整起见,这里摘录了ClassLoader.getSystemClassLoader:的javadoc

如果在以下情况下定义了系统属性"java.system.class.loader"方法,然后将该属性的值取为将作为系统类加载器返回的类的名称。该类使用默认的系统类加载器加载,并且必须定义一个接受类型为的单个参数的公共构造函数用作委托父级的ClassLoader。一个实例是然后使用此构造函数和默认系统类创建loader作为参数。生成的类加载器被定义为系统类加载器。

在这个自定义类加载器中,您可以始终返回插入指令的类。

agentmainpremain之间的区别在于,前者是在将代理附加到正在运行的JVM(通过附加API)时调用的,而后者是在JVM启动时在命令行上指定代理时调用的。在运行时注册代理实际上可能是一种解决方案。我链接的博客提供了一个很好的描述。

最新更新