写在前面
本文看下如何使用javassist生成带有注解的类。
1:程序
- 测试类
package com.dahuyou.javassist.huohuo.cc;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;// 保留到哪个阶段,SOURCE 仅仅在源代码中保留,编译器不保留该信息。CLASS(默认保留方式),编译器保留,但VM不保留。RUNTIME VM保留,即一直在
// SOURCE作用:给人看,有点类似于注释了,但是是源代码的一部分,比如Override,告诉我们这是重写父类的方法
// CLASS作用:编译器用?咋用???
// RUNTIME作用:程序运行使用,用的最多,比如Deprecated
@Retention(RetentionPolicy.RUNTIME)
// 用在哪里 TYPE 类上 METHOD方法上 FIELD用在字段上
@Target(ElementType.TYPE)
public @interface MyAnnotationOnClazz {boolean opened() default false;String desc() default "";int number();
}
package com.dahuyou.javassist.huohuo.cc;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;// 保留到哪个阶段,SOURCE 仅仅在源代码中保留,编译器不保留该信息。CLASS(默认保留方式),编译器保留,但VM不保留。RUNTIME VM保留,即一直在
// SOURCE作用:给人看,有点类似于注释了,但是是源代码的一部分,比如Override,告诉我们这是重写父类的方法
// CLASS作用:编译器用?咋用???
// RUNTIME作用:程序运行使用,用的最多,比如Deprecated
@Retention(RetentionPolicy.RUNTIME)
// 用在哪里 TYPE 类上 METHOD方法上 FIELD用在字段上
@Target(ElementType.METHOD)
public @interface MyAnnotationOnMethod {String typeName() default "";long payAmount();
}
- 生成代码类
package com.dahuyou.javassist.huohuo.cc;import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.Modifier;
import javassist.bytecode.AnnotationsAttribute;
import javassist.bytecode.Bytecode;
import javassist.bytecode.ConstPool;
import javassist.bytecode.MethodInfo;
import javassist.bytecode.annotation.Annotation;
import javassist.bytecode.annotation.BooleanMemberValue;
import javassist.bytecode.annotation.LongMemberValue;
import javassist.bytecode.annotation.StringMemberValue;public class MyDoItttt extends ClassLoader {public static void main(String[] args) throws Exception {ClassPool pool = ClassPool.getDefault();// 创建类CtClass ctClass = pool.makeClass("com.dahuyou.javassist.huohuo.bb.Helloworld_javassist");// 创建方法
// CtMethod queryAmount = new CtMethod(CtClass.doubleType, "queryAmount", new CtClass[]{pool.get(String.class.getName())}, ctClass);CtMethod queryAmount = new CtMethod(CtClass.doubleType, "queryAmount", new CtClass[]{}, ctClass);
// queryAmount.addLocalVariable();
// queryAmount.
// CtMethod queryAmount = new CtMethod(CtClass.voidType, "queryAmount", new CtClass[]{pool.get(String.class.getName())}, ctClass);queryAmount.setModifiers(Modifier.PUBLIC);// 执行到这里,空方法public double queryAmount(String var1) {}就有了ctClass.addMethod(queryAmount);MethodInfo methodInfo = queryAmount.getMethodInfo();// 获取常量池,注意虽然是通过类信息对象获取的,但常量池是属于类级别的,只不过这里和方法做了关联ConstPool constPool = methodInfo.getConstPool();// 设置类注解AnnotationsAttribute annotationsAttribute = new AnnotationsAttribute(constPool, AnnotationsAttribute.invisibleTag);Annotation clazzAnnotation = new Annotation("com/dahuyou/javassist/huohuo/cc/MyAnnotationOnClazz", constPool);// 设置类注解的属性们/*String clazzDesc() default "";String alias() default "";long timeOut() default 350;boolean opened() default false;String desc() default "";int number();*/clazzAnnotation.addMemberValue("opened", new BooleanMemberValue(true, constPool));clazzAnnotation.addMemberValue("desc", new StringMemberValue("api描述啊", constPool));
// clazzAnnotation.addMemberValue("number", new IntegerMemberValue(455, constPool));annotationsAttribute.setAnnotation(clazzAnnotation);ctClass.getClassFile().addAttribute(annotationsAttribute); // 画龙点睛,调用类的addAttribute添加注解,没有可不行// 添加方法注解AnnotationsAttribute methodAttribute = new AnnotationsAttribute(constPool, AnnotationsAttribute.visibleTag);Annotation methodAnnotation = new Annotation("com/dahuyou/javassist/huohuo/cc/MyAnnotationOnMethod", constPool);methodAnnotation.addMemberValue("typeName", new StringMemberValue("查询费用111", constPool));methodAnnotation.addMemberValue("payAmount", new LongMemberValue(562L, constPool));methodAttribute.setAnnotation(methodAnnotation);methodInfo.addAttribute(methodAttribute);Bytecode bytecode = new Bytecode(constPool);bytecode.addGetstatic("java/math/BigDecimal", "TEN", "Ljava/math/BigDecimal;");bytecode.addInvokevirtual("java/math/BigDecimal", "doublevalue", "()D");bytecode.addReturn(CtClass.doubleType);methodInfo.setCodeAttribute(bytecode.toCodeAttribute());ctClass.writeFile();// 以下代码总是报:Ljava/math/BigDecimal;java.lang.ClassFormatError: Arguments can't fit into locals... 感觉是本地变量表不够大导致。但不知道该如何设置System.out.println("-------华丽的分割线-------");/* byte[] bytes = ctClass.toBytecode();Class<?> clazzNew = new MyDoItttt().defineClass("com.dahuyou.javassist.huohuo.bb.Helloworld_javassist", bytes, 0, bytes.length);MyAnnotationOnClazz annotationOnClazz = clazzNew.getAnnotation(MyAnnotationOnClazz.class);System.out.println("annotationOnClazz.opened: " + annotationOnClazz.opened());System.out.println("annotationOnClazz.desc: " + annotationOnClazz.desc());MyAnnotationOnMethod annotationOnMethod = clazzNew.getDeclaredMethod("queryAmount").getAnnotation(MyAnnotationOnMethod.class);System.out.println("annotationOnMethod.payAmount: " + annotationOnMethod.payAmount());System.out.println("annotationOnMethod.typeName: " + annotationOnMethod.typeName());*/}
}
运行后查看生成的字节码: