L'interception de méthodes dans des JARs tiers en Java requiert des approches adaptées, car les méthodes classiques comme la réflexion ou les proxies présentent des contraintes pour un hooking passif.
La réflexion permet une invocation active des méthodes, mais ne facilite pas l'interposition de code sans modifictaion de l'appelant.
Les proxies dynamiques offrent une solution partielle :
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ExempleProxyIntercepteur {
public interface InterfaceService {
void effectuerAction(String entree);
}
public static class ImplementationService implements InterfaceService {
public void effectuerAction(String entree) {
System.out.println("Exécution originale, paramètre: " + entree);
}
}
public static void main(String[] arguments) {
InterfaceService serviceOriginal = new ImplementationService();
InterfaceService proxyService = (InterfaceService) Proxy.newProxyInstance(
InterfaceService.class.getClassLoader(),
new Class[]{InterfaceService.class},
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method methode, Object[] params) throws Throwable {
System.out.println("Paramètres capturés: " + Arrays.toString(params));
Object resultat = methode.invoke(serviceOriginal, params);
System.out.println("Terminé");
return resultat;
}
}
);
proxyService.effectuerAction("Test");
}
}
Toutefois, cette approche requiert l'utilisation du proxy dans votre code, ce qui est inopérant si la bibliothèque tierce instancie directement les classes.
La programmation orientée aspect via AspectJ constitue une alternative :
@Aspect
public class AspectInterception {
@Around("execution(* com.exemple.ImplementationService.effectuerAction(..))")
public Object autourDeAction(ProceedingJoinPoint pointJoint) throws Throwable {
System.out.println("Avant l'exécution");
Object resultat = pointJoint.proceed();
System.out.println("Après l'exécution");
return resultat;
}
}
Cependant, AspectJ nécessite l'accès au code source pour le tissage à la compilation. Pour des bibliothèques tierces sans source, il faut recourir au tissage après compilation ou au tissage au chargement (LTW).
Le tissage au chargement permet de modifier le bytecode dynamiquement lors du chargement des classes, sans code source. Il s'implémente via un agent Java avec -javaagent et un fichier de configuration aop.xml.
Des solutions comme jnihook proposent également des méthodes spécifiques pour le hooking au niveau natif.