javassist使用与源码解析(一)
来源:互联网 发布:淘宝美工教程视频 编辑:程序博客网 时间:2024/06/08 07:32
Javassist是一个Java字节码操作类库,Java字节码被保存在一个被称为class文件的二进制文件中, 每个类文件都包含一个Java类或接口。
一、ClassPool
ClassPool对象是代表类文件的CtClass对象的容器。它读取类文件来构建CtClass对象,并且记录对象结构,以便于后面的访问。
程序中获取ClassPool默认如下方式:
ClassPool cp = ClassPool.getDefault();
查看ClassPool源码:
public static synchronized ClassPool getDefault() { if (defaultPool == null) { defaultPool = new ClassPool(null); defaultPool.appendSystemPath(); } return defaultPool; }
静态方法ClassPool.getDefault()返回的默认ClassPool会搜索和当前JVM相同的搜索的路径。如果程序是运行在譬如JBoss和Tomcat之类的web应用服务器上,ClassPool对象可能就找不到用户自己的类,这是由于web应用服务器除了使用系统类加载器之外,还有多个自定义类加载器。在这种情况下,其他的类路径就需要注册到ClassPool中。例如:
cp.insertClassPath(new ClassClassPath(this.getClass()));
上面的语句将this对象对应的类路径注册进来。除了使用this.getClass(),你还可以使用任何Class对象作为参数,用于类对象的类加载路径就这样被注册进来了。
添加类路径还有以下几种方法:
//用文件目录名称作为类搜索路径cp.appendClassPath("/com/xzq/test");//用URL作为类搜索路径ClassPath classPath = new URLClassPath("127.0.0.1", 8080, "/", "javassist");cp.appendClassPath(classPath);还有一种父子结构的ClassPool,类似classLoader
ClassPool child = new ClassPool(cp);
二、CtClass操作
2.1、获取类
CtClass是类文件的抽象代表,一个CtClass对象负责处理一个类文件,示例如下:
ClassPool cp = ClassPool.getDefault();CtClass ct = cp.get("com.xzq.javassist.Bus");查看ClassPool源码,看看get方法都做了哪些操作:
protected synchronized CtClass get0(String classname, boolean useCache) throws NotFoundException { CtClass clazz = null; if (useCache) { //通过缓存获取 clazz = getCached(classname); if (clazz != null) return clazz; } //如果有父ClassPool,则从父ClassPool中获取 if (!childFirstLookup && parent != null) { clazz = parent.get0(classname, useCache); if (clazz != null) return clazz; } clazz = createCtClass(classname, useCache); if (clazz != null) { // clazz.getName() != classname if classname is "[L<name>;". if (useCache) cacheCtClass(clazz.getName(), clazz, false); return clazz; } if (childFirstLookup && parent != null) clazz = parent.get0(classname, useCache); return clazz; }protected CtClass getCached(String classname) { //classes类型是Hashtable return (CtClass)classes.get(classname); }
从实现的角度看,ClassPool就是CtClass对象的哈希表,以类名称作为键值。ClassPool的get()方法通过指定的键值来搜寻CtClass对象。
如果当前ClassPool有父ClassPool,执行get()调用时,子ClassPool会首先委派给父ClassPool,当父ClassPool没找到这个类文件时,子ClassPool才会在它的类搜索路径下寻找这个类文件。类似ClassLoader的双亲委派原则。
当设置ClassPool.childFirstLookup=true时,子ClassPool就会先于父ClassPool来寻找此类文件。
2.2、创建类
ClassPool cp = ClassPool.getDefault();CtClass ctClass = cp.makeClass("com.xzq.javassist.Jeep");//设置新方法的返回类型为StringCtClass ctString = new CtClass("java.lang.String") {};ctClass.addMethod(CtNewMethod.make(ctString, "getName", null, null,"return \"123\";", ctClass));Class c1azz = ctClass.toClass();Method method = c1azz.getDeclaredMethod("getName", null);System.out.println(method.invoke(c1azz.newInstance(), null));
makeClass()方法创建一个新的类,通过addMethod()方法添加一个新的方法。
makeInterface()方法创建一个新的接口,通过CtNewMethod的abstractMethod()方法创建,请注意接口方法是抽象的。
CtClass定义了几种基本类型:
public static CtClass booleanType; public static CtClass charType; public static CtClass byteType; public static CtClass shortType; public static CtClass intType; public static CtClass longType; public static CtClass floatType; public static CtClass doubleType; public static CtClass voidType;可以通过CtClass(String name)构造方法来设置自定义类型。
查看ClassPool源码,看看makeClass做了哪些操作:
public synchronized CtClass makeClass(String classname, CtClass superclass) throws RuntimeException{ //1、检查父ClassPool中是否存在这个类,如果存在则异常; //2、检查类是否冻结 checkNotFrozen(classname); CtClass clazz = new CtNewClass(classname, this, false, superclass); //把类放入缓存 cacheCtClass(classname, clazz, true); return clazz; }还有其他方式能创建类,比如通过改变类名来新建类或者重命名冻结的类来新建类,示例如下:
ClassPool cp = ClassPool.getDefault();CtClass cc = cp.get("com.xzq.javassist.Car");//通过改变类名来新建类 cc.setName("Test");CtClass cc1 = cp.get("com.xzq.javassist.Bus");cc1.writeFile();//重命名冻结的类来新建类CtClass cc3 = cp.getAndRename("com.xzq.javassist.Bus", "NewTest");
2.3、冻结/解冻类
ClassPool cp = ClassPool.getDefault();CtClass ct = cp.get("com.xzq.javassist.Bus");ct.writeFile();//下面这句会报错:class is frozenct.setSuperclass(cp.get("com.xzq.javassist.Car"));
如果一个CtClass对象通过writeFile(),toClass(),toBytecode()方法被转换为类文件,Javassist就冻结了此对象。对此CtClass对象的后续修改都是不允许的,因为JVM不允许再次加载同一个类。
一个冻结的CtClass对象可以被解冻,这样类定义的修改就被允许。示例如下:
ClassPool cp = ClassPool.getDefault();CtClass ct = cp.get("com.xzq.javassist.Bus");ct.writeFile();//解冻ct.defrost();ct.setSuperclass(cp.get("com.xzq.javassist.Car"));执行 defrost()方法后,CtClass对象就可再次被修改。查看源码,defrost方法操作如下:
public void defrost() { checkPruned("defrost"); //设置冻结为false wasFrozen = false;}
如果 ClassPool.doPruning()方法设置为true,Javassist可以优化调整一个被冻结的CtClass对象的数据结构。优化调整指的是为了减少内存使用,去除对象内的一些不必要的属性(比如attribute_info,方法体中的Code_attribute)。因此,当一个CtClass对象被优化调整后,一个方法的字节码除了方法名,方法签名和注解外都是不可访问的。优化后的CtClass对象不能被再次解冻。ClassPool.doPruning()方法默认值为false。调用CtClass对象的stopPruning()方法,可防止其优化调整。
2.4、删除类
ClassPool cp = ClassPool.getDefault();CtClass cc = cp.get("com.xzq.javassist.Car");cc.writeFile();cc.detach();
当你调用CtClass对象的 detach()方法时,CtClass对象就会从ClassPool中删除掉,不能再调用 CtClass对象的任何方法。可以通过ClassPool的get()方法获取一个新的实例,当调用get()方法时,ClassPool会再次读取class文件并创建一个新的CtClass对象。
查看detach方法源码:
public void detach() { ClassPool cp = getClassPool(); //从缓存中删除 CtClass obj = cp.removeCached(getName()); if (obj != this) cp.cacheCtClass(getName(), obj, false); }
- javassist使用与源码解析(一)
- Volley框架使用与源码解析(一)
- Javassist 教程(一)
- Selenium2Library源码解析与扩展(一)
- Javassist学习笔记(一)
- Tomcat源码解析(一)下载源码与导入eclipse
- Tomcat源码解析(一)下载源码与导入eclipse
- Tomcat源码解析(一)下载源码与导入eclipse
- Hibernate源码解析 Hibernate中的动态代理Javassist
- Hibernate源码解析 Hibernate中的动态代理Javassist
- Hibernate源码解析 Hibernate中的动态代理Javassist
- EventBus使用与源码解析
- AsyncTask使用与源码解析
- Android 源码环境搭建配置与问题解析(一)
- (一)Tomcat源码解析 - Apache与Tomcat
- 【特征匹配】SURF原理与源码解析(一)
- 《Android源码设计模式解析与实战》读书笔记(一)
- 【读书笔记】Android源码设计模式解析与实践(一)
- C语言 函数
- Android初识-Intent的用法
- Asp.Net+ EntityFramework+MVC
- weblogic 集群
- 四个月薪资翻4倍
- javassist使用与源码解析(一)
- 有关eclipse项目搬迁到Android Studio后.so文件引用的问题
- 黑马程序员——第十三篇:字符缓冲流、IO流练习、其他流对象
- Kafka系统工具
- pojRed and Black(dfs)
- 深度学习框架 Digits 3.0 安装运行
- 安卓自带下拉刷新SwipeRefreshLayout
- ThinkPHP去重 distinct和group by
- 每日Android1_launchMode