java注解的jar包_Java 注解 Annotation

目录:

从 JDK5 开始,Java 增加了对元数据(MetaData)的支持,也就是 Annotation。Annotation 其实就是代码里面的特殊标记,这些标记可以在编译、类加载、运行时被读取,并执行相应的处理。通过使用注解,开发人员可以在不改变原有逻辑的情况下,在源文件中嵌入一些补充信息。代码分析工具、开发工具和部署工具可以通过这些补充信息进行验证或者进行部署。

1、基本 Annotation   

Java 提供的 5 个基本 Annotation 如下:

1)Override,2)Deprecated, 3)SuppressWarnings, 4)SafeVarargs, 5)FunctionalInterface

1.1、限定重写父类方法:@Override   

@Override 的用法举例:

public classAnimal {public voideat() {

System.out.println("Animal 的 eat() 方法");

}

}class Cat extendsAnimal {//使用 @Override 注解指定下面的 eat() 方法必须是重写父类方法

@Overridepublic voideat() {//super.eat();

System.out.println("Cat 的 eat() 方法");

}

}

@Override 的源码:

packagejava.lang;import java.lang.annotation.*;

@Target(ElementType.METHOD)

@Retention(RetentionPolicy.SOURCE)public @interface Override {}

@Target(ElementType.METHOD):表示 @Override 注解只能标注在方法上。

@Retention(RetentionPolicy.SOURCE):表示 @Override 注解只会保留在源代码中,编译器直接丢弃(class 文件中不保留)。

1.2、标记已过时:@Deprecated   

@Deprecated 用于表示某个程序元素(类、方法等)已过时,当程序使用已过时的类、方法时,编译器将会给出警告。

@Deprecated 使用举例:使用已过时的方法 eat(),IDE 会在该方法上加上中划线 “—” 的标识

b2859c39462414efecd6e0c9a9f55407.png

1.3、抑制编译器警告:@SuppressWarnings   

@SuppressWarnings 使用举例:

25f47da160d9209810b817bca3edf074.png

1db720e493cb40c80a43e3466a713cab.png

a2fd50ccc7a712444b67c6be4d2995b3.png

1.4、Java7 的“堆污染”警告和 @SafeVarargs   

下面的代码引发错误的原因成为“堆污染”,当把一个不带泛型的对象赋给一个带泛型的对象时,往往就会发生这种“堆污染”。

public static voidmain(String[] args) {

List list= new ArrayList();

list.add(10);//将 list 赋给 list2,编译运行都完全正常

List list2 =list;//但只要访问list2里面的元素,就会引起运行时异常

/*Exception in thread "main" java.lang.ClassCastException:

java.lang.Integer cannot be cast to java.lang.String

at com.oy.Animal.main(Animal.java:11)*/System.out.println(list2.get(0));

}

对于形参个数可变的方法,该形参又是泛型,将更容易导致“堆污染”。如下面的代码,listArray 是可变参数,相当于数组,但是Java不支持泛型数组,所以只能把 List... 当成 List[]来处理,这样就发生了“堆污染”,当在访问 listArray 的元素时引起运行时异常。

public classDemo {public static voidmain(String[] args) {

method(Arrays.asList("aaa"), Arrays.asList("bbb"));

}public static void method(List... listArray) {

List[] array=listArray;

array[0] = Arrays.asList(1);//java.lang.Integer cannot be cast to java.lang.String

System.out.println(listArray[0].get(0));

}

}

但有时候我们知道代码没有问题,我们不想看到这个警告,可以使用 @SafeVarargs 来压制这个警告

00af1afb640a8cc36ead89b24de413eb.png

1.5、Java8 新增的用于标识(限定)函数式接口:@FunctionalInterface   

如果接口中只有一个抽象方法(可以包含多个默认方法或多个 static 方法),该接口就是函数式接口。@FunctionalInterface 就是用来指定某个接口必须是函数式接口。

@FunctionalInterface 注解只是告诉编译器检查这个接口,保证这个接口只能由一个抽象方法,否则就会报编译出错。

42ab5905d5171189ed323cf438a09058.png

2、JDK 的元 Annotation   

JDK 提供了 5 个Meta Annotation(元注解),用于修饰其他的 Annotation 定义,即:

1) Retention, 2) Target, 3) Documented, 4) Inherited, 5) Repeatable。

2.1、注解的保留策略限定:@Retention   

@Retention 源码:

@Documented

@Retention(RetentionPolicy.RUNTIME)

@Target(ElementType.ANNOTATION_TYPE)public @interfaceRetention {

RetentionPolicy value();

}

@Retention 只能用于修饰 Annotation 定义,用于指定被修饰的 Annotation 可以保留多长时间,@Retention 包含一个 RetentionPolicy 类型的 value 成员变量,所以使用 @Retention 时必须为该 value 成员变量指定值。value 成员变量的值只能是如下三个:

RetentionPolicy.SOURCE: Annotation 只保留在源代码中,编译器直接丢弃这种注解。

RetentionPolicy.CLASS: 编译器将把 Annotation 记录在 class 文件中。当运行 Java 程序时,JVM 不可获取该 Annotation 信息。这是默认值。

RetentionPolicy.RUNTIME: 编译器将把 Annotation 记录在 class 文件中。当运行 Java 程序时,JVM 也可获取该 Annotation 信息,程序可以通过反射获取该 Annotation 信息。

定义一个 “编译时注解”:

//RetentionPolicy.SOURCE: 表示此注解只保留在源代码中,编译器直接丢弃这种注解//通常称这种注解为 “编译时注解”

@Retention(RetentionPolicy.SOURCE)public @interfaceMyTag {

}

定义一个 “运行时注解”:

//RetentionPolicy.SOURCE: 编译器将把 Annotation 记录在 class 文件中。当运行 Java 程序时,JVM 也可获取该 Annotation 信息,//程序可以通过反射获取该 Annotation 信息。//通常称这种注解为 “运行时注解”

@Retention(RetentionPolicy.RUNTIME)public @interfaceMyTag {

}

2.2、注解的作用目标限定:@Target   

@Target 源码:

@Documented

@Retention(RetentionPolicy.RUNTIME)

@Target(ElementType.ANNOTATION_TYPE)public @interfaceTarget {

ElementType[] value();

}

@Target 包含一个 ElementType[] 类型的 value 成员变量,该成员变量的值只能为如下:

public enumElementType {/**Class, interface (including annotation type), or enum declaration*/TYPE,/**Field declaration (includes enum constants)*/FIELD,/**Method declaration*/METHOD,/**Formal parameter declaration*/PARAMETER,/**Constructor declaration*/CONSTRUCTOR,/**Local variable declaration*/LOCAL_VARIABLE,/**Annotation type declaration*/ANNOTATION_TYPE,/**Package declaration*/PACKAGE,/*** Type parameter declaration

*

*@since1.8*/TYPE_PARAMETER,/*** Use of a type

*

*@since1.8*/TYPE_USE

}

ANNOTATION_TYPE:限定该注解只能用于修饰 Annotation。比如 @Target 定义时就使用的是这个限定。

2.3、@Documented   

@Documented 是一个标记注解,没有成员。用于指定此注解是否被javadoc 工具提取成文档。如果定义注解时加上了 @Documented,则使用该注解修饰的程序元素的 API 文档中将会包含该注解说明。

@Documentedpublic @interfaceMyTag {

}

2.4、@Inherited   

@Inherited 用于指定被它修改的 Annotation 将具有继承性---如果某个类使用了@Xxx 注解(定义该注解时使用了 @Inherited 修饰),则其子类将自动被 @Xxx 修饰。

@Inherited

@Retention(RetentionPolicy.RUNTIME)public @interfaceMyTag {

}

@MyTagclassBase {

}//这个类继承了 Base类//并未直接使用 @MyTag 修饰,

public class TestBase extendsBase {public static voidmain(String[] args) {//isAnnotationPresent: 判断此程序元素是否被某个注解修饰//注意:@MyTag 定义时指定保留策略为 RUNTIME

System.out.println(TestBase.class.isAnnotationPresent(MyTag.class)); //true

}

}

2.5、Java8 新增的重复注解:@Repeatable   

某些情况下,一个程序元素需要使用多个同一种类型的注解。@Repeatable元注解,顾名思义,重复注解,就是在声明创建注解的时候,指定该注解可以被同一个程序元素多次使用。

使用 @Repeated 举例:

定义一个注解:

packagecom.oy;importjava.lang.annotation.ElementType;importjava.lang.annotation.Repeatable;importjava.lang.annotation.Retention;importjava.lang.annotation.RetentionPolicy;importjava.lang.annotation.Target;

@Repeatable(Roles.class)

@Retention(RetentionPolicy.RUNTIME)

@Target(ElementType.TYPE)public @interfaceRole {//为该注解定义了一个成员变量

String value();

}

定义一个 “容器” 注解

packagecom.oy;importjava.lang.annotation.ElementType;importjava.lang.annotation.Retention;importjava.lang.annotation.RetentionPolicy;importjava.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)

@Target(ElementType.TYPE)public @interfaceRoles {

Role[] value();

}

测试

packagecom.oy;importjava.lang.annotation.Annotation;

// 重复使用 @Role 注解

@Role("admin")

@Role("user")public classDemo {public static voidmain(String[] args) {

Annotation[] annotations= Demo.class.getAnnotations();for(Annotation a : annotations) {//@com.oy.Roles(value=[@com.oy.Role(value=admin), @com.oy.Role(value=user)])

System.out.println(a);

}//getDeclaredAnnotationsByType: 可以获取多个重复注解

Role[] array = Demo.class.getDeclaredAnnotationsByType(Role.class);for(Role r: array) {//@com.oy.Role(value=admin)//@com.oy.Role(value=user)

System.out.println(r);

System.out.println(r.value());

}

}

}

3、自定义注解   

3.1、定义 Annotation   

定义注解时,定义了一个成员变量 name, 并且指定了默认值

importjava.lang.annotation.Documented;importjava.lang.annotation.ElementType;importjava.lang.annotation.Retention;importjava.lang.annotation.RetentionPolicy;importjava.lang.annotation.Target;

@Documented

@Retention(RetentionPolicy.RUNTIME)

@Target({ ElementType.TYPE, ElementType.METHOD })public @interfaceMyTag {

String name()default "hello";

}

根据 Annotation 是否包含成员变量,将 Annotation 分为如下两类:

1)标记 Annotation:没有定义成员变量。这种 Annotation 仅利用自身的存在与否来提供信息,如前面介绍的 @Override等。

2)元数据 Annotation:包含成员变量。

3.2、提取 Annotation 信息   

使用 Annotation 修饰了类、方法、成员变量等成员之后,这些 Annotation 不会自己生效,必须由开发者提供相应的工具来提取并处理 Annotation 信息。

Java 使用 Annotation 接口来代表程序元素前面的注解,该接口是所有注解的父接口。Java5 在java.lang.reflect 包下新增了 AnnotatedElement 接口,该接口代码程序中可以接受注解的程序元素。该接口主要有如下几个实现类:

1)Class: 类定义;

2)Constructor: 构造器定义;

3)Field:类的成员变量定义;

4)Method:类的方法定义;

5)Package:类的包定义;

java.lang.reflect 包下主要包含一些实现反射功能的工具类,从 Java5 开始,java.lang.reflect 包所提供的反射 API 增加了读取运行时 Annotation 的能力。只有当定义 Annotation 时使用了 @Retention(RerentionPolicy.RUNTIME) 修饰,该 Annotation 才会在运行时可见,JVM 才会在加载 class 字节码文件时读取保存在 class 文件中的 Annotation 信息。

反射获取注解信息的 API:

1) A getAnnotation(Class annotationClass): 返回该程序元素上存在的指定类型的注解,如果指定的注解不存在,返回null;

2) A getDeclaredAnnotation(Class annotationClass): 这是 Java8 新增的方法,该方法尝试获取直接修饰该程序元素的指定类型的注解,如果指定的注解不存在,返回null;

3)Annotation[] getAnnotations(): 返回该程序元素上存在的所有注解;

4)Annotation[] getDeclaredAnnotations(): 返回直接修饰该程序元素的所有注解;

5)boolean isAnnotationPresent(Class extends Annotation) annotationClass): 判断该程序元素上是否存在指定类型的注解,存在返回 true,否则返回 false;

6) A[] getAnnotationsByType(class annotationClass): 与 getAnnotation() 功能类似,但是可以获取指定类型的重复注解 ;

7) A[] getDeclaredAnnotationsByType(class annotationClass): 与 getDeclaredAnnotation() 类似,但是可以获取直接修复该程序元素的指定类型的重复注解 ;

4、使用 “运行时注解“ 的例子   

0594ea8226556cd162fa8a2141525e05.png

自定义注解 @Test

packagecom.oy;importjava.lang.annotation.ElementType;importjava.lang.annotation.Retention;importjava.lang.annotation.RetentionPolicy;importjava.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)

@Target(ElementType.METHOD)public @interfaceTest {booleanvalue();

}

测试使用该注解:

packagecom.oy;importjava.lang.reflect.Method;public classDemo {public static voidmain(String[] args) {for (Method method : Demo.class.getMethods()) {//方法上没有标注 @Test 注解,不处理

if (!method.isAnnotationPresent(Test.class)) continue;//@Test(false) 不处理

Test test = method.getAnnotation(Test.class);if (!test.value()) continue;//执行 @Test(true) 标注的方法

try{

method.invoke(null);

}catch(Exception e) {

e.printStackTrace();

}

}

}

@Test(true)public static voidtest1() {

System.out.println("test1...");

}

@Test(false)public static voidtest2() {

System.out.println("test2...");

}public static voidtest3() {

System.out.println("test3...");

}

}

更多使用 “运行时注解“ 的例子可以参考博客:

5、“编译时注解”   

RetentionPolicy.SOURCE: Annotation 只保留在源代码中,编译器直接丢弃这种注解。这种注解通常称为 “编译时注解”,编译器在对源码进行编译时读取注解信息,做一些额外的处理。

APT:Annotation Processing Tool, 是一种注解处理工具,它对源代码进行检查,并找出源文件所包含的注解信息,然后针对注解信息进行额外的处理。使用 APT 的主要目的是简化开发者的工作量,因为 APT 可以在编译程序源代码的同时生成一些附加文件(比如源文件、类文件、程序发布描述文件等),这些附属文件的内容也都与源代码有关。换句话说,使用 APT 可以替代传统的对代码信息和附属文件的维护工作。

Java 提供的 javac.exe 工具有一个 -processor 选项,该选项可以指定一个 Annotation 处理器,如果在编译源文件时通过该选项指定了 Annotation 处理器,那么这个 Annotation 处理器将会在编译时提取并处理 Java 源文件种的 Annotation信息。

每个 Annotation 处理器都需要实现 javax.annotation.processing 包下的 Processor 接口。不过实现该接口要实现它里面所有的方法,因此通常会采用继承 AbstractProcessor 的方式来编写 Annotation 处理器。一个 Annotation 处理器可以处理一个或多个 Annotation 类型。一个继承 AbstractProcessor 的举例如下:

importjava.util.LinkedHashSet;importjava.util.Set;importjavax.annotation.processing.AbstractProcessor;importjavax.annotation.processing.ProcessingEnvironment;importjavax.annotation.processing.RoundEnvironment;importjavax.annotation.processing.SupportedAnnotationTypes;importjavax.annotation.processing.SupportedSourceVersion;importjavax.lang.model.SourceVersion;importjavax.lang.model.element.TypeElement;public class MyProcessor extendsAbstractProcessor {

@Overridepublic boolean process(Set extends TypeElement>annoations,

RoundEnvironment env) {return false;

}

@Overridepublic SetgetSupportedAnnotationTypes() {

Set annotataions = new LinkedHashSet();

annotataions.add("com.example.MyAnnotation");returnannotataions;

}

@OverridepublicSourceVersion getSupportedSourceVersion() {returnSourceVersion.latestSupported();

}

@Overridepublic synchronized voidinit(ProcessingEnvironment processingEnv) {super.init(processingEnv);

}

}

其中,init(ProcessingEnvironment processingEnv):所有的注解处理器类都必须有一个无参构造函数。然而,有一个特殊的方法init(),它会被注解处理工具调用,以ProcessingEnvironment作为参数。ProcessingEnvironment 提供了一些实用的工具类Elements, Types和Filer。

process(Set annoations, RoundEnvironment env):这类似于每个处理器的main()方法。你可以在这个方法里面编码实现扫描,处理注解,生成 java 文件。使用RoundEnvironment 参数,你可以查询被特定注解标注的元素。

getSupportedAnnotationTypes():在这个方法里面你必须指定哪些注解应该被注解处理器注册。注意,它的返回值是一个String集合,包含了你的注解处理器想要处理的注解类型的全称。换句话说,你在这里定义你的注解处理器要处理哪些注解。

getSupportedSourceVersion():用来指定你使用的 java 版本。通常你应该返回SourceVersion.latestSupported()。不过,如果你有足够的理由坚持用 java 6 的话,你也可以返回SourceVersion.RELEASE_6。

关于getSupportedAnnotationTypes()和getSupportedSourceVersion()这两个方法,你也可以使用相应注解进行代替。代码如下:

@SupportedSourceVersion(SourceVersion.RELEASE_8)

@SupportedAnnotationTypes("com.example.MyAnnotation")public class MyProcessor extendsAbstractProcessor {

....

5.1、"编译时注解" 的简单使用:编译时打印字段字符串   

分别编译 DataTest 和 MyProcessor, 然后使用 "javac -process 注解处理器 类名.java" 命令编译使用 @DataTest 注解的类

6ea299216e0673f24a70e99cfd8e745d.png

DataTest 注解的定义

importjava.lang.annotation.Retention;importjava.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.SOURCE)public @interfaceDataTest {

}

注解处理器 MyProcessor

importjava.util.Set;importjavax.annotation.processing.AbstractProcessor;importjavax.annotation.processing.ProcessingEnvironment;importjavax.annotation.processing.Processor;importjavax.annotation.processing.RoundEnvironment;importjavax.annotation.processing.SupportedAnnotationTypes;importjavax.annotation.processing.SupportedSourceVersion;importjavax.lang.model.SourceVersion;importjavax.lang.model.element.TypeElement;importjavax.tools.Diagnostic;

@SupportedAnnotationTypes("DataTest")

@SupportedSourceVersion(SourceVersion.RELEASE_8)public class MyProcessor extendsAbstractProcessor {privateProcessingEnvironment processingEnv;

@Overridepublic synchronized voidinit(ProcessingEnvironment processingEnv) {this.processingEnv =processingEnv;

}

@Overridepublic boolean process(Set extends TypeElement>annotations, RoundEnvironment roundEnv) {

processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE,"MyProcessor process()...");return false;

}

}

测试类 TestDemo

@DataTestpublic classTestDemo {public static voidmain(String[] args) {

System.out.println("main run...");

}

}

5.2、将注解和注解处理器打成工具 jar 包使用   

上面的例子简单演示了 “javac -processor 注解处理器 类名.java” 命令的使用。开发中可能需要将注解和注解处理器打成工具 jar 包使用。

步骤1:新建一个 maven 工程:

c393ec54a39d01e3eede4a60dd665541.png

pom.xml 文件

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

4.0.0

com.oy

MyAnnotationUtils

0.0.1-SNAPSHOT

com.squareup

javawriter

2.5.1

com.google.auto.service

auto-service

1.0-rc5

org.apache.maven.plugins

maven-compiler-plugin

1.8

1.8

View Code

DataTest

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

packagecom.oy.annotation;importjava.lang.annotation.Retention;importjava.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.SOURCE)public @interfaceDataTest {

}

View Code

MyProcessor

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

packagecom.oy.annotation;importjava.util.Set;importjavax.annotation.processing.AbstractProcessor;importjavax.annotation.processing.ProcessingEnvironment;importjavax.annotation.processing.Processor;importjavax.annotation.processing.RoundEnvironment;importjavax.annotation.processing.SupportedAnnotationTypes;importjavax.annotation.processing.SupportedSourceVersion;importjavax.lang.model.SourceVersion;importjavax.lang.model.element.TypeElement;importjavax.tools.Diagnostic;importcom.google.auto.service.AutoService;

@SupportedAnnotationTypes("com.oy.annotation.DataTest")

@SupportedSourceVersion(SourceVersion.RELEASE_8)

@AutoService(Processor.class)public class MyProcessor extendsAbstractProcessor {privateProcessingEnvironment processingEnv;

@Overridepublic synchronized voidinit(ProcessingEnvironment processingEnv) {this.processingEnv =processingEnv;

}

@Overridepublic boolean process(Set extends TypeElement>annotations, RoundEnvironment roundEnv) {

processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE,"MyProcessor process...");return false;

}

}

View Code

步骤2:选择项目,右键 ==> Run as => Maven build... => clean package, 会在项目根目录的 target 目录下生成 MyAnnotationUtils.jar, 从而可以在其他项目中使用。

2294c36a508171a766e8b1176221c5c8.png

步骤三:新建一个目录,将MyAnnotaionUtils.jar 复制到这个目录

b148c0c5944a1fdb7820c3d7de16aa8d.png

解压这个jar会发现,在 META-INF/services 下面会有个文件 javax.annotation.processing.Processor,是 @AutoService 注解帮助生成的。

2d60f66e6bd3571b8e6e18354d549c90.png

步骤四:编写测试类 TestDemo

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

importcom.oy.annotation.DataTest;

@DataTestpublic classTestDemo {public static voidmain(String[] args) {

System.out.println("main run...");

}

}

View Code

步骤五:执行 "java -cp MyAnnotationUtils.jar TestDemo.java" 命令

09444f76672015778501996f41dba92b.png

5.3、使用 ”编译时注解“ 的例子:模拟 lombok 注解 @Data   

47afa317a7fdca2dc2c11a02ec7063c0.png

pom.xml与上面的例子一样,都引入了依赖'com.squareup:javawriter:2.5.1' 和 ‘com.google.auto.service:auto-service:1.0-rc5’

Data

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

packagecom.oy.annotation;importjava.lang.annotation.ElementType;importjava.lang.annotation.Retention;importjava.lang.annotation.RetentionPolicy;importjava.lang.annotation.Target;

@Retention(RetentionPolicy.SOURCE)

@Target(ElementType.TYPE)public @interfaceData {

}

View Code

DataAnnotationProcessor

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

packagecom.oy.annotation;importjava.io.IOException;importjava.io.Writer;importjava.util.EnumSet;importjava.util.HashMap;importjava.util.List;importjava.util.Map;importjava.util.Set;importjavax.annotation.processing.AbstractProcessor;importjavax.annotation.processing.Filer;importjavax.annotation.processing.Messager;importjavax.annotation.processing.ProcessingEnvironment;importjavax.annotation.processing.Processor;importjavax.annotation.processing.RoundEnvironment;importjavax.annotation.processing.SupportedAnnotationTypes;importjavax.annotation.processing.SupportedSourceVersion;importjavax.lang.model.SourceVersion;importjavax.lang.model.element.Element;importjavax.lang.model.element.ElementKind;importjavax.lang.model.element.Modifier;importjavax.lang.model.element.Name;importjavax.lang.model.element.TypeElement;importjavax.lang.model.type.TypeMirror;importjavax.lang.model.util.Elements;importjavax.tools.Diagnostic;importjavax.tools.JavaCompiler;importjavax.tools.JavaFileObject;importjavax.tools.StandardJavaFileManager;importjavax.tools.ToolProvider;importcom.google.auto.service.AutoService;importcom.squareup.javawriter.JavaWriter;

@SupportedSourceVersion(SourceVersion.RELEASE_8)

@SupportedAnnotationTypes({"com.oy.annotation.Data"})

@AutoService(Processor.class)public class DataAnnotationProcessor extendsAbstractProcessor {private Messager messager; //用于打印日志

private Elements elementUtils; //用于处理元素

private Filer filer; //用来创建java文件或者class文件

@Overridepublic synchronized voidinit(ProcessingEnvironment processingEnv) {super.init(processingEnv);

messager=processingEnv.getMessager();

elementUtils=processingEnv.getElementUtils();

filer=processingEnv.getFiler();

}//@Override//public SourceVersion getSupportedSourceVersion() {//return SourceVersion.latestSupported();//}//

//@Override//public Set getSupportedAnnotationTypes() {//Set set = new HashSet<>();//set.add(Data.class.getCanonicalName());//return Collections.unmodifiableSet(set);//}

@Overridepublic boolean process(Set extends TypeElement>annotations, RoundEnvironment roundEnv) {

messager.printMessage(Diagnostic.Kind.NOTE,"-----开始自动生成源代码");try{//标识符

boolean isClass = false;//类的全限定名

String classAllName = null;//返回被注释的节点

Set extends Element> elements = roundEnv.getElementsAnnotatedWith(Data.class);

Element element= null;for(Element e : elements) {//如果注释在类上

if (e.getKind() == ElementKind.CLASS && e instanceofTypeElement) {

TypeElement t=(TypeElement) e;

isClass= true;

classAllName=t.getQualifiedName().toString();

element=t;break;

}

}//未在类上使用注释则直接返回,返回false停止编译

if (!isClass) {return true;

}//返回类内的所有节点

List extends Element> enclosedElements =element.getEnclosedElements();//保存字段的集合

Map fieldMap = new HashMap<>();for(Element ele : enclosedElements) {if (ele.getKind() ==ElementKind.FIELD) {//字段的类型

TypeMirror typeMirror =ele.asType();//字段的名称

Name simpleName =ele.getSimpleName();

fieldMap.put(typeMirror, simpleName);

}

}//生成一个Java源文件(文件名为 类名.java)

JavaFileObject sourceFile =filer.createSourceFile(getClassName(classAllName));//写入代码

createSourceFile(classAllName, fieldMap, sourceFile.openWriter());//手动编译

compile(sourceFile.toUri().getPath());

}catch(IOException e) {

messager.printMessage(Diagnostic.Kind.ERROR, e.getMessage());

}

messager.printMessage(Diagnostic.Kind.NOTE,"-----完成自动生成源代码");return true;

}private void createSourceFile(String classAllName, MapfieldMap, Writer writer)throwsIOException {//生成源代码

JavaWriter jw = newJavaWriter(writer);

jw.emitPackage(getPackage(classAllName));

jw.beginType(getClassName(classAllName),"class", EnumSet.of(Modifier.PUBLIC));for (Map.Entrymap : fieldMap.entrySet()) {

String type=map.getKey().toString();

String name=map.getValue().toString();//字段

jw.emitField(type, name, EnumSet.of(Modifier.PRIVATE));

}for (Map.Entrymap : fieldMap.entrySet()) {

String type=map.getKey().toString();

String name=map.getValue().toString();//getter

jw.beginMethod(type, "get" + humpString(name), EnumSet.of(Modifier.PUBLIC)).emitStatement("return " +name)

.endMethod();//setter

jw.beginMethod("void", "set" + humpString(name), EnumSet.of(Modifier.PUBLIC), type, "arg")

.emitStatement("this." + name + " = arg").endMethod();

}

jw.endType().close();

}/*** 编译文件

*

*@parampath

*@throwsIOException*/

private void compile(String path) throwsIOException {//拿到编译器

JavaCompiler complier =ToolProvider.getSystemJavaCompiler();//文件管理者

StandardJavaFileManager fileMgr = complier.getStandardFileManager(null, null, null);//获取文件

Iterable units =fileMgr.getJavaFileObjects(path);//编译任务

JavaCompiler.CompilationTask t = complier.getTask(null, fileMgr, null, null, null, units);//进行编译

t.call();

fileMgr.close();

}/*** 驼峰命名

*

*@paramname

*@return

*/

privateString humpString(String name) {

String result=name;if (name.length() == 1) {

result=name.toUpperCase();

}if (name.length() > 1) {

result= name.substring(0, 1).toUpperCase() + name.substring(1);

}returnresult;

}/*** 从带包类名读取类名

*

*@paramclassAllName 带包类名

*@return

*/

privateString getClassName(String classAllName) {

String result=classAllName;if (classAllName.contains(".")) {

result= classAllName.substring(classAllName.lastIndexOf(".") + 1);

}returnresult;

}/*** 从带包类名读取包名

*

*@paramclassAllName 带包类名

*@return

*/

privateString getPackage(String classAllName) {

String result=classAllName;if (classAllName.contains(".")) {

result= classAllName.substring(0, classAllName.lastIndexOf("."));

}else{

result= "";

}returnresult;

}

}

View Code

测试使用:

a1b09f51224fdc3e9965fe910c995fc8.png

5.4、eclipse 创建 maven 项目,使用自己写的 Annotation Processor   

首先将自己写的 Annotation Processor(maven 项目)打包到本地仓库:

456cbd8a73a3f6aad65a40f45a1b72f6.png

新建一个 mavenDemo 项目,在 pom.xml 添加对 自己写的 Annotation Processor 的依赖:

com.oy

MyAnnotationUtils

0.0.1-SNAPSHOT

d1610cab4d86c4b33f3187f2367b2731.png

然后,选中 MavenDemo 项目,右键-> run as -> maven build... -> clean compile -> 在 target 目录下生成如下文件:

e254c981001a373caab4716c4dd9df2b.png

这个例子会报 com.oy.Demo 类重复的错,因为原来的Demo 类没删,所以重复了。

参考:

1)《疯狂 Java 讲义》--李刚

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值