`
benx
  • 浏览: 276162 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

javassist与classLoader

    博客分类:
  • java
 
阅读更多

 

javassist.jar是个非常不错的classCode修改框架,简单实用

 

通过javassist和classLoader结合,在加载class时修改class,类似于Spring AOP,可以在指定的class中额外加载指定的功能

 

下面的例子中,在AppClassLoader的classPath中包含的class都加入了方法信息打印(参数打印、方法调用栈),方法返回信息打印。

 

利用这个,如果要看Spring,Hibernate的大点的框架时,通过这个运行一个例子,可以很方便了解方法的调用时序图(调用堆栈),每个参数的值。

 

当然这个例子很简陋,比如打印参数只能打印基本类型,如果是高级对象需要重写,解析。

 

 

 


import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.NotFoundException;

/**
 * 该类的主要原理是通过类加载时修改classCode,修改的方法可以是对方法的修改,新增,删除,属性的修改,新增删除,等其他操作
 * 通过这个例子,可以很方便的对程序进行日志跟踪,排错,流程理解等
 * 
 * @author jin.xiong
 * 
 */
 public class JavassistLoader extends ClassLoader {

	@SuppressWarnings({ "rawtypes", "unchecked" })
	public static void main(String[] args) throws Throwable {
		JavassistLoader s = new JavassistLoader();
		Class c = s.loadClass("com.benx.Test");
		c.getDeclaredMethod("main", new Class[] { String[].class }).invoke(null, new Object[] { args });
	}

	private ClassPool	pool;

	private ClassLoader	classLoader;

	public JavassistLoader() throws NotFoundException {

		/**
		 * 设置classLoader的parent为ExtClassLoader,如果没有设置,将默认使用AppClassLoader,
		 * 因为classLoader的加载顺序是从parent
		 * --child的相亲委派加载关系,所以如果不设置那么classPath的class将不会使用自定义加载器
		 * 
		 * 如果加载的class为外部的,那么可以不设置parent,使用默认
		 */
		super(Thread.currentThread().getContextClassLoader().getParent());
		classLoader = Thread.currentThread().getContextClassLoader();

		String[] classPath = System.getProperty("java.class.path").split(";");
		pool = ClassPool.getDefault();
		for (String path : classPath) {
			System.out.println(path);
			pool.appendClassPath(path);
		}
		// TODO 可以自定义需要加载的类
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	protected Class findClass(String name) throws ClassNotFoundException {
		// TODO 可以对class过滤
		try {
			CtClass cc = pool.get(name);
			System.out.println(cc.isFrozen() + "  " + cc.isInterface() + "  " + cc.isModified() + "   " + cc.isPrimitive());
			if (!cc.isInterface()) {
				CtMethod[] ms = cc.getDeclaredMethods();
				for (CtMethod m : ms) {

					if (m.isEmpty())
						continue;
					// TODO 可以对method过滤
					StringBuffer sb = new StringBuffer("");
					sb.append("{");
					// TODO 可以指定修改的内容
					m.insertBefore(getStackTraceCode().toString() + getParaCode(m).toString());

					m.insertAfter(getReturnCode().toString());
					sb.append("}");
				}
			}
			byte[] b = cc.toBytecode();
			return defineClass(name, b, 0, b.length);
		} catch (Exception e) {
			// e.printStackTrace();
			Class cls = classLoader.loadClass(name);
			return cls;
			// throw new ClassNotFoundException();
		}
	}

	private StringBuffer getReturnCode() {
		StringBuffer temp = new StringBuffer("");
		temp.append("StackTraceElement[] as = Thread.currentThread().getStackTrace();");
		temp.append("System.out.println(\"class:\"+as[1].getClassName()+\" method:\"+as[1].getMethodName() +\"  return:\"+$_);");
		return temp;
	}

	/**
	 * 方法参数内容显示的嵌入代码
	 * 
	 * @param cm
	 * @return
	 * @throws NotFoundException
	 */
	private StringBuffer getParaCode(CtMethod cm) throws NotFoundException {
		StringBuffer temp = new StringBuffer("");
		temp.append("		Object[] ob = $args; ");
		temp.append("		StringBuffer sb = new StringBuffer();");
		temp.append("       sb.append(\"[\");");
		temp.append("       for (int i = 0; i < ob.length; i++) {");
		temp.append("            sb.append(ob[i]+\",\");");
		temp.append("       }");
		temp.append("       sb.append(\"]\");");
		temp.append("		System.out.println(\"parameter is:\"+sb);");
		return temp;
	}
	/**
	 * 方法调用栈的嵌入代码
	 * 
	 * @return
	 */
	private StringBuffer getStackTraceCode() {
		StringBuffer sb = new StringBuffer("");
		sb.append("		StackTraceElement[] as = Thread.currentThread().getStackTrace();");
		sb.append("		System.out.print(\"class:\"+as[1].getClassName()+\" method:\"+as[1].getMethodName());");
		sb.append("		StringBuffer temp = new StringBuffer();");
		sb.append("		for (int i = (as.length - 1); i > 0; i--) {");
		sb.append("			temp.append(as[i].getClassName() + \".\" + as[i].getMethodName() + \"   \");");
		sb.append("		}");
		sb.append("		System.out.print(\"   StackTrace:\");");
		sb.append("		System.out.print(temp.toString());");
		return sb;
	}
}
 
分享到:
评论

相关推荐

    javaagent+javassist

    将`javaagent`与`javassist`结合使用,可以实现强大的功能。例如,我们可以通过javaagent在程序启动时注入javassist生成的代码,以实现对目标类的动态增强。以下是一个简单的示例: 1. 创建一个`javaagent`,定义一...

    Javassist代码注入

    在本文中,我们将深入探讨Javassist如何实现代码注入,并结合实例`HotPatchDemo`来展示其实现过程。 首先,理解代码注入的基本概念是必要的。代码注入是一种技术,允许在程序运行时向现有代码中插入新的行为或修改...

    javassist-3.15.0-GA

    - **类加载器集成**:与Java的ClassLoader协同工作,可以在运行时修改并加载修改后的类。 - **高级API**:提供了高级的API来操作类,如类型转换、注解处理、异常处理等。 - **兼容性**:支持JDK1.3及以上版本,...

    javassist-3.7.ga.jar

    `javassist-3.7.ga.jar` 文件是 Javaassist 库的一个版本,版本号为 3.7 GA(General Availability),意味着这是一个公开可用的稳定版本。 Javaassist 提供了一系列的 API,使得开发者可以方便地读取、修改和创建 ...

    javassist3.9GA

    这个"javassist3.9GA"版本是Javaassist的一个发行版本,主要目的是清理从字节码类文件中测试代码。这个过程对于构建可维护和高效的软件系统尤其重要,因为测试代码通常会在生产环境中增加不必要的负担。 Javaassist...

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

    6. **兼容性**:Javassist与Java标准库的`java.lang.ClassLoader`和`java.lang.reflect`紧密集成,因此可以无缝地与其他Java应用和库配合使用。同时,它支持从Java 1.4到最新的Java版本,确保了广泛的兼容性。 7. *...

    使用自定义ClassLoader解决反序列化serialVesionUID不一致问题 _ 回忆飘如雪1

    此方法能解决所有类的`serialVersionUID`问题,但如果类的属性类型发生变化,仅修改`serialVersionUID`是不够的,因为`serialVersionUID`的变更不仅与类的标识有关,还与类的属性和方法相关。 5. **使用`...

    美团IDEA插件实现Java应用的热部署实践

    热部署插件的实现原理主要是通过Agent字节码增强、Javassist、Classloader等技术来实现的。Agent字节码增强是指在Java字节码中插入一些用于热部署的代码,以便在运行时可以实现热部署。Javassist是一个Java库,提供...

    collection-agent:通过javaagent和javassist技术实现对java的ArrayList和HashMap的增强,避免在虚拟器一次load大量数据时导致OOM

    collection-agent 通过javaagent和javassist技术实现对java的ArrayList和HashMap的增强,在操作集合元素时判断集合...agent的代码与你的main方法在同一个JVM中运行,并被同一个system classloader装载,被同一的安全

    扫描Class文件的方法

    3. **Javassist**:除了字节码操作,Javassist还提供了扫描类的能力,可以用于动态修改类。 五、实战示例 以下是一个简单的文件系统扫描示例: ```java import java.io.File; import java.io.FilenameFilter; ...

    java类重载,可以用于热更新

    在Java中,类是由ClassLoader加载的,当一个类被加载后,如果该类的.class文件被修改,ClassLoader可以重新加载这个修改后的类,从而实现运行时的代码更新。但是,需要注意的是,热更新并非总是可行的,有些复杂的...

    aop的四种实现方式

    public byte[] transform(ClassLoader loader, String className, Class&lt;?&gt; classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) { // 字节码操作逻辑 } } ``` ### 4. Dynamic ...

    动态编译字符串成java,并且添加class到jvm

    默认的`ClassLoader`无法加载内存中的`.class`,所以我们需要自定义一个`ClassLoader`,覆盖`findClass()`方法,从内存中读取`.class`字节码。 8. **创建并执行实例**: 使用`Class.forName()`加载类后,可以调用`...

    Java之——类热加载

    Java的`javac`工具可用于编译源代码,`ASM`或`Javassist`库可以方便地操作字节码。 5. Maven插件支持 在开发环境中,我们可以使用Maven的`maven-compiler-plugin`和`maven-surefire-plugin`等插件配置热加载。例如...

    修改.class文件.zip

    然后,使用`java.lang.ClassLoader`的`defineClass`方法将字节码转换为`java.lang.Class`对象。接下来,获取到需要修改的方法,这可以通过反射API完成,如`Class.getMethod`。一旦找到了目标方法,你可以使用`...

    FakeClassloader:替换classloader,完成全局代理,可代理接口、private方法、final方法及final类

    在Java编程语言中,`ClassLoader`是一个至关重要的组件,它负责加载类到JVM(Java虚拟机)中。`FakeClassloader`是一个特殊设计的类加载器,它允许开发者进行全局代理,覆盖常规的类加载行为,使得我们可以对类、...

    Java面试要点(适用于2年以上经验,1年亦可)

    1. 反射与 javassist 2. 反射与工厂模式、java.lang.reflect.* 序列化 1. 什么是序列化与反序列化、为什么序列化 2. 序列化底层原理 3. 序列化与单例模式 4. protobuf 5. 序列化并不安全 注解 1. 元注解、自定义...

    seachClass.zip

    在这个场景中,我们关注的是一个名为"seachClass.zip"的压缩包,它显然与Java编程语言中的类(Class)文件有关。Java类文件是编译后Java源代码的结果,用于在Java虚拟机(JVM)上运行。这个压缩包可能包含了多个....

    行业分类-设备装置-一种向目标进程内注入Java字节码的方法及装置.zip

    这些库允许程序在运行时动态生成或修改字节码,然后通过ClassLoader加载到JVM中,使得修改后的字节码影响目标进程的执行流程。 2. **应用场景**: - **AOP(面向切面编程)**:在不修改原有代码的情况下,通过字节...

Global site tag (gtag.js) - Google Analytics