使用Javassist添加语句

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

每当在非本地字段上执行读取操作时,如何添加语句(例如,上述语句)? 而且我还需要知道所读取字段的细节,并且细节集应该与字段的唯一性相对应

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

 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 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允许您编辑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(""); 

解释参数的最佳方法是使用一个示例,假设Greet类(恰好位于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教程中找到更多相关信息(在该部分中搜索FieldAccess,因为它们没有直接锚点,对不起!)但基本上这行说的是字段访问的结果值是为字段访问调用的虚方法的值,换句话说就是字段的实际值。

请记住,您必须在分离的JVM进程中使用注入器重写Greet类,然后才能使用具有注入行为的类,除非您使用类加载做一些技巧以确保加载修改后的版。 我不会涉及这些主题,因为它超出了范围,但如果您需要帮助,请说出来。 我很乐意帮助你。