javasist应用实践
目录
javasist
javasist采用操作字节码方式,可以动态修改和生成class文件或者说java类。class文件的属性,是通过自己去动态拼接的,包括所有的字段、方法、接口都是事先添加的,然后调用writFile()生成。可用于热更新,监控,动态生成、修改java类。
1.获取一个池并创建一个类
//获取池减少消耗
ClassPool classPool = ClassPool.getDefault();
// 创建一个类
CtClass ctClass = classPool.makeClass(className);
2.添加类注解
// 添加类注解
ClassFile ccFile = ctClass.getClassFile();
ConstPool constPool = ccFile.getConstPool();
AnnotationsAttribute bodyAttr = new AnnotationsAttribute(constPool, AnnotationsAttribute.visibleTag);
Annotation bodyAnnot = new Annotation("com.game2sky.publib.annotation.Dict", constPool);
bodyAttr.addAnnotation(bodyAnnot);
Annotation bodyAnnot2 = new Annotation("javax.persistence.Table", constPool);
bodyAnnot2.addMemberValue("name", new StringMemberValue(tableName, constPool));
bodyAttr.addAnnotation(bodyAnnot2);
ccFile.addAttribute(bodyAttr);
3.为类设置字段
CtField field = null;
try
{
field = new CtField(classPool.get(paramTypes.get(i)), paramNames.get(i), ctClass);
field.setModifiers(Modifier.PRIVATE);
}
4.添加getter和setter方法
// 添加getter和setter方法
ctClass.addMethod(CtNewMethod.setter(setParams.get(i), field));
ctClass.addMethod(CtNewMethod.getter(getParams.get(i), field));
ctClass.addField(field);
5.导入某个目录下的包,第三方jar包
//往池里导入某个目录下的包,那么在获取对应的class时池对象会从当前目录进行搜索
classPool.importPackage("gnu.trove.map");
classPool.importPackage("gnu.trove.map.hash");
classPool.importPackage("java.util");
classPool.importPackage("com.game2sky.application.common.dict.init");
例如:我们构造trove4J的public static TIntObjectMap<DictAbility> map = new TIntObjectHashMap<DictAbility>();
需要导同时入:
classPool.importPackage("gnu.trove.map");
classPool.importPackage("gnu.trove.map.hash");
不然,会报找不到TIntObjectMap对象
6.添加getter和setter方法
// 添加getter和setter方法
ctClass.addMethod(CtNewMethod.setter(setParams.get(i), field));
ctClass.addMethod(CtNewMethod.getter(getParams.get(i), field));
ctClass.addField(field);
7.给字段设置public 和 static
ctField.setModifiers(Modifier.PUBLIC | Modifier.STATIC);
8.添加方法,一定注意泛型需要/* */包住,并且DictAbility对象一定要扫描的到
CtMethod m = CtNewMethod.make(
"public void init(List/*<DictAbility>*/ l){int size = l.size();\n" +
"\t\tTIntObjectMap/*<DictAbility>*/ map = new TIntObjectHashMap/*<DictAbility>*/();\n" +
"\t\tfor (int index = (size-1); index >= 0; index--)\n" +
"\t\t{\n" +
"\t\t\tDictAbility record = (DictAbility) l.get(index);\n" +
"\t\t\tDictAbility existRecord = map.get(record.getId());\n" +
"\t\t\tnewCache.put(record.getId(), record);\n" +
"\t\t} \n" +
"\t\tmap = newCache;}",
ctClass);
ctClass.addMethod(m);
// 另一种为类设置方法,在body体设置不如上面那种灵活
CtMethod method = new CtMethod(CtClass.voidType, "init", new CtClass[] { classPool.get(List.class.getName()) },
ctClass);
method.setModifiers(Modifier.PUBLIC);
method.setBody("{}");
ctClass.addMethod(method);
9.设置接口
//设置代理类的接口
CtClass interName = classPool.getCtClass(interfaceName); //获取代理对象的接口类
CtClass[] interfaces = new CtClass[]{interName};
ctClass.setInterfaces(interfaces);
10.生成并返回class对象
//调用writeFile()对象会锁死
ctClass.writeFile();
Class<?> clazz = ctClass.toClass();
11.把生成的class文件写入文件
byte[] byteArr = ctClass.toBytecode();
File file = new File("F:/xxx/"+clazz.getSimpleName()+".class");
if(file.exists())
{
file.delete();
}
总结
javasist在构造class过程中,所有出现的对象都必须要扫描的到,例如上面出现的DictAbility对象。