添加代码以打包私有库方法

Tho*_* S. 10 java aspectj

我有一个包含私有方法的库类.通过子类直接覆盖此方法是没有选择的.当从库内部调用这个包私有方法时,有没有办法,无论多么难看,执行自己的代码,例如使用AspectJ?

这是类的简化示例(packagePrivateMethod()实际上不是直接调用,而是从本机代码调用):

public LibClass {

  public LibClass() {
    ...
    packagePrivateMethod();
    ...
  }

  void packagePrivateMethod() {
    // <-- here I want to execute additional code
    ...
  }
}
Run Code Online (Sandbox Code Playgroud)

Mat*_*ias 6

你可以使用相当重量级的方法.

  1. 写一个关于该主题的小型Java代理SO帖子.
  2. 使用提供的Instrumentation接口拦截类加载
  3. 使用字节代码修改库(例如ASMJava Assist(仅Java 6!))来检测字节代码(例如,用你真正想做的任何东西替换方法调用.

这可以工作,因为您可以修改所有内容的字节代码,但它需要您在执行之前修改该字节代码.

当然,您也可以通过修改类文件静态地执行此操作,将现有字节代码替换为您在上面的步骤3中创建的字节代码.

如果您不希望/不能静态替换类的字节代码,则必须在运行时修改字节码.对于使用Java代理是一个好的和坚实的想法.

由于到目前为止这一切都相当抽象,我添加了一个示例,它将拦截您的库类的加载,在包私有方法中注入一个方法调用.当main方法执行时,您可以从输出中看到,在库类的代码之前直接调用注入的方法.如果添加return;为注入的代码,则还可以完全阻止该方法的执行.

所以这里是使用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!");
  }
}
Run Code Online (Sandbox Code Playgroud)

你的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");
  }
}
Run Code Online (Sandbox Code Playgroud)

中介:

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 Code Online (Sandbox Code Playgroud)

用于运行示例的命令(您必须将代理放在具有属性的清单的jar中Premain-Class: com.aop.agent.Agent:

%JAVA_HOME%\bin\java -cp .;..\javassist-3.12.1.GA.jar -javaagent:..\..\agent.jar com.aop.example.Main
Run Code Online (Sandbox Code Playgroud)

此示例的输出运行如下命令:

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
Run Code Online (Sandbox Code Playgroud)