使用 Javassist 添加语句

Adding a statement using Javassist

System.out.println("A read operation on a field is encountered "); 

我怎样才能添加一个声明,比方说,上面的声明, 每当对非本地字段执行读取操作时? 而且我还需要知道读取的字段的详细信息和 details应该对应字段的唯一性

示例(删除问题中的抽象):

public class Greet{
   int knowncount;
   public Greet()
   {
      System.out.println("Hello");
      knowncount++;
   }   
   public Greet(String language)
   {
     if(String.equals("ENGLISH"))  {System.out.println("Hello"); knowncount++; }
     else  if(String.equals("SPANISH")) {System.out.println("Hola"); knowncount++;}
     else  System.out.println("Language not recognized");
   }

   public void showCount() 
   {
     System.out.println("count : "+knowncount);
   }

}

并且用户 class 测试是:

class test{
  public static void main(String[] args){
    Greet g("SPANISH");
    g.showCount();

  }

}

在上面的示例中,在使用 javassist 之后我们的代码应该输出:

A read operation on a field is encountered
1

您可以使用 Javassist 的 ExprEditor. ExprEditor allows you to edit what is done in a FieldAccess 来完成您的要求,您可以创建一个执行以下操作的注入器来实现您的请求:

ClassPool classPool = ClassPool.getDefault();
CtClass greetCtClass = classPool.get(Greet.class.getName());

greetCtClass.instrument(new ExprEditor() {
        @Override
        public void edit(FieldAccess fieldAccess)
                throws CannotCompileException {
            if (fieldAccess.getFieldName().equals("knowncount")) {
                fieldAccess
                        .replace(" { System.out.println(\"A read operation on a field is encountered \"); $_ = $proceed($$); } ");
            }
        }
    });

    greetCtClass
            .writeFile("<ROOT DIRECTORY WHERE THE CLASSES ARE>");

解释参数的最佳方式是举个例子,假设 Greet class(恰好在 greatPackage 中)在以下路径 /home/user/dev/proj1/build/greetPackage/Greet.class 中。在这种情况下,您的根目录将为 /home/user/dev/proj1/build/.

以上所有样板的兴趣线如下:

{ System.out.println(\"A read operation on a field is encountered \"); $_ = $proceed($$); }

这里发生了什么?

  • 首先请注意 所有代码 都在大括号之间,如果您了解 javassist 的使用方法,这对您来说是微不足道的,否则可能会让您浪费几分钟时间尝试了解哪里出了问题。
  • 然后你就有了你要求的System.out
  • 你终于有了一条神奇的线$_ = $proceed($$);。您可以在 javassist tutorial 中找到有关此的更多信息(在该部分中搜索 FieldAccess,因为它们没有直接的锚点,对不起!)但基本上这一行的意思是结果值字段访问是为字段访问调用的虚拟方法的值,换句话说就是字段的实际值。

请记住,您必须在单独的 JVM 进程中使用注入器重写 Greet class,只有在之后您才能将 class 与注入行为一起使用,除非您对 classloading 做了一些技巧,以确保加载修改后的版本。我不会讨论这些主题,因为它超出了范围,但如果您也需要帮助,请说出来。我很乐意帮助你。