首页
Web开发
Windows程序
编程语言
数据库
移动开发
系统相关
微信
其他好文
会员
首页
>
编程语言
> 详细
Javassist字节码增强示例
时间:
2015-10-30 23:02:23
阅读:
277
评论:
0
收藏:
0
[点我收藏+]
标签:
概述
Javassist是一款字节码编辑工具,可以直接编辑和生成Java生成的字节码,以达到对.class文件进行动态修改的效果。熟练使用这套工具,可以让Java编程更接近与动态语言编程。
下面一个方法的目的是获取一个类加载器(ClassLoader),以加载指定的.jar或.class文件,在之后的代码中会使用到。
[java]
view plain
copy
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()]));
}
获取类型信息
[java]
view plain
copy
@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();
}
修改类方法
[java]
view plain
copy
@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方法
}
动态创建类
[java]
view plain
copy
@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);
}
创建代理类
[java]
view plain
copy
@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类内容如下:
[java]
view plain
copy
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;
}
}
获取方法名称
[java]
view plain
copy
@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来实现这一点的。
Javassist字节码增强示例
标签:
原文地址:http://www.cnblogs.com/cunkouzh/p/4924481.html
踩
(
0
)
赞
(
0
)
举报
评论
一句话评论(
0
)
登录后才能评论!
分享档案
更多>
2021年07月29日 (22)
2021年07月28日 (40)
2021年07月27日 (32)
2021年07月26日 (79)
2021年07月23日 (29)
2021年07月22日 (30)
2021年07月21日 (42)
2021年07月20日 (16)
2021年07月19日 (90)
2021年07月16日 (35)
周排行
更多
Spring Cloud 从入门到精通(一)Nacos 服务中心初探
2021-07-29
基础的排序算法
2021-07-29
SpringBoot|常用配置介绍
2021-07-29
关于 .NET 与 JAVA 在 JIT 编译上的一些差异
2021-07-29
C语言常用函数-toupper()将字符转换为大写英文字母函数
2021-07-29
《手把手教你》系列技巧篇(十)-java+ selenium自动化测试-元素定位大法之By class name(详细教程)
2021-07-28
4-1 YAML配置文件 注入 JavaBean中
2021-07-28
【python】 用来将对象持久化的 pickle 模块
2021-07-28
马拉车算法
2021-07-28
用Python进行冒泡排序
2021-07-28
友情链接
兰亭集智
国之画
百度统计
站长统计
阿里云
chrome插件
新版天听网
关于我们
-
联系我们
-
留言反馈
© 2014
mamicode.com
版权所有 联系我们:gaon5@hotmail.com
迷上了代码!