写在前面
本文看下如何使用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());*/
}
}
运行后查看生成的字节码: