对于一个监控项目,我尝试使用仪器 OutputStream
使用javassist初始化。我想做一个
OutputStream write()
方法打印什么 byte[]
是给它的(编码是一个不同的问题,在这里并不重要)。我的问题是 OutputStream
在运行时使用代理初始化会使jvm崩溃。使用javassit仅仅操纵字节码失败了 ClassPool.get("java.io.OutputStream")
还会使jvm崩溃。
那班人是不是很难碰?同样的方法也适用于 URI
班级。
有没有其他方法来做我想做的事情,或者是我唯一的选择来替换jre中的rt.jar?
@Override
public byte[] transform(ClassLoader loader, String className,
Class<?> classBeingRedefined, ProtectionDomain protectionDomain,
byte[] classfileBuffer) {
ClassPool pool = ClassPool.getDefault();
try {
CtClass cc = pool.get("java.io.OutputStream");
CtMethod method =
Arrays.stream(cc.getDeclaredMethods()).filter(ctMethod -> ctMethod.getLongName().equals("java.io.OutputStream.write(byte[],int,int)"))
.findFirst().get();
method.insertBefore("System.out.println(new String($1, java.nio.charset.StandardCharsets.UTF_8));");
return cc.toBytecode();
} catch (NotFoundException | CannotCompileException | IOException e) {
e.printStackTrace();
}
return null;
}
生成输出:
线程“main”java.lang.noclassdeffounderror中出现异常:org/springframework/boot/springapplication(错误名称:java/io/outputstream)
因为我用“new”覆盖了第一个加载的类 OutputStream
已经有了。这个 OutputStream
但是,类永远不会被调用到转换方法中。。。
1条答案
按热度按时间6yt4nkrj1#
这个
transform
方法是一个回调函数,可以为每个加载的类调用它。您将在className
参数和中的原始定义classfileBuffer
.因此,首先要做的是检查当前调用是否真的对应于要转换的类。如果没有,就回来
null
. 不执行此操作,但返回java.io.OutputStream
不管请求您转换哪个类。所以你才会出错。你被要求改变班级org/springframework/boot/SpringApplication
但是返回一个名为java/io/OutputStream
. 注意消息中的“错误名称”。当然,如果没有名称验证,这个不匹配的类将导致更多的问题。在检查当前调用是否负责
java.io.OutputStream
,您应该使用提供的classfileBuffer
用于阅读原始类版本,而不是使用ClassPool.getDefault().get(…)
,作为ClassPool
不知道您在参数中收到的类的版本,可能会找到错误的版本,或者无法访问类al all。但是请注意,检测方法
OutputStream.write(byte[],int,int)
光靠自己是不够的。正如其文件所说:鼓励子类重写此方法并提供更有效的实现。
在所有相关的实现类中都会发生这种情况。要记录重写方法的调用,还必须对它们进行检测。还要注意,子类可以重写
write(byte[])
不委托给write(byte[],int,int)
.