javassist的基本功能

javassist的基本功能
Javassist是一个动态类库,可以用来检查、”动态”修改以及创建 Java类。其功能与jdk自带的反射功能类似,但比反射功能更强大。

重要的类
ClassPool:javassist的类池,使用ClassPool 类可以跟踪和控制所操作的类,它的工作方式与 JVM 类装载器非常相似, 
CtClass: CtClass提供了检查类数据(如字段和方法)以及在类中添加新字段、方法和构造函数、以及改变类、父类和接口的方法。不过,Javassist 并未提供删除类中字段、方法或者构造函数的任何方法。 
CtField:用来访问域 
CtMethod :用来访问方法 
CtConstructor:用来访问构造器

语法
使用javassist来编写的代码与java代码不完全一致,主要的区别在于 javassist提供了一些特殊的标记符(以开头),用来表示方法,构造函数参数、方法返回值等内容。示例:System.out.println(“Argument1:”+开头),用来表示方法,构造函数参数、方法返回值等内容。示例:System.out.println(“Argument1:”+1); 其中的$1表示第1个参数.

示例
可以通过javassist来修改java类的方法,来修改其实现。如下所示:

      ClassPool classPool = ClassPool.getDefault();
      CtClass ctClass = classPool.get("org.light.lab.JavassistTest");
      CtMethod ctMethod = ctClass.getDeclaredMethod("test");
      ctMethod.setBody("System.out.println(\"this method is changed dynamically!\");");
      ctClass.toClass();

上面的方法即是修改一个方法的实现,当调用ctClass.toClass()时,修改后的类将被当前的ClassLoader加载并实例化。

完整的代码:

public class ChangeDemo {

  public static void main(String[] args) throws NotFoundException, CannotCompileException {
       replaceMethodBody("foo.Student", "execute", "System.out.println(\"this method is changed dynamically!\");");
       Student student = new Student();
       student.execute();
   }

public static void replaceMethodBody(String clazzName, String methodName, String newMethodBody) {
    try {
        CtClass clazz = ClassPool.getDefault().get(clazzName);
        CtMethod method = clazz.getDeclaredMethod(methodName);
        method.setBody(newMethodBody);
        clazz.toClass();
    } catch (NotFoundException | CannotCompileException e) {
       throw new RuntimeException(e);
    }
}

}

限制与局限性
需要注意的是,在调用ctClass.toClass()时,会加载此类,如果此类在之前已经被加载过,则会报一个duplicate load的错误,表示不能重复加载一个类。所以,修改方法的实现必须在修改的类加载之前进行。
不能访问块之外的局部变量。如果在一个方法的开始和结尾都增加了代码段,那么在方法的结尾块中无法访问方法开始中的代码段中的变量(不太完美的解决方法是将原方法改名,然后再增加与原方法同名的方法)。
与aspectj的区别
使用aspectj也可以同样达到修改的效果,不过修改指定的类,则需要为修改这个类添加一个aspect,然后将这个aspect加入配置文件中以使其生效,比起javassist来说,修改一个类还是使用javassist相对简单一点。

容器中的Classpath
在tomcat之类的容器中是无法通过ClassPool.getDefault()获取到用户定义的类的,可以通过以下方法获取: 
pool.insertClassPath(new ClassClassPath(this.getClass())); 
或者: 
ClassPool pool = ClassPool.getDefault(); 
pool.insertClassPath(“/usr/local/javalib”);

参考资料:
http://www.ibm.com/developerworks/cn/java/j-dyn0916/ 
http://jboss-javassist.github.io/javassist/tutorial/tutorial.html
 

 

转载至:https://blog.csdn.net/bjo2008cn/article/details/53543467

你可能感兴趣的:(杂笔)