Ich habe versucht, den Byte-Code mehrerer Klassen zu ändern, deren Verpackungs-JAR-Dateien nicht im Klassenpfad sind - sie werden während einer Laufzeit mit einer URL ClassLoader
geladen. Ich habe versucht, eine java agent
mit ClassFileTransformer
in der Hoffnung zu verwenden, diese Klassen abzufangen, aber gescheitert. Der Classloader ist Teil eines Legacy-Projekts und kann daher nicht direkt geändert werden.Wie instrumentieren Sie Klassen, die von einem benutzerdefinierten Klassenlader geladen wurden?
Der Agent eignet sich gut für Klassen, die von 'Local' von AppClassLoader geladen werden, ignoriert jedoch nur die vom benutzerdefinierten Classloader geladenen Klassen.
die 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;
}
}
}
die ClassFileTransformer in meinem Agenten verwendet (mit 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;
}
}
der -Agent:
public class Agent
{
public static void premain(String agentArgs, Instrumentation inst)
{
inst.addTransformer(new MyTransformer());
}
}
Ich habe einen Workaround gefunden, indem ich den CustomClassLoader selbst instrumentiert habe, indem ich instrumentation.redifineClasses() aufgerufen habe, aber nicht weiß, wie ich die Instrumentierungsinstanz an die CustomClassLoader-Instanz übergeben soll. Ich bin neu in der Instrumentierung/Klassenbelastung und immer noch nicht ganz klar mit ihrem Mechanismus. Irgendwelche Hilfe? Vielen Dank.
Um es einfach zu machen:
- Innerhalb einer App einen benutzerdefinierten URLClassLoader erstellen, das einige JAR-Dateien an anderer Stelle lädt in ihrem System während der Laufzeit-Datei.
- Implementieren Sie einen Java-Agenten, der eine Klasse transformiert, die von Ihrem Klassenlader geladen wurde, und dabei einen der Körper der Methode ersetzt.
- Innerhalb der App zuweisen Sie eine Klasse Instanz seiner Schnittstelle und rufen Sie seine instrumentierten Methoden.
- Führen Sie die App mit -Javaagent zu überprüfen.
haben Sie versucht mit einem einfachen Agenten ohne Abhängigkeit? versuchen Sie zum Beispiel, einfach System.out.println (Klassenname) aufzurufen; in Ihrer Methode transform dann classfileBuffer zurückgeben. Ich habe versucht, eine Klasse mit Ihrem CustomClassLoader zu laden und ich sehe den Namen der Klasse in meiner Konsole –
@NicolasFilotto Problem gelöst und danke, erinnern Sie mich an die Tools, die ich mit Agent verwendet. Der Agent selbst wurde über das Classloading-Ereignis benachrichtigt, aber das Instrumentierungswerkzeug kann den Bytecode nicht abrufen, wenn die externe URL nicht zum Klassenpfad hinzugefügt wurde. –