Java – add code to package private library method
I have a library class that contains private methods Overriding this method directly through subclasses is not optional Is there any way, no matter how ugly, to execute your own code when this package private method is called from the library, such as using AspectJ?
This is a simplified example of a class (packageprivatemethod() is not actually called directly, but from native code):
public LibClass { public LibClass() { ... packagePrivateMethod(); ... } void packagePrivateMethod() { // <-- here I want to execute additional code ... } }
Solution
You can use a fairly heavy method
Write a small java proxy so post about that topic. > Use the provided instrumentation interface to intercept class loading > Modify libraries using bytecode (e.g. ASM or Java assist (Java 6 only!)) To adjust the bytecode (for example, with any replacement method call you want to make)
In this way, the bytecode of all contents can be modified, but the bytecode needs to be modified before execution
Of course, you can also do this statically by modifying the class file, replacing the existing bytecode with the bytecode you created in step 3 above
If you do not want / cannot statically replace the bytecode of the class, you must modify the bytecode at run time It's a good and solid idea to use Java proxies
Since this is all abstraction, until now, I have added an example, which will intercept the loading of your library class and inject a method call into a package private method When the main method executes, it can be seen from the output that the injected method is called directly before the code of the library class. If you add a return; As injected code, you can also completely block the execution of this method
So here is the code of an example of a problem solved using java 6 and javaassist If you want to follow this path and use something new like Java 7, you just need to replace bytecode operations with ASM It's a bit unreadable, but it's not entirely rocket science
Main categories:
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!"); } }
Your 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"); } }
Intermediary:
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>>>!"); } }
Run the command of the example (you must put the agent in a jar with the manifest of the attribute premain class: com.aop.agent.agent:
%JAVA_HOME%\bin\java -cp .;..\javassist-3.12.1.GA.jar -javaagent:..\..\agent.jar com.aop.example.Main
The output of this example runs the following command:
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