Я пытался изменить байтовый код нескольких классов, чьи файлы 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 который загружает некоторые файлы фляги в другом месте вы файловой системы во время выполнения.
- Внесите java-агент, преобразующий класс, загруженный вашим загрузчиком классов, заменив его тело или что-то в этом роде.
- В приложении назначьте экземпляр класса его интерфейсу и вызовите его инструментальные методы.
- Запустите приложение с -javaagent для проверки.
Вы пробовали с простым агентом без зависимости? например, попробуйте просто вызвать System.out.println (className); в вашем методе transform затем возвращайте classfileBuffer. Я попытался загрузить класс, используя свой CustomClassLoader, и я вижу имя класса на моей консоли. –
@NicolasFilotto Проблема решена и спасибо, вы напоминаете мне инструменты, которые я использовал с агентом. Агент сам получил уведомление о событии загрузки классов, но инструмент инструментария не может получить байт-код, если внешний URL не добавлен в его путь к классам. –