一、注解简介:

1.1.什么是“注解”:

​ 在我们编写代码时,一定看到过这样的代码:

class Student {
private String name; @Override
public String toString(String str) {//编译错误!
return "Student name = " + name;
}
}

​ 其中的@Override,就是一个“注解”,@Override一般出现在重写equals()或者toString()方法的上边,意思是告诉编译器:下边的代码是重写父类方法的。这时编译器会按照“重写”的语法严格检查下面的方法,如果不符合重写语法,将会编译错误。

​ "注解"作为一种“标记”,被写在源码中,不会改变程序的执行流程。它通常由“注解解析工具”来解析,而“注解解析器”可以随Java编译器启动,也可以独立启动,来解析注解,并以此可以做一些事情。

1.2.注解的分类:

源码注解:

​ 注解只在源码中,编译成class文件后就不存在了。

编译时注解:

​ 注解在源码和.class文件中都存在(如:JDK内置系统注解)

运行时注解:

​ 在运行阶段还起作用,甚至会影响运行逻辑的注解(如:JUnit的@Test)

1.3.注解的作用

​ 注解的作用非常广泛,注解可以被用在类、属性、构造方法、成员方法、局部变量等位置,用于对这些元素进行说明。由“注解解析工具”解析后,可以生成文档、进行代码分析、编译检查等。

​ 本例将会实现一个用作"编译检查“的注解,以及一个"注解解析器"。"注解解析器"将会随着javac编译器一同启动来对使用了注解的类进行编译,并检查类名、字段名、方法名是否以大写、小写字符开头,如果违反了规则,编译时将会报错。

二、自定义注解:

2.1.定义注解的基本语法

​ “注解”本质上是一个“类”,我们可以根据自己的需要定义自己的注解。

​ 定义注解的语法很简单:

public @interface CheckWord{
...
}

​ "注解”编译后会生成.class文件。但这是一个非常简单的注解,它可以被用在任何位置,而且编译器遇到这种注解也不做任何事情。例如:

@CheckWord
public class Student {
@CheckWord
public Student() {
} @CheckWord
private String name; @CheckWord
public void study() {
}
}

下面我们先使用“元注解”来规定这个注解可以被用在哪里。

2.2.元注解

​ “元注解”也是一种“注解”,它是已经实现好的。必须用在“注解”的定义上,它可以规定注解可以用在哪里,以及可以存在于源码中,或者class中,或者运行时。

常用的“元注解”有两个:

​ 1).@Target : 规定注解可以用在哪里。常用的取值被定义在枚举java.lang.annotation.ElementType中:

​ ElementType.TYPE:类和接口上

​ ElementType.FIELD: 用在成员变量上

​ ElementType.METHOD: 用在方法上

​ ElementType.PARAMETER: 用在参数上

​ ElementType.CONSTRUCTOR: 用在构造方法上

​ ElementType.LOCAL_VARIABLE: 用在局部变量上

​ 2).@Retention : 规定注解可以存在于哪里。常用的取值被定义在枚举java.lang.annotation.RetentionPolicy中:

​ RetentionPolicy.SOURCE: 规定注解只存在于Java源代码中, 编译生成的字节码文件中就不存在了。

​ RetentionPolicy.CLASS: 规定注解存在于Java源代码、 编译以后的字节码文件中, 但JVM运行时,不会被加载到内存。

​ RetentionPolicy.RUNTIME: 规定注解存在于Java源代码中、 编译以后的字节码文件中、 运行时内存中, 程序可以通过反射获取该注解。

​ 例如:修改我们的注解,规定它只能用在"类","字段",“方法”上,并且可以存在于“源码中”:

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; @Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.SOURCE)
public @interface CheckWord { }

​ 如果再编译之前的Student类,会发现用在"构造方法"上的@CheckWord会编译错误,因为我们规定了它只能用在"类","字段","方法"上。

2.3.定义注解的属性:

1.“注解”中可以定义一些属性,“注解解析器”可以根据“属性”的不同,分别做不同的事情。

​ 例如@Target注解中的ElementType.TYPE就是此注解的一个属性,它是一个"枚举"类型。

​ 下面让我们来看看怎样定义属性,然后再解析这些属性。

​ 注解中定义属性的语法:数据类型 属性名() [deafult 值];

​ 1.其中“数据类型”可以是:

​ 1).所有基本类型;

​ 2).String;

​ 3).Class;

​ 4).枚举;

​ 5).注解;

​ 6).以上任一类型的数组

​ 2.属性名():属性名可以自由设定,要遵循Java标识符的命名规则;其中的一对()是必须的。

​ 3.[default 值]:为此属性设置的默认值。

2.本例中由于只检查大小写,为了规范取值,所以定义一个"枚举"类型的属性。

​ 1).先定义枚举:

public enum StartsWith {
UPPERCASE, LOWERCASE
}

​ 此枚举定义了两个值:UPPERCASE表示:大写;LOWERCASE表示:小写。

​ 2).修改"CheckWord"注解的代码:

@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.SOURCE)
public @interface CheckWord {
StartsWith value();
}

​ 说明:

​ a.StartsWith表示"数据类型",是一个"枚举"类型。

​ b.value表示"属性名",在使用此注解时,此属性的可取值只有StartsWith.UPPERCASE和StartsWith.LOWERCASE两个。

​ c.此属性没有设置"默认值",在使用此注解时必须要设置此属性的值。如下面的代码:

​ 3).修改"Student"类的代码:

@CheckWord(StartsWith.UPPERCASE)
public class Student {
@CheckWord(StartsWith.LOWERCASE)
private String stuName;
@CheckWord(StartsWith.LOWERCASE)
public void show() {
}
}

2.4注解解析器:

​ 1."注解解析器"通常是随着注解一起定义的,用于解析"注解",并做一些事情。本例的"注解解析器"用于与javac编译器一起启动,编译Student类时,检查各元素的名称是否按要求以指定的大写、小写字母开头。

​ 2.自定义"注解解析器"需要继承AbstractProcessor类,并重写process()方法,完整的"注解解析器"代码如下:

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;
import java.util.Set; @SupportedAnnotationTypes("CheckWord")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class MyProcessor extends AbstractProcessor { @Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
//获取所有使用了@CheckWord注解的元素
Set<? extends Element> annoEle = roundEnv.getElementsAnnotatedWith(CheckWord.class);
// 遍历这些元素
for (Element e : annoEle) {
//获取元素名称,可能是:类名、属性名、方法名
String name = e.getSimpleName().toString();
//获取这个名字的第一个字母
char c = name.charAt(0);
//获取这个元素上的@CheckWord注解对象
CheckWord anno = e.getAnnotation(CheckWord.class);
//获取这个注解的value属性的值,它是一个StartsWith枚举类型
StartsWith sw = anno.value();
//判断属性值是否设置为:StartsWith.UPPERCASE,但名字的首字母是小写
if (sw == StartsWith.UPPERCASE && Character.isLowerCase(c)) {
//向控制台打印异常信息
this.processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "名称:" + name + " 首字母应该大写!");
return false;
}
//判断属性值是否设置为:StartsWith.LOWERCASE,但名字的首字母是大写
if (sw == StartsWith.LOWERCASE && Character.isUpperCase(c)) {
this.processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "名称:" + name + " 首字母应该小写!");
return false;
} }
return true;
}
}

​ 此代码的细节大家可以根据注释一点一点研究。一些类:TypeElement,RoundEnvironment,Element等的一些方法大家可以在API手册中查找。

​ 其它说明:

​ @SupportedAnnotationTypes("CheckWord") : 表示只处理CheckWord注解。

​ @SupportedSourceVersion(SourceVersion.RELEASE_8) : 表示支持JDK1.8。

2.5.编译和测试:

1.在编译前,我们看一下完整的代码清单:请确保以下的四个类在同一个目录下

​ 1).枚举类:

public enum StartsWith {
UPPERCASE, LOWERCASE
}

​ 2).自定义注解类:

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; @Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.SOURCE)
public @interface CheckWord {
StartsWith value();
}

​ 3).使用了CheckWord注解的Student类:

@CheckWord(StartsWith.UPPERCASE)
public class Student {
@CheckWord(StartsWith.LOWERCASE)
private String StuName; @CheckWord(StartsWith.LOWERCASE)
public void show() { }
}

​ 4).注解解析器类:

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;
import java.util.Set; @SupportedAnnotationTypes("CheckWord")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class MyProcessor extends AbstractProcessor {
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
Set<? extends Element> annoEle = roundEnv.getElementsAnnotatedWith(CheckWord.class);
for (Element e : annoEle) {
String name = e.getSimpleName().toString();
char c = name.charAt(0); CheckWord anno = e.getAnnotation(CheckWord.class);
StartsWith sw = anno.value();
if (sw == StartsWith.UPPERCASE) {
if (Character.isLowerCase(c)) {
this.processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "名称:" + name + " 首字母应该大写!");
return false;
}
}
if (sw == StartsWith.LOWERCASE) {
if (Character.isUpperCase(c)) {
this.processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "名称:" + name + " 首字母应该小写!");
return false;
}
}
}
return true;
}
}

2.启动命令行,使用javac依次进行编译:

​ javac StartsWith.java

​ javac CheckWord.java

​ javac MyProcessor.java(如果报错: 编码GBK的不可映射字符,是因为代码中的中文,可以使用javac -encoding UTF-8 MyProcessor.java进行编译)

​ 接下来使用MyProcessor解析器编译Student:

​ javac -processor MyProcessor Student.java

​ 执行命令后,会有错误提示:

​ 错误: 名称:StuName 首字母应该小写!

​ 1 个错误

三、总结:

​ 源码级注解的应用非常广泛,例如:进行代码检查、生成新类、生成文件。本文实现了基本的代码检查,用于检查类中的元素是否按照要求进行首字母大写或者小写。也可以根据需要,验证是否全部大写,或者全部小写。希望大家通过本案例能够了解源码级注解的编写及使用。

关于JAVA中源码级注解的编写及使用的更多相关文章

  1. 浅谈java中源码常见的几个关键字(native,strictfp,transient,volatile)

    最近看源码总发现一些没见过的关键字,今天就来整理一下native,strictfp,transient,volatile native 本地 native是与C++联合开发的时候用的!java自己开发 ...

  2. 【Java编程实战】Metasploit_Java后门运行原理分析以及实现源码级免杀与JRE精简化

    QQ:3496925334 文章作者:MG1937 CNBLOG博客ID:ALDYS4 未经许可,禁止转载 某日午睡,迷迷糊糊梦到Metasploit里有个Java平台的远控载荷,梦醒后,打开虚拟机, ...

  3. MapReduce的ReduceTask任务的运行源码级分析

    MapReduce的MapTask任务的运行源码级分析 这篇文章好不容易恢复了...谢天谢地...这篇文章讲了MapTask的执行流程.咱们这一节讲解ReduceTask的执行流程.ReduceTas ...

  4. TaskTracker任务初始化及启动task源码级分析

    在监听器初始化Job.JobTracker相应TaskTracker心跳.调度器分配task源码级分析中我们分析的Tasktracker发送心跳的机制,这一节我们分析TaskTracker接受JobT ...

  5. 【转】Java HashMap 源码解析(好文章)

    ­ .fluid-width-video-wrapper { width: 100%; position: relative; padding: 0; } .fluid-width-video-wra ...

  6. 源码级强力分析hadoop的RPC机制

    分析对象: hadoop版本:hadoop 0.20.203.0 必备技术点: 1. 动态代理(参考 :http://weixiaolu.iteye.com/blog/1477774 )2. Java ...

  7. Java集合源码分析(四)Vector<E>

    Vector<E>简介 Vector也是基于数组实现的,是一个动态数组,其容量能自动增长. Vector是JDK1.0引入了,它的很多实现方法都加入了同步语句,因此是线程安全的(其实也只是 ...

  8. Java集合源码分析(三)LinkedList

    LinkedList简介 LinkedList是基于双向循环链表(从源码中可以很容易看出)实现的,除了可以当做链表来操作外,它还可以当做栈.队列和双端队列来使用. LinkedList同样是非线程安全 ...

  9. Java集合源码分析(二)ArrayList

    ArrayList简介 ArrayList是基于数组实现的,是一个动态数组,其容量能自动增长,类似于C语言中的动态申请内存,动态增长内存. ArrayList不是线程安全的,只能用在单线程环境下,多线 ...

随机推荐

  1. schedule of 2016-10-17~2016-10-23(Monday~Sunday)——1st semester of 2nd Grade

    most important things to do 1.joint phd preparations 2.journal paper to write 3.solid fundamental kn ...

  2. MySQL 基础 SQL 操作

    MySQL 用户 --登录 mysql -u<用户名> -p[密码] --修改密码 mysqladmin -u<用户名> -p[密码] password <new_pas ...

  3. 【转】ArcGIS Server 10.1 动态图层

    ArcGISServer将GIS资源以服务的方式发布,能够让更多的人在Web上浏览.使用.不过,诸如气象.环保等方面的信息是实时变化的,按照之前常规的方法,我们先要将最新获得的信息组织成地图文档后再对 ...

  4. SpringBoot项目配置Date类型数据自动格式转换

    application.yml加入配置 spring: jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT+8

  5. hdu - 4990

    Read the program below carefully then answer the question.    #pragma comment(linker, "/STACK:1 ...

  6. NPOI导出Excel生成多个sheet

    using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.We ...

  7. 8.for循环及练习

    For循环:   虽然所有循环结构都可以用 while 或者 do...while 表示,但Java提供了另一种语句— —for循环,使一些循环结构变的更加简单. for 循环语句是支持迭代的一种通用 ...

  8. 一张图快速上手Xmind思维导图

    使用软件:Xmind8 pro版(可在网上找破解版),应用广泛,功能强大

  9. Java中SMB的应用

    目录 SMB 服务操作 Ⅰ SMB简介 Ⅱ SMB配置 2.1 Windows SMB Ⅲ 添加SMB依赖 Ⅳ 路径格式 Ⅴ 操作共享 Ⅵ 登录验证 SMB 服务操作 Ⅰ SMB简介 ​ SMB(全称 ...

  10. NOI2019滚粗记

    Day -15 期末考完了,爆炸爆炸,就连数学和物理都错了好多傻*错误QwQ 哎呀管他的,NOI我来了! 跑到广附集训来了23333 Day -14 -- -2 做题,听题,哇和一群队爷在一个教室,真 ...