我正在通过使用Javaagent(与Javaassit)


premain(String agentArgs,Instrumentation inst)


方法,我想知道为什么ClassFileTransformer不考虑类

简短的介绍:


我的项目中有两个类,其中将javaagent(premain)应用于
MyMainClass是具有main方法的类
在MyMainClass中,MyLogicReference类不是通过导入引用的,...
仅对MyMainClass调用ClassFileTransformer变换方法,而对MyLogicReference不调用
如果我调用java.lang.instrument.Instrumentation getAllLoadedClasses,则可以看到MyLogicReference类已加载
?这是代理程序的工作方式吗?如果是,我如何让代理程序也转换第二个(MyLogicReference)类?


更新

我想我已经在Javadocs https://docs.oracle.com/javase/7/docs/api/java/lang/instrument/ClassFileTransformer.html中找到了一些有用的信息。

在java.lang.instrument.Instrumentation.retransformClasses中,我应该能够从示例中注册MyLogicReference类。但是我仍然对这种行为感到好奇...让我们尝试一下...


对于每个添加了canRetransform true的转换器,
这些变压器中称为变换方法


详细:

我正在使用代理来更改方法,...通过注释(某种注入)。

我将类简化为下面的示例,我想知道为什么只有MyMainClass放到javaagent转换器(类文件缓冲区)而不是MyLogicReference类。

public class MyMainClass {

  ... //Main method and call of myMethod();

  @MyAnnotationToApplyLogic
  public void myMethod(){
    //Some code here
  }


我更改代码的过程的切入点是注释,在该注释中我引用了另一个类(MyLogicReference)。

@Documented
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.CLASS)
@Functional(value = MyLogicReference.class, type = ElementType.PARAMETER)
public @interface MyAnnotationToApplyLogic {

}

public class MyLogicReference {
  // @MyAnnotationToApplyLogic  in the MyMainClass method references to this class
  // The Javaagent Class file transformer adjust the MyMainClass.myMethod code based on the annotation
  public void mySecondMethod(){
  }
}


如果我使用


java.lang.instrument.Instrumentation getAllLoadedClasses方法


我可以看到MyLogicReference类。但是,此类永远不会调用ClassFileTransformer。这对javaagents正确吗?

例如,如果我将MyLogiReference.class导入MyMainClass中,我就知道已调用了转换器。


所以我目前的假设是,只有
直接在主类上引用的被发送到
ClassFileTransformer。如果正确,那么我该如何强制
javaagent转换以前未转​​换过的类?


我的javaaagent清单条目(MVN):

<Premain-Class>com.MyTestAgent</Premain-Class>
<Agent-Class>com.MyTestAgent</Agent-Class>
<Can-Redefine-Classes>true</Can-Redefine-Classes>
<Can-Retransform-Classes>true</Can-Retransform-Classes>


我将ClassFileTransformer剥离为此,第二个类仍未加载:

@Override
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
  if (!className.startsWith("java/") && !className.startsWith("javax/") && !className.startsWith("sun/")) {
    log("NOW PROCESSING: " + className);
    return classfileBuffer;
  }
  return null;
}

//Output:
//NOW PROCESSING: MyMainClass

最佳答案

根据您所说的,我猜您是在javaagent代码中某处引用MyLogicReference。结果,JVM在检测开始之前就加载了该类。要记住的两件事:


Java代理只是一个Java程序,它需要加载类。
Java代理在加载类时对其进行转换。对于在Java代理启动之前加载的任何类,您将需要使用java.lang.instrument.Instrumentation#retransformClasses(您会注意到自己)。


在这里似乎也不需要使用java.lang.instrument.Instrumentation#retransformClasses。我认为最好还是避免这种情况(为简单起见)。您可以:


分隔javaagent使用的代码和正在检测的代码。
仅将MyLogicReference称为字符串,例如。使用foo.getClass().getName().equals("MyLogicReference")代替foo instanceof MyLogicReference


根据提供的信息,我也想知道您是否考虑使用注释处理器(https://docs.oracle.com/javase/7/docs/api/javax/annotation/processing/Processor.html)。

09-28 02:18