【发布时间】:2016-05-31 10:01:07
【问题描述】:
我试图修改几个类的字节码,这些类的打包 jar 文件不在类路径中 - 它们在运行时由自定义 ClassLoader 加载,给定一个 URL。我尝试使用java agent 和ClassFileTransformer 希望拦截这些类但失败了。类加载器是遗留项目的一部分,因此我无法直接对其进行更改。
代理在 AppClassLoader“本地”加载的类上工作正常,但只是忽略自定义类加载器加载的类。
CustomClassLoader:
public class CustomClassLoader extends URLClassLoader {
public CustomClassLoader(URL[] urls) {
super(urls, CustomClassLoader.class.getClassLoader());
}
// violates parent-delegation pattern
@Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
synchronized (getClassLoadingLock(name)) {
Class<?> clazz = findLoadedClass(name);
if (clazz == null) {
try {
clazz = findClass(name);
} catch (ClassNotFoundException e) {
}
if (clazz == null) {
clazz = getParent().loadClass(name);
}
}
if (resolve) {
resolveClass(clazz);
}
return clazz;
}
}
}
在我的代理中使用的 ClassFileTransformer(使用 javassist):
public class MyTransformer implements ClassFileTransformer
{
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException
{
byte[] byteCode = null;
if (className.replace("/", ".").equals("com.example.services.TargetService"))
{
ClassPool cp = ClassPool.getDefault();
CtClass cc;
try
{
cc = cp.get("com.example.services.TargetService");
CtMethod verifyMethod = cc.getDeclaredMethod("verify");
//invalidate the verification process of method : verify
verifyMethod.insertBefore("{return true;}");
byteCode = cc.toBytecode();
cc.detach();
return byteCode;
}
catch (Exception e)
{
e.printStackTrace();
}
}
return byteCode;
}
}
代理:
public class Agent
{
public static void premain(String agentArgs, Instrumentation inst)
{
inst.addTransformer(new MyTransformer());
}
}
我想出了一个解决方法,方法是检测 CustomClassLoader 本身,调用 instrumentation.redifineClasses() 但不知道如何将检测实例传递给 CustomClassLoader 实例;我是仪器/类加载的新手,但对它们的机制仍然不太清楚。 有什么帮助吗?谢谢。
为了简单起见:
- 在应用程序内部创建一个自定义 URLClassLoader,它在运行时将一些 jar 文件加载到文件系统的其他位置。
- 实现一个 java 代理来转换类加载器加载的类,替换其中一个方法的主体或其他东西。
- 在应用程序内部将类的实例分配给其接口并调用其检测方法。
- 使用 -javaagent 运行应用程序进行检查。
【问题讨论】:
-
你试过没有依赖的简单代理吗?例如尝试简单地调用 System.out.println(className);在您的方法转换中,然后返回 classfileBuffer。我尝试使用您的 CustomClassLoader 加载一个类,我在控制台中看到了该类的名称
-
@NicolasFilotto 问题解决了,谢谢,你让我想起了我与代理一起使用的工具。代理本身收到了类加载事件的通知,但如果未将外部 url 添加到其类路径中,则检测工具无法获取字节码。
标签: java classloader instrumentation javassist javaagents