Java 注解:注解处理器获取泛型真实类型
来源:互联网 发布:js原型和原型链的特点 编辑:程序博客网 时间:2024/05/29 09:05
- Java 注解注解处理器获取泛型真实类型
- 如何定义注解
- 获取注解信息
- 注解处理器
- AbstractProcessor
- 示例注解处理器获取泛型信息
- 示例代码地址
Java 注解:注解处理器获取泛型真实类型
注解 annotation 是 Java 中的一大特性,是插入代码中的元数据。注解的使用能够大大简化代码的编写,所以在很多框架中得到了使用,比如 Web 框架 Spring 中的 @Service、@Resource 注解,比如参数校验框架 hibernate-validator 中的 @NotNull 等注解。
如何定义注解
定义注解需要用到元注解 meta annotation,即描述注解的注解。元注解有以下几类:
1. @Target:描述了注解所修饰的对象范围,比如:类、方法、字段、以及注解本身等等。
2. @Retention:定义注解被保留的时间长短,有三种:源文件、class 文件、运行时。
3. @Documented:表示含有该注解类型的元素(带有注释的)会通过javadoc或类似工具进行文档化。
4. @Inherited:表示注解类型能被自动继承。
Java 中使用 Class 来定义类,使用 enum 定义枚举,使用 @interface 来定义注解。具体定义格式如下:
public @interface 注解名 { 定义体 }
其中的定义体里可以写若干方法来表示配置,方法的名称就是参数的名称,返回值类型就是参数的类型,可以通过 default 来声明参数的默认值。下面给出一个注解的示例:
@Target(ElementType.FIELD)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface AutoParse { String key(); int type() default 0;}
该注解 AutoParse 可以用于描述 FIELD 即类中字段,在运行时仍然能够获取,不可被继承(没有使用 Inherited 注解描述)。
获取注解信息
注解通常用来描述类、方法等,那如何获取一个方法或类上的注解呢?可以通过 Class 类或 Method 类对应的 getAnnotation 方法获取:
public final class Class<T> implements java.io.Serializable, GenericDeclaration, Type, AnnotatedElement { public Annotation[] getAnnotations(); public <A extends Annotation> A getAnnotation(Class<A> annotationClass); public <A extends Annotation> A getDeclaredAnnotation(Class<A> annotationClass); public Annotation[] getDeclaredAnnotations();}public final class Method extends Executable { public <T extends Annotation> T getAnnotation(Class<T> annotationClass); public Annotation[] getDeclaredAnnotations(); // 返回方法参数的注解 public Annotation[][] getParameterAnnotations();}
getDeclaredAnnotations 方法只返回直接存在于此元素上的注解,不会返回继承的注解;而 getAnnotation 则返回全部注解,包含继承的。
注:Field 也有类似的方法可以获取注解。
注解处理器
注解处理器 annotation processor 是 javac 内置的编译时扫描和处理注解的工具,比较常见的用法是在编译时获取注解信息,动态生成 Java 文件。
AbstractProcessor
想要自定义注解处理器需要继承实现 AbstractProcessor 类:
1. init 方法入参是环境变量 ProcessingEnvironment,从中可以拿到报告错误信息的 Messager,用于生成代码文件的 Filer。
2. process 抽象方法,是我们需要实现的,用于处理注解信息的方法,生成类文件的代码放这里。
public abstract class AbstractProcessor implements Processor { public synchronized void init(ProcessingEnvironment processingEnv) { if (initialized) throw new IllegalStateException("Cannot call init more than once."); Objects.requireNonNull(processingEnv, "Tool provided null ProcessingEnvironment"); this.processingEnv = processingEnv; initialized = true; } public abstract boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv);}
示例:注解处理器获取泛型信息
Java 中的泛型有一个类型擦除的概念:
Java中的泛型基本上都是在编译器这个层次来实现的。在生成的Java字节码中是不包含泛型中的类型信息的。使用泛型的时候加上的类型参数,会在编译器在编译的时候去掉。这个过程就称为类型擦除。
那么如何在运行时获取某对象的某个 Field 的真实类型信息呢?既然是在编译期间擦除的,那么我们就可以注解处理器在编译期间获取泛型的真实类型信息。
以下给出一个处理上面的 @AutoParse 注解的注解处理器示例,该注解处理器获取了注解所描述字段的类型信息,并将这些信息写入了一个类文件中:
1. @SupportedAnnotationTypes:指明要处理的注解。
2. @SupportedSourceVersion(SourceVersion.RELEASE_7):指明适合的 Java 版本。
3. 继承 process 方法,做真正的处理工作。
4. Filer 用于创建新的 Java 类。
5. RoundEnvironment 类包含了被注解所描述的 Field 的真实类型信息。
@SupportedAnnotationTypes({ "com.albon.arith.annotation.procecssor.AutoParseField" })@SupportedSourceVersion(SourceVersion.RELEASE_8)public class AutoParseFieldProcessor extends AbstractProcessor { private Filer filer; @Override public synchronized void init(ProcessingEnvironment processingEnv) { super.init(processingEnv); filer = processingEnv.getFiler(); } @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { Map<String, String> fieldTypeMap = Maps.newHashMap(); for (Element elem : roundEnv.getElementsAnnotatedWith(AutoParseField.class)) { AutoParseField annotation = elem.getAnnotation(AutoParseField.class); String message = System.currentTimeMillis() + " - annotation found in " + elem.getSimpleName() + " with key " + annotation.key() + " kind " + elem.getKind() + " class " + elem.getClass() + " asType " + elem.asType() + "\n\tgetEnclosingElement().getSimpleName() " + elem.getEnclosingElement().getSimpleName() + "\n\tgetEnclosingElement().asType() " + elem.getEnclosingElement().asType(); fieldTypeMap.put(elem.getEnclosingElement().asType().toString() + "#" + elem.getSimpleName(), elem.asType().toString()); processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, message); } if (fieldTypeMap.isEmpty()) { return true; } Writer writer = null; try { JavaFileObject jfo = filer .createSourceFile("com.albon.arith.annotation.service.AutoParseFieldInfo"); writer = jfo.openWriter(); writer.write("package com.albon.arith.annotation.service;\n" + "\n" + "import com.google.common.collect.Maps;\n" + "\n" + "import java.util.Map;\n" + "\n" + "public class AutoParseFieldInfo {\n" + "\n" + " // key: classpath#fieldName, value: fieldType\n" + " public static final Map<String, String> FIELD_TYPE_MAP = Maps.newHashMap();\n" + "\n" + " static {\n"); for (Map.Entry<String, String> entry : fieldTypeMap.entrySet()) { writer.write(" FIELD_TYPE_MAP.put(\"" + entry.getKey() + "\", \"" + entry.getValue() + "\");\n"); } writer.write(" }\n" + "\n" + " public static void main(String[] args) {\n" + " System.out.println(FIELD_TYPE_MAP);\n" + " }\n" + "}\n"); } catch (Exception e) { e.printStackTrace(); } finally { if (writer != null) { try { writer.close(); } catch (IOException e) { e.printStackTrace(); } } } return true; // no further processing of this annotation type }}
我们写了一个使用 @AutoParse 注解描述的 SimplePoJo 类:
public class SimplePoJo { @AutoParse(key = "first") private Integer first; @AutoParse(key = "second") private String second; @AutoParse(key = "third") private List<String> third;}
还需要在配置文件中指定使用该注解处理器,请在 resources 文件夹下新建 META-INF/services 文件夹,再新建文件 javax.annotation.processing.Processor,文件内容如下:
com.albon.arith.annotation.procecssor.AutoParseFieldProcessor
代码完成之后,使用 mvn clean package 进行编译,编译后可以看到经由注解处理器生成的 AutoParseFieldInfo.java 文件,其内容如下所示:
public class AutoParseFieldInfo { // key: classpath#fieldName, value: fieldType public static final Map<String, String> FIELD_TYPE_MAP = Maps.newHashMap(); static { FIELD_TYPE_MAP.put("com.albon.arith.annotation.service.SimplePoJo#third", "java.util.List<java.lang.String>"); FIELD_TYPE_MAP.put("com.albon.arith.annotation.service.SimplePoJo#second", "java.lang.String"); FIELD_TYPE_MAP.put("com.albon.arith.annotation.service.SimplePoJo#first", "java.lang.Integer"); } public static void main(String[] args) { System.out.println(FIELD_TYPE_MAP); }}
示例代码地址
完整的示例代码请看 GitHub: annotation-processor。
- Java 注解:注解处理器获取泛型真实类型
- Java注解(二):注解处理器
- Java获取集合泛型的类型上的注解
- java 注解:注解(Annotation)--注解处理器
- Java注解(2)-自定义注解、注解处理器
- Java注解处理器
- Java注解处理器
- Java 注解处理器
- Java注解处理器
- JAVA注解及处理器
- Java 注解处理器
- java获取泛型的真实类型
- JAVA 自定义注解(Annotation)-注解处理器
- Java注解(Annotation)-注解处理器小结
- Java注解与自定义注解处理器
- java注解(Annotation)--注解处理器
- Java注解处理器(编译时注解)
- Java注解和注解处理器使用详解
- 数据库中被问到的常见问题
- 数据结构基础--单链表逆序
- Redis基础使用
- h3c 构建中小企业网络 实验手册 第2章 笔记
- redis 发布订阅机制
- Java 注解:注解处理器获取泛型真实类型
- poj2631 Roads in the North(求树的直径裸题)
- 类中的集中特殊方法
- scala基础练习:实现日历练习
- sql语言语法
- OOP 相关知识的复习
- Javascript设计模式与开发实践(关于this/call/apply)
- 笨方法学python16习题 【读写文件】 最详解
- Volley的框架解读二(Http访问及处理)