`
youaremoon
  • 浏览: 32874 次
  • 性别: Icon_minigender_1
  • 来自: 重庆
社区版块
存档分类
最新评论

javassist使用中遇到的问题记录

阅读更多

Javassit提供了运行时操作Java字节码的方法,其效率低于asm。javassist主要是提供了代码级别的修改(也有bytecode级别),相比与asm的字节码级别的修改,学习成本低,开发效率高。因此,在实际应用中javassist是一个非常不错的选择。以下是在使用javassist的过程中碰到的问题及处理方法:

1、ClassLoader问题

我们知道java中有ExtClassLoader、AppClassLoader等来加载运行时需要的字节码,同时系统也允许我们自定义ClassLoader来实现不同的加载方式(如tomcat实现的加载机制)。在实际应用中会有这样的问题,如AClassLoader加载/home/admin/a/目录下的类A,BClassLoader加载/home/admin/b目录下的类B,类A想要引用B是无法引用成功的,因为类A的ClassLoader无法找到类B的定义。解决的方法就是加载B时指定BClassLoader去加载。对于Javassit来说,要想修改某个类,必须要先加载类信息,因此也存在类加载问题。知道了问题,处理起来就比较简单了,javassist中有一个ClassPath接口,该接口提供了查找类、加载类的字节码的方法。在遇到ClassLoader问题时,我们可以使用LoaderClassPath来处理,代码如下:

 

ClassPool pool = new ClassPool(true);
pool.appendClassPath(new LoaderClassPath(classLoader));

ClassPath还有其他的实现来应对不同的情况:ByteArrayClassPath、ClassClassPath、DirClassPath、JarClassPath、JarDirClassPath、UrlClassPath。

 

如果一个应用中有存在多个不同的ClassLoader,建议对不同的ClassLoader创建不同的ClassPool,示例代码:

 

	private static ConcurrentHashMap<ClassLoader, ClassPool> CLASS_POOL_MAP = new ConcurrentHashMap<ClassLoader, ClassPool>();
	
	/**
	 * 不同的ClassLoader返回不同的ClassPool
	 * @param loader
	 * @return
	 */
	public static ClassPool getClassPool(ClassLoader loader) {
		if (null == loader) {
			return ClassPool.getDefault();
		}
		
		ClassPool pool = CLASS_POOL_MAP.get(loader);
		if (null == pool) {
			pool = new ClassPool(true);
			pool.appendClassPath(new LoaderClassPath(loader));
			CLASS_POOL_MAP.put(loader, pool);
		}
		return pool;
	}

2、内存占用问题

 

javassist在加载类时会用Hashtable将类信息缓存到内存中,这样随着类的加载,内存会越来越大,甚至导致内存溢出。如果你的应用中要加载的类比较多,建议在使用完CtClass之后删除缓存:CtClass.detach()。

3、class的NotFoundException问题

NotFoundException包括找不到类定义、找不到方法定义等等,我们这里主要讨论找不到类定义的情况。你可能会觉得奇怪,前面不是有这么多ClassPath实现,难道还有这些ClassPath没有覆盖的情况? 是的,确实存在这种状态。比如我们使用javassist生成了一个自定义的类C, 由于该类完全是在内存中生成的,你无法通过一个具体的路径找到它,因此如果你后续希望再引用C,你可能会找不到它。为什么是可能? javassist在加载类时会将其信息缓存起来,然而有的应用因为内存方面的考虑,会通过detach移除缓存信息。对于普通的类来说,缓存移除后通过添加LoaderClassPath或者其他ClassPath的方式可以重新加载,但是对于javassist动态生成的类来说,由于其只在内存中存在,因此无法再次找到其信息。 知道了问题以后,我们可以怎么处理呢?

a) 在CtClass.detach()之前,将生成的字节码保存到指定目录下:CtClass.writeFile(dir), 然后通过指定DirClassPath来重新加载信息。

b) 如果CtClass操作已经被封装,无法加入writeFile方法的话,可以在系统启动时指定静态变量CtClass.debugDump="/home/admin/code_cache/dump"; 然后在需要对动态类进行二次代理时调用

pool.appendClassPath(new DirClassPath("/home/admin/code_cache/dump"));

4、特殊变量

 

javassist提供了一些特殊的变量来方便你操作(http://jboss-javassist.github.io/javassist/tutorial/tutorial2.html#before):

$0,$1,$2, ... $0表示this,其他的表示实际的参数
$args 参数数组. 相当于newObject[]{$1,$2,....},其中的基本类型会被转为包装类型
$$ 所有的参数,如m($$)相当于m($1,$2...),如果m无参数则m($$)相当于m()
$cflow(...) 表示一个指定的递归调用的深度
$r 用于类型装换,表示返回值的类型.
$w 将基础类型转换为一个包装类型.如Integer a=($w)5;表示将5转换为Integer。如果不是基本类型则什么都不做。
$_

返回值,如果方法为void,则返回值为null; 值在方法返回前获得,

如果希望发生异常是有返回值(默认值,如nul),需要将insertAfter方法的第二个参数asFinally设置为true

$sig 方法参数的类型数组,数组的顺序为参数的顺序
$type 返回类型的class, 如返回Integer则$type相当于java.lang.Integer.class, 注意其与$r的区别
$class 方法所在的类的class

其中cflow的用法如下:

 

// 被修改的方法
int fact(int n) {
    if (n <= 1)
        return n;
    else
        return n * fact(n - 1);
}

// 修改前的调用
CtMethod cm = fact方法;
cm.useCflow("fact");

//此时$cflow(fact)表示fact方法的递归深度,第一次调用是为0
cm.insertBefore("if ($cflow(fact) == 0) {System.out.println(\"fact \" + $1);}");

cflow使用场景举例:

 

应用需要监控方法的执行时间,并找出执行时间长的方法,如果遇到递归调用期望忽略内部递归的记录,只记录最外层的时间,此时可以使用cflow。

最后,顺便提醒javassist也提供了动态代理的接口(javassist.util.proxy.ProxyFactory),但效率非常低,可测试时使用,不建议在生产环境下使用。

 

 

 

分享到:
评论

相关推荐

    java 反编译工具 jboss-javassist

    4. **面向切面编程(AOP)**:在AOP中,Javassist可以用来动态插入切面代码,实现如日志记录、事务管理等横切关注点。 5. **教学与研究**:由于它可以轻松地反编译和修改字节码,Javassist在教学和研究Java字节码和...

    jboss-javassist和JByteMode整合包

    2. **修改main方法**:在得到源代码后,可以使用Javassist定位并修改main方法,例如增加日志记录、改变控制流等。 3. **字节码重新生成**:使用Javassist将修改后的源代码重新编译为字节码,并替换原有的.class文件...

    javassist-3.15.0-GA

    - **社区支持**:Javaassist有一个活跃的社区,遇到问题时可以寻求帮助。 总结来说,"javassist-3.15.0-GA"是Java开发中的一个重要工具,它使得在运行时动态修改和增强Java代码成为可能,广泛应用于AOP、动态代理...

    Java动态代理机制详解(JDK 和CGLIB,Javassist,ASM)

    在Java中,我们可以使用JDK自带的动态代理或者第三方库如CGLIB、Javassist、ASM来实现。 **JDK动态代理**: JDK的动态代理主要依赖于`java.lang.reflect.Proxy`和`java.lang.reflect.InvocationHandler`两个类。...

    javassist-3.7.ga.jar下载

    在实际应用中,`javassist-3.7.ga.jar`的使用可能包括以下步骤: 1. 引入依赖:将jar包添加到项目的类路径中,如果是Maven项目,则在pom.xml中添加对应的依赖配置。 2. 创建`ClassPool`:这是使用Javaassist的第一步...

    javassist-3.9.0.GA.jar.zip

    Javaassist是一个开源库,主要用在Java编程中,它提供了对字节码操作的强大功能,使得程序员可以在运行时动态修改类或创建新的类。这个"javassist-3.9.0.GA.jar.zip"文件包含了Javaassist库的版本3.9.0 GA,这是一个...

    运用javassist和annotation修改class的特定method的class byte code

    在实际应用中,这样的技术可以用于实现如AOP拦截器、性能监控、日志记录等功能,而无需改变原有代码。通过这种方式,我们可以在不侵入原始代码的情况下,灵活地扩展或修改类的行为。同时,由于注解的存在,这种修改...

    javassist 包

    3. 类加载器:在运行时,Javaassist使用自定义的类加载器 (`ClassPool` 中的 `makeClass` 方法) 将修改后的字节码转换为实际的Java类并加载到JVM中。 4. 代码生成:Javaassist提供了类似于Java语法的API,可以方便...

    javassist,Java字节码工程工具包.zip

    1. 类转换:Javassist可以读取任何已存在的类,并将其转换为字节码表示。这使得在运行时修改已有类的结构成为可能,比如添加新的方法、字段或者修改已有方法的行为。 2. 动态代理:在Java中,Javassist可以用于创建...

    Struts2升级到2.5.30,问题解决及过程记录

    代码中使用了Struts2 API的地方需要检查和适配。 4. **兼容性测试**:在升级后,进行全面的单元测试和集成测试,确保所有功能正常工作,特别是那些依赖于Struts2核心功能的部分。 5. **安全修复**:Struts2 2.5.30...

    sisu-plexus-shim-2.3.0-11.el7.x64-86.rpm.tar.gz

    sisu-plexus-shim-2.3.0-11.el7.x64-86.rpm.tar.gz是一款Linux操作系统中使用的安装包,主要分布在使用Red Hat Enterprise Linux(RHEL)及其衍生版本(如CentOS)的环境中。该安装包通过tar.gz格式压缩,通常用于...

    struts2辅助jar包

    描述中提到的问题,当只引入了Struts2的核心包并尝试启动Tomcat服务器时,可能会遇到报错。这通常是由于缺少必要的依赖导致的。在这种情况下,"javassist-3.9.0.GA.jar"和"commons-io-2.0.1.jar"是两个关键的辅助JAR...

    maven-plugin-testing-harness-2.1-11.el7.x64-86.rpm.tar.gz

    如果在安装过程中遇到任何问题,文件描述中提到可以通过私信博主来获得安装指导。 文件中还包含了一个标签“rpm”,这进一步强调了软件包的安装类型。文件列表中包含了一个readme.md文件,这通常是个文本文件,包含...

    更正之前上传的马士兵老师hibernate环境的jar包

    在Hibernate项目中,日志记录是非常关键的,它可以帮助开发者在调试和问题排查时获取必要的信息。因此,缺少这个jar包可能会导致日志功能无法正常使用。 一个完整的Hibernate环境通常需要以下主要的jar包: 1. ...

    plexus-cli-1.2-20.el7.x64-86.rpm.tar.gz

    安装指令中提到,如果用户在安装过程中遇到任何问题,可以通过私信博主来获得全程的安装指导。 对于熟练的Linux用户来说,这一系列操作可以使得plexus-cli工具快速地被安装到系统中,从而利用plexus-cli提供的各种...

    maven-error-diagnostics-2.2.1-47.el7.x64-86.rpm.tar.gz

    如果在安装过程中遇到问题,文件的描述中提到了“私信博主,全程指导安装”的选项,这表明提供了一个额外的用户支持途径,以帮助用户通过可能的安装难题。 此外,所有提及的文件和包都是64位的(x64),且适用于...

    maven-plugin-registry-2.2.1-47.el7.x64-86.rpm.tar.gz

    此外,如果用户在安装过程中遇到任何问题,可以私信博主获取全程指导。 从压缩包中的文件名称列表可以看出,这些文件是一系列RPM包,这些RPM包是Red Hat及其衍生版如CentOS的标准软件包格式。这些包可能包含Java库...

    HikariCP所需jar包

    HikariCP还可能依赖其他的jar包,如slf4j-api.jar(日志框架API)、logback-classic.jar(具体的日志实现)、guava.jar(Google的工具集,提供各种实用工具类)、spring-jdbc.jar(如果在Spring框架中使用HikariCP)...

    resteasy-base-jaxrs-3.0.6-4.el7.x64-86.rpm.tar.gz

    此外,安装过程中可能还会涉及到解决依赖冲突、权限问题以及对已安装软件的升级等问题。 这个压缩包包含了构建RESTful Web Services所需的一系列基础组件。它的安装可以为开发基于RESTful架构的服务提供支持,但...

    maven-wagon-2.4-3.el7.x64-86.rpm.tar.gz

    maven-wagon-2.4-3.el7.x64-86.rpm.tar.gz是一个压缩包文件,它包含了maven-...安装完maven-wagon-2.4-3.el7后,用户可以享受Maven提供的多种传输协议支持,便于在项目构建中使用Maven进行高效、跨平台的文件传输。

Global site tag (gtag.js) - Google Analytics