我正在通过使用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)。