Ответ 1
Вы можете использовать довольно тяжелый подход.
- Напишите небольшой Java-агент SO сообщение об этой теме.
- Используйте предоставленный Инструментальный интерфейс, чтобы перехватить загрузку класса.
- Используйте библиотеку модификации кода байта (например, ASM или Java Assist (только Java 6!)), Чтобы задействовать байтовый код (например, чтобы заменить вызов метода тем, что вы действительно хотите сделать.
Это будет работать, поскольку вы можете изменить байтовый код всего, но он требует, чтобы вы изменили этот байт-код до его выполнения.
Конечно, вы можете сделать это также статически, просто изменив файл класса, заменив существующий код байта на код байта, который вы создаете на шаге 3 выше.
Если вы не хотите/не можете статически заменить байтовый код класса, вам придется выполнить модификацию байт-кода во время выполнения. Для использования Java-агента это хорошая и прочная идея.
Так как это довольно абстрактно до сих пор, я добавил пример, который перехватит загрузку вашего класса библиотеки, введет вызов метода в приватный метод пакета. Когда основной метод выполняется, вы можете видеть из вывода, что введенный метод вызывается непосредственно перед кодом классов библиотеки. Если вы добавите return;
в качестве введенного кода, вы также можете предотвратить выполнение этого метода alltogether.
Итак, вот код примера к вашей проблеме, разрешенный с помощью Java 6 и JavaAssist. Если вы хотите пойти по этому пути и использовать что-то более новое, как Java 7, вам просто нужно заменить манипуляцию с байтовым кодом на ASM. Это немного менее читаемо, но также не совсем научная ракета.
Основной класс:
package com.aop.example;
public class Main {
public static void main(String[] args) {
System.out.println("Main starts!");
LibClass libClass = new LibClass();
System.out.println("Main finished!");
}
}
Ваш LibClass:
package com.aop.example;
public class LibClass {
public LibClass() {
packagePrivateMethod();
}
void packagePrivateMethod() {
// <-- here I want to execute additional code
System.out.println("In packagePrivateMethod");
}
}
Агент:
package com.aop.agent;
import java.io.IOException;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;
import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.LoaderClassPath;
import javassist.NotFoundException;
public class Agent {
public static void premain(String agentArgs, Instrumentation instr) {
System.out.println("Agent starts!");
instr.addTransformer(new ClassFileTransformer() {
@Override
public byte[] transform(ClassLoader classLoader, String className, Class<?> arg2, ProtectionDomain arg3,
byte[] bytes)
throws IllegalClassFormatException {
System.out.println("Before loading class " + className);
final String TARGET_CLASS = "com/aop/example/LibClass";
if (!className.equals(TARGET_CLASS)) {
return null;
}
LoaderClassPath path = new LoaderClassPath(classLoader);
ClassPool pool = new ClassPool();
pool.appendSystemPath();
pool.appendClassPath(path);
try {
CtClass targetClass = pool.get(TARGET_CLASS.replace('/', '.'));
System.out.println("Enhancing class " + targetClass.getName());
CtMethod[] methods = targetClass.getDeclaredMethods();
for (CtMethod method : methods) {
if (!method.getName().contains("packagePrivateMethod")) {
continue;
}
System.out.println("Enhancing method " + method.getSignature());
String myMethodInvocation = "com.aop.agent.Agent.myMethodInvocation();";
method.insertBefore(myMethodInvocation);
}
System.out.println("Enhanced bytecode");
return targetClass.toBytecode();
}
catch (CannotCompileException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
catch (IOException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
catch (NotFoundException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
});
}
public static void myMethodInvocation() {
System.out.println("<<<My injected code>>>!");
}
}
Команда для запуска примера (вы должны поместить агента в банку с манифестом, имеющим атрибут Premain-Class: com.aop.agent.Agent
:
%JAVA_HOME%\bin\java -cp .;..\javassist-3.12.1.GA.jar -javaagent:..\..\agent.jar com.aop.example.Main
Результат этого примера запускает следующую команду:
Agent starts!
Before loading class com/aop/example/Main
Main starts!
Before loading class com/aop/example/LibClass
Enhancing class com.aop.example.LibClass
Enhancing method ()V
Enhanced bytecode
<<<My injected code>>>!
In packagePrivateMethod
Main finished!
Before loading class java/lang/Shutdown
Before loading class java/lang/Shutdown$Lock