用 javassist 来修改 class 文件

  在一个老项目中,不知是哪位牛人写的程序,调用了FileInputStream,但是没有关闭文件流。而这个地方是最常用的一个地方,导致系统运行一段时间之后内存耗尽,报文件句柄数过多的错误。
  处理这种问题,如果有源码,加上关闭文件流的操作即可。但是,由于公司的源码管理得不好,这个项目的源码已经丢失了,没有源码,只能反编译得到源码加上关闭文件流的操作再编译一次,这样编译出来的class文件跟原来的class文件里面的一些变量有差异,替换之后会引起另外一个更严重的问题:链接走正常路线的时候可以访问,但是走通过F5负载均衡的时候就不能访问了。
  这个问题相当的棘手,因为以替换class文件,就会引起更严重的问题,但是文件句柄不释放的问题也很重要。至于"通过F5负载均衡的时候为什么不能访问"这个问题,由于对F5负载均衡不了解,通过解决这个问题来解决句柄问题看起来很不可能。于是想到了用开源的jar包来直接修改class文件,直接在class文件中直接添加将文件流关闭的操作。
  在网上查资料之后有两种方式来编辑class文件:classEdit和javassist。我用了javassist来修改class文件,在class文件中添加关闭文件流的操作之后,重新生成了一个class文件,用这个class文件和修改前的class文件反编译对比,非常的完美,就只比修改前的文件多了关闭文件流的操作,其他的地方一模一样。
 
    修改calss文件的示例代码如下:
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
 
public class Test {
 
    public static void main(String[] args) throws Exception {
        ClassPool classPool = ClassPool.getDefault();
        // 必须将class文件放在这个工程编译后的class文件中,路径也对应起来
        CtClass ctClass = classPool.get("com.ambitionstone.esa2000.pki.AZTPkiServlet");
        
        //设置方法需要的参数,一定要能匹配起来,而且必须引入这些参数类的包
        CtClass[] param = new CtClass[4] ;                
        param[0] = classPool.get("javax.servlet.http.HttpServletRequest") ;
        param[1] = classPool.get("javax.servlet.http.HttpServletResponse") ;
        param[2] = classPool.get("int") ;
        param[3] = classPool.get("java.lang.String") ;
        
        // 找到需要修改的行所在的方法
        CtMethod  method = ctClass.getDeclaredMethod("doOnlineValidate", param);
        
        // 在这个方法的182行添加关闭文件流的方法
        method.insertAt(182, "fin.close();");
        
        // 将文件写到指定的目录,生成之后在test\com\ambitionstone\esa2000\pki\AZTPkiServlet\这个文件夹下面可以找到编译后的类
        ctClass.writeFile("D:\\test") ;
    }
}

javassist.jar 是一个比较常见的包,网上一搜就能搜到,这里就不在添加了。

posted @ 2013-03-07 00:28  白色黄昏  阅读(3891)  评论(0编辑  收藏  举报