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
The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>