Javassist简单应用小结

概述

  Javassist是一款字节码编辑工具,可以直接编辑和生成Java生成的字节码,以达到对.class文件进行动态修改的效果。熟练使用这套工具,可以让Java编程更接近与动态语言编程。

  下面一个方法的目的是获取一个类加载器(ClassLoader),以加载指定的.jar或.class文件,在之后的代码中会使用到。

private static ClassLoader getLocaleClassLoader() throws Exception {
	List<URL> classPathURLs = new ArrayList<>();
	// 加载.class文件路径
	classPathURLs.add(classesPath.toURI().toURL());

	// 获取所有的jar文件
	File[] jarFiles = libPath.listFiles(new FilenameFilter() {
		@Override
		public boolean accept(File dir, String name) {
			return name.endsWith(".jar");
		}
	});
	Assert.assertFalse(ObjectHelper.isArrayNullOrEmpty(jarFiles));

	// 将jar文件路径写入集合
	for (File jarFile : jarFiles) {
		classPathURLs.add(jarFile.toURI().toURL());
	}

	// 实例化类加载器
	return new URLClassLoader(classPathURLs.toArray(new URL[classPathURLs.size()]));
}

获取类型信息

@Test
public void test() throws NotFoundException {
	// 获取默认类型池对象
	ClassPool classPool = ClassPool.getDefault();

	// 获取指定的类型
	CtClass ctClass = classPool.get("java.lang.String");

	System.out.println(ctClass.getName());	// 获取类名
	System.out.println("\tpackage " + ctClass.getPackageName());	// 获取包名
	System.out.print("\t" + Modifier.toString(ctClass.getModifiers()) + " class " + ctClass.getSimpleName());	// 获取限定符和简要类名
	System.out.print(" extends " + ctClass.getSuperclass().getName());	// 获取超类
	// 获取接口
	if (ctClass.getInterfaces() != null) {
		System.out.print(" implements ");
		boolean first = true;
		for (CtClass c : ctClass.getInterfaces()) {
			if (first) {
				first = false;
			} else {
				System.out.print(", ");
			}
			System.out.print(c.getName());
		}
	}
	System.out.println();
}

修改类方法

@Test
public void test() throws Exception {
	// 获取本地类加载器
	ClassLoader classLoader = getLocaleClassLoader();
	// 获取要修改的类
	Class<?> clazz = classLoader.loadClass("edu.alvin.reflect.TestLib");

	// 实例化类型池对象
	ClassPool classPool = ClassPool.getDefault();
	// 设置类搜索路径
	classPool.appendClassPath(new ClassClassPath(clazz));
	// 从类型池中读取指定类型
	CtClass ctClass = classPool.get(clazz.getName());

	// 获取String类型参数集合
	CtClass[] paramTypes = {classPool.get(String.class.getName())};
	// 获取指定方法名称
	CtMethod method = ctClass.getDeclaredMethod("show", paramTypes);
	// 赋值方法到新方法中
	CtMethod newMethod = CtNewMethod.copy(method, ctClass, null);
	// 修改源方法名称
	String oldName = method.getName() + "$Impl";
	method.setName(oldName);

	// 修改原方法
	newMethod.setBody("{System.out.println(\"执行前\");" + oldName + "($$);System.out.println(\"执行后\");}");
	// 将新方法添加到类中
	ctClass.addMethod(newMethod);

	// 加载重新编译的类
	clazz = ctClass.toClass();		// 注意,这一行会将类冻结,无法在对字节码进行编辑
	// 执行方法
	clazz.getMethod("show", String.class).invoke(clazz.newInstance(), "hello");
	ctClass.defrost();	// 解冻一个类,对应freeze方法
}

动态创建类

@Test
public void test() throws Exception {
	ClassPool classPool = ClassPool.getDefault();

	// 创建一个类
	CtClass ctClass = classPool.makeClass("edu.alvin.reflect.DynamiClass");
	// 为类型设置接口
	//ctClass.setInterfaces(new CtClass[] {classPool.get(Runnable.class.getName())});

	// 为类型设置字段
	CtField field = new CtField(classPool.get(String.class.getName()), "value", ctClass);
	field.setModifiers(Modifier.PRIVATE);
	// 添加getter和setter方法
	ctClass.addMethod(CtNewMethod.setter("setValue", field));
	ctClass.addMethod(CtNewMethod.getter("getValue", field));
	ctClass.addField(field);

	// 为类设置构造器
	// 无参构造器
	CtConstructor constructor = new CtConstructor(null, ctClass);
	constructor.setModifiers(Modifier.PUBLIC);
	constructor.setBody("{}");
	ctClass.addConstructor(constructor);
	// 参数构造器
	constructor = new CtConstructor(new CtClass[] {classPool.get(String.class.getName())}, ctClass);
	constructor.setModifiers(Modifier.PUBLIC);
	constructor.setBody("{this.value=$1;}");
	ctClass.addConstructor(constructor);

	// 为类设置方法
	CtMethod method = new CtMethod(CtClass.voidType, "run", null, ctClass);
	method.setModifiers(Modifier.PUBLIC);
	method.setBody("{System.out.println(\"执行结果\" + this.value);}");
	ctClass.addMethod(method);

	// 加载和执行生成的类
	Class<?> clazz = ctClass.toClass();
	Object obj = clazz.newInstance();
	clazz.getMethod("setValue", String.class).invoke(obj, "hello");
	clazz.getMethod("run").invoke(obj);

	obj = clazz.getConstructor(String.class).newInstance("OK");
	clazz.getMethod("run").invoke(obj);
}

创建代理类

@Test
public void test() throws Exception {
	// 实例化代理类工厂
	ProxyFactory factory = new ProxyFactory();  

	//设置父类,ProxyFactory将会动态生成一个类,继承该父类
	factory.setSuperclass(TestProxy.class);

	//设置过滤器,判断哪些方法调用需要被拦截
	factory.setFilter(new MethodFilter() {
		@Override
		public boolean isHandled(Method m) {
			return m.getName().startsWith("get");
		}
	});

	Class<?> clazz = factory.createClass();
	TestProxy proxy = (TestProxy) clazz.newInstance();
	((ProxyObject)proxy).setHandler(new MethodHandler() {
		@Override
		public Object invoke(Object self, Method thisMethod, Method proceed, Object[] args) throws Throwable {
			//拦截后前置处理,改写name属性的内容
			//实际情况可根据需求修改
			System.out.println(thisMethod.getName() + "被调用");
			try {
				Object ret = proceed.invoke(self, args);
				System.out.println("返回值: " + ret);
				return ret;
			} finally {
				System.out.println(thisMethod.getName() + "调用完毕");
			}
		}
	});

	proxy.setName("Alvin");
	proxy.setValue("1000");
	proxy.getName();
	proxy.getValue();
}

  其中,TestProxy类内容如下:

public class TestProxy {
	private String name;
	private String value;

	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getValue() {
		return value;
	}
	public void setValue(String value) {
		this.value = value;
	}
}

获取方法名称

@Test
public void test() throws Exception {
	// 获取本地类加载器
	ClassLoader classLoader = getLocaleClassLoader();
	// 获取要修改的类
	Class<?> clazz = classLoader.loadClass("edu.alvin.reflect.TestLib");

	// 实例化类型池
	ClassPool classPool = ClassPool.getDefault();
	classPool.appendClassPath(new ClassClassPath(clazz));
	CtClass ctClass = classPool.get(clazz.getName());

	// 获取方法
	CtMethod method = ctClass.getDeclaredMethod("show", ObjectHelper.argumentsToArray(CtClass.class, classPool.get("java.lang.String")));
	// 判断是否为静态方法
	int staticIndex = Modifier.isStatic(method.getModifiers()) ? 0 : 1; 

	// 获取方法的参数
	MethodInfo methodInfo = method.getMethodInfo();
	CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
	LocalVariableAttribute localVariableAttribute = (LocalVariableAttribute)codeAttribute.getAttribute(LocalVariableAttribute.tag);

	for (int i = 0; i < method.getParameterTypes().length; i++) {
		System.out.println("第" + (i + 1) + "个参数名称为: " + localVariableAttribute.variableName(staticIndex + i));
	}
}

  关于“获取方法名称”,其主要作用是:当Java虚拟机加载.class文件后,会将类方法“去名称化”,即丢弃掉方法形参的参数名,而是用形参的序列号来传递参数。如果要通过Java反射获取参数的参数名,则必须在编辑是指定“保留参数名称”。Javassist则不存在这个问题,对于任意方法,都能正确的获取其参数的参数名。

  Spring MVC就是通过方法参数将请求参数进行注入的,这一点比struts2 MVC要方便很多,Spring也是借助了Javassist来实现这一点的。

附录

  代码中使用了一个ObjectHelper类,这是我自用的一个小工具类,该类的代码可点击查看

Javassist简单应用小结

时间: 2025-01-02 00:35:49

Javassist简单应用小结的相关文章

180725-InfluxDB-v1.6.0安装和简单使用小结

InfluxDB安装和简单使用小结 InfluxDB是一个时序性数据库,因为工作需求,安装后使用测试下是否支持大数据下的业务场景 说明: 安装最新版本 v1.6.0 集群版本要收费,单机版本免费 内部集成的web控制台被ko掉了 I. 安装 直接到官网,查询对应的下载安装方式 Installing InfluxDB OSS 安装方式 SHA256: fa118d657151b6de7c79592cf7516b3d9fada813262d5ebe16516f5c0bf62039 wget http

hessian 简单使用小结

hessian简介:是一个轻量级的remoting onhttp工具,使用简单的方法提供了RMI的功能. 相比WebService,Hessian更简单.快捷.采用的是二进制RPC协议,因为采用的是二进制协议,所以它很适合于发送二进制数据 1.开发环境 maven----springmvc 2.hessian需要一个客服端一个服务器端  客服端和服务端都需要有hessian的jar 使用maven 直接添加 <dependency> <groupId>com.caucho</

简单的小结对象

1 //初始化对象2 function__construct(){ } Java面向对象的特征:封装.继承.多态.抽象

SimpleDateFormat.format的简单使用小结

format的用法 是将当前时间格式转换为指定格式 场景一:给定毫秒数或者当前系统时间,返回指定时间格式 输入 注意:在第二行和第三行抓化为long类型时要强制性long转换,否则会提示type int is out of range 场景二:给定任意时间格式,返回毫秒数 parse转化为Date类型后可以直接获取毫秒.输入2017-06-28T09:52 返回毫秒 场景三:将给定格式转换为指定格式 输入 06-29-2017  输出  2017/06/29  先parse转化为date类型,再

PHP多进程简单实例小结

本文实例讲述了PHP多进程.分享给大家供大家参考,具体如下: PHP创建多进程需要使用到pcntl模块 在编译时加上--enable-pcntl打开进程控制支持,不是Unix类系统不支持此模块 php官网介绍http://php.net/manual/zh/book.pcntl.php,创建子进程需要使用到pcntl_fork(),文档上介绍该函数说 ,pcntl_fork — 在当前进程当前位置产生分支(子进程). 译注:fork是创建了一个子进程,父进程和子进程 都从fork的位置开始向下继

tomcat的简单概要小结

tomcat的主要目录的概念 1.bin目录(主要目录) 主要是用来存放tomcat的命令,比如启动和停止.主要有两大类 以.sh结尾的(linux命令) 以.bat结尾的(windows命令) 很多环境变量的设置都在此处,例如可以设置JDK路径.TOMCAT路径,startup 用来启动tomcat,shutdown 用来关闭tomcat,修改catalina可以设置tomcat的内存. 2.conf目录 主要是用来存放tomcat的一些配置文件. 3.lib目录 主要用来存放tomcat运行

ASP.NET MVC ViewData/ViewBag 简单小结

近期在项目中遇到一个问题,就是用ViewBag.Model存储匿名对象传递给View,但是需要根据条件给匿名对象添加属性,这个可真心不易,Google了一下发现很多方案都是动态编译神马的,感觉好高大上,最后也没采用,因为不知道动态编译的性能消耗大不大. 最后是自己简单研究了一下,在ViewBag.Model中存储了Dictionary<string, object>,在View通过Model[key]的方式可以正常读取相应的值,在此对ViewData和ViewBag的使用进行一个简单的小结:1

变量的存储小结

变量是C语言最基础也是最核心的概念,对于初学C语言的同学来说变量的存储总是觉得比较混乱.这里对C的变量做一个简单的小结,希望对大家有所帮助. 我们看到对变量的讲解的时候看到很多概念,属性,存储期,内部链接,外部链接,空链接,全局变量,局部变量,寄存器变量,静态变量等等.看起来很混乱,下面我们梳理一下. 其实我们再使用变量的时候大多关心的只有三个方面:变量的存在期,变量的类型,变量的作用域. 我们想一下变量的定义方法分为三部分: <存储类别>  <变量的数据类型>   <变量名

极化码小结

一个暑假过去了,身心懈怠,知识荒疏,为了方便接下类的研究工作.这里对已经掌握的极化码知识做一个简单的小结. Chapter1 极化码简介: 极化码建立在信道极化这一现象之上. 信道极化现象来自于信道合并与信道分裂这两种信道操作. 信道合并: 将N个独立信道W通过变换使之变为一个具有“集体意义”的信道WN,这里“集体意义”的产生来源于变换,而变换遵循固定的规则. 首先,变换是一个递归过程.对于N(N=2n)个独立信道W,想要将其变为一个具有“集体意义”的信道WN需要进行n次信道操作,每次信道操作都