注解 annotation

什么是注解?

1. Annotation是从JDK5.0开始引入的新技术。

2. Annotation的作用:

– 不是程序本身,可以对程序作出解释。(这一点,跟注释没什么区别)

– 可以被其他程序(比如:编译器等)读取。(注解信息处理流程,是注解和注释的重大区别 。如果没有注解信息处理流程,则注解毫无意义)

3. Annotation的格式:

– 注解是以“@注释名”在代码中存在的,还可以添加一些参数值,例如: @SuppressWarnings(value="unchecked")。

4. Annotation在哪里使用?

– 可以附加在package, class, method, field等上面,相当于给它们添加了额外的辅助信 息,我们可以通过反射机制编程实现对这些元数据的访问。


内置注解

1.  @Override

– 定义在java.lang.Override中,此注释只适用于修辞方法,表示一个方 法声明打算重写超类中的另一个方法声明。

2.  @Deprecated

– 定义在java.lang.Deprecated中,此注释可用于修辞方法、属性、类 ,表示不鼓励程序员使用这样的元素,通常是因为它很危险或存在更 好的选择。
3. @SuppressWarnings

– 定义在java.lang.SuppressWarnings中,用来抑制编译时的警告信息 。

– 与前两个注释有所不同,你需要添加一个参数才能正确使用,这些参 数值都是已经定义好了的,我们选择性的使用就好了,参数如下:

参数 说明
deprecation 使用了过时的类或方法警告
unchecked 执行了未检查的转换时的警告,如使用集合时为指定泛型
fallthrough 当在switch语句使用时发生了case穿透
path 在类路径,源文件路径中有不存在路径的警告
serial 当在可序列化的类上缺少 serialVersionUID 定义时的警告
finally 任何finally字句不能完成时的警告
all 关于以上所有情况的警告

– @SuppressWarnings("unchecked")

– @SuppressWarnings(value={"unchecked", "deprecation"})

注:后面新增的注解没有介绍。

import java.util.ArrayList;
import java.util.Date;
import java.util.List;//@SuppressWarnings("all")
public class AnnotationDemo /**extends Object*/{@Overridepublic String toString() {return "";}@Deprecatedpublic static void test01(){System.out.println("test01");}@SuppressWarnings("all")public static void test02(){List list = new ArrayList();List list2 = new ArrayList();List list3 = new ArrayList();}public static void main(String[] args){Date date = new Date();test01();}
}

注:SuppressWarnings 主机里面的 String[] value(); 并不是一个方法,而是一个参数,value是参数名,String[] 是参数类型

@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {String[] value();
}

自定义注解

1. 使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口

2. 要点:

– @interface用来声明一个注解

3. 格式为:

– public @interface 注解名 {定义体} – 其中的每一个方法实际上是声明了一个配置参数。

– 方法的名称就是参数的名称

– 返回值类型就是参数的类型(返回值类型只能是基本类型、Class、String、enum)

– 可以通过default来声明参数的默认值。 – 如果只有一个参数成员,一般参数名为value

4. 注意:

注解元素必须要有值。我们定义注解元素时,经常使用空字符串、0作为默认值。 也经常使用负数(比如:-1)表示不存在的含义

元注解

元注解的作用就是负责注解其他注解。 Java定义了4个标准的 meta-annotation类型,它们被用来提供对其它 annotation 类型作说明。 这些类型和它们所支持的类在java.lang.annotation包中可以找到。 @Target 、 @Retention 、 @Documented 、@Inherited

@Target

作用: 用于描述注解的使用范围(即:被描述的注解可以用在什么地方)

所修饰范围 取值 ElementType
package 包 PACKAGE
类、接口、枚举、Annotation 类型 TYPE

类型成员(方法、构造方法、成员变量、枚举值)

CONSTRUCTOR 用于描述构造器

FIELD 用户标识域

METHOD 用于描述方法


方法参数和本地变量

LOCAL_VARIABLE 用于描述局部变量

PARAMETER 用于描述参数

– @Target(value=ElementType.TYPE)


package java.lang.annotation;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {/*** Returns an array of the kinds of elements an annotation type* can be applied to.* @return an array of the kinds of elements an annotation type* can be applied to*/ElementType[] value();
}
package java.lang.annotation;
public enum ElementType {/** 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** @since 1.8*/TYPE_PARAMETER,/*** Use of a type** @since 1.8*/TYPE_USE
}

@Retention

作用: 表示需要在什么级别保存该注释信息,用于描述注解的生命周期

取值 RetentionPolicv 作用
SOURCE 在源文件中有效(即源文件保留)
CLASS 在class文件中有效(即class保留)
RUNTIME

在运行时有效(即运行时保留)

为Runtime可以被反射机制读取

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(value = {ElementType.METHOD, ElementType.TYPE})//可以修饰方法和类
@Retention(RetentionPolicy.RUNTIME)//保留到运行时
public @interface MyAnnotation01 {//没有默认值需要定义,不然会报错String studentName() default "";int age() default 0;int id() default -1;//表示不存在 比如 String indexof("") 没有找到返回 -1String[] schools() default {"北京大学","清华大学"};//数组使用 {} 来初始化
}

注:当我们的注解类里面只有一个参数的时候,通常把它定义为value ,数组的初始化用的是 {} 表示空数组。

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target(value = {ElementType.METHOD, ElementType.TYPE})//可以修饰方法和类
@Retention(RetentionPolicy.RUNTIME)//保留到运行时
public @interface MyAnnotation02 {String value();
}

注:当我们的注解类里面只有一个属性,且属性名为value 的时候可以省略 value =

// @MyAnnotation01 编译警告
public class AnnotationDemo02 {@MyAnnotation01(age = 19, studentName = "张三",id = 10011, schools = {"野鸡大学"})//显示的定义值public void test(){ }//@MyAnnotation02(value = "aaa")@MyAnnotation02("aaa")//当里面只有一个属性,且属性名为value 的时候可以省略 value =public void test02() { }
}

前面定义的这些注解如果不解析,这些定义的注解将不会有任何意义


使用反射机制读取注解信息

如上我们只讲解了注解的定义。我们必须再将注解的读取学会 才能轰然一体,彻底搞定注解

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;//java的属性和表的属性的对应
@Target(value = {ElementType.FIELD})//修饰属性
@Retention(RetentionPolicy.RUNTIME)//保留到运行时
public @interface SxtField {String columnName();//列名String type();//数据类型int length();//长度}
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target(value = {ElementType.TYPE})//可以类
@Retention(RetentionPolicy.RUNTIME)//保留到运行时
public @interface SxtTable {String value();//类和表对应
}
@SxtTable("tb_student")//类和表的映射
public class SxStudent {@SxtField(columnName = "id", type = "int", length = 10)//用于把属性转换成表里面的字段private int id;@SxtField(columnName = "sname", type = "varchar", length = 10)//用于把属性转换成表里面的字段private String studentName;@SxtField(columnName = "age", type = "int", length = 3)//用于把属性转换成表里面的字段private int age;public int getId() {return id;}public void setId(int id) {this.id = id;}public String getStudentName() {return studentName;}public void setStudentName(String studentName) {this.studentName = studentName;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}
}
import org.junit.Test;import java.lang.annotation.Annotation;
import java.lang.reflect.Field;/*** 使用反射读取注解的信息,模拟处理注解信息的流程*/
public class AnnotationDemo {@Testpublic void test() {try {Class<?> clazz = Class.forName("com.bug.test.SxStudent");Annotation[] annotations = clazz.getAnnotations();//获取这个类的所有的注解for (Annotation annotation:annotations) {System.out.println(annotation);}//通过类的指定的注解SxtTable st = clazz.getAnnotation(SxtTable.class);System.out.println(st);//获得类的属性的注解Field field = clazz.getDeclaredField("studentName");System.out.println(field);SxtField sxtField = field.getAnnotation(SxtField.class);System.out.println(sxtField);System.out.println("列名:\t"+sxtField.columnName()+"\n类型值\t"+sxtField.type()+"\n长度:\t"+sxtField.length());//根据获得的表名,字段的信息,拼出DDL语句,然后使用JDBC执行这个SQL,在数据库中生成相关的表}catch (Exception e){e.printStackTrace();}}
}

编译时处理 Annotation

APT(Annotation Processing Tool)是一种注解处理工具,它对源代码文件进行检测,并找出源文件所包含的 Annotation信息,然后针对 Annotation信息进行额外的处理。

使用APT工具处理 Annotation时可以根据源文件中的 Annotation生成额外的源文件和其他的文件(文件的具体内容由 Annotation处理器的编写者决定),APT还会编译生成的源代码文件和原来的源文件,将它们一起生成cass文件。

使用APT的主要目的是简化开发者的工作量,因为APT可以在编译程序源代码的同时生成一些附属文件(比如源文件、类文件、程序发布描述文件等),这些附属文件的内容也都与源代码相关。换句话说,使用APT可以代替传统的对代码信息和附属文件的维护工作。

了解过 Hibernate早期版本的读者都知道:每写一个Java类文件,还必须额外地维护一个 Hibernate映射文件(名为*hbm.xml的文件,也有一些工具可以自动生成)。下面将使用 Annotation来简化这步操作。

不了解 Hibernate的读者也无须担心,你只需要明白此处要做什么即可——通过注解可以在Java源文件中放置一些 Annotation,然后使用APT工具就可以根据该 Annotation 生成另一份XML文件,这就是 Annotation的作用。

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

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

为了示范使用APT根据源文件中的注解来生成额外的文件,下面将定义3种 Annotation类型,分别用于修饰持久化类、标识属性和普通成员属性。

import java.lang.annotation.*;@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
@Documented
public @interface Persistent {String table();
}

这是一个非常简单的 Annotation,它能修饰类、接口等类型声明,这个 Annotation使用了@Retention元数据注解指定它仅在Java源文件中保留,运行时不能通过反射来读取该 Annotation信息。

下面是修饰标识属性的@ld Annotation

import java.lang.annotation.*;@Target(ElementType.FIELD)
@Retention(RetentionPolicy.SOURCE)
@Documented
public @interface Id {String column();String type();String generator();
}

这个@ld与前一个@Persistent的结构基本相似,只是多了两个成员变量而已。下面还有一个用于修饰普通成员属性的 Annotation。

import java.lang.annotation.*;@Target(ElementType.FIELD)
@Retention(RetentionPolicy.SOURCE)
@Documented
public @interface Property {String column();String type();
}

定义了这三个 Annotation之后,下面提供一个简单的Java类文件,这个Java类文件使用这三个Annotation来修饰。

@Persistent(table = "person_inf")
public class Person {@Id(column = "person_id", type = "integer", generator = "identity")private int id;@Property(column = "person_name", type = "string")private String name;@Property(column = "person_age", type = "integer")private int age;//无参数的构造器public Person() {}//初始化全部成员变量的构造器public Person(int id, String name, int age) {this.id = id;this.name = name;this.age = age;}//id的setter和getter方法public void setId(int id) {this.id = id;}public int getId() {return this.id;}//name的setter和getter方法public void setName(String name) {this.name = name;}public String getName() {return this.name;}//age的setter和getter方法public void setAge(int age) {this.age = age;}public int getAge() {return this.age;}}

上面的 Person类是一个非常普通的Java类,但这个普通的Java类中使用了@Persistent、@Id 和 @Property 三个 Annotation进行修饰。下面为这三个 Annotation提供一个APT工具,该工具的功能是根据注解来生成一个 Hibernate映射文件(不懂 Hibernate也没有关系,只需要明白可以根据这些Annotation来生成另一份XML文件即可)。

import javax.annotation.processing.*;
import javax.lang.model.element.*;
import javax.lang.model.*;import java.io.*;
import java.util.*;@SupportedSourceVersion(SourceVersion.RELEASE_8)
// 指定可处理@Persistent、@Id、@Property三个Annotation
@SupportedAnnotationTypes({"Persistent", "Id", "Property"})
public class HibernateAnnotationProcessor extends AbstractProcessor {// 循环处理每个需要处理的程序对象public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {// 定义一个文件输出流,用于生成额外的文件PrintStream ps = null;try {// 遍历每个被@Persistent修饰的class文件for (Element t : roundEnv.getElementsAnnotatedWith(Persistent.class)) {// 获取正在处理的类名Name clazzName = t.getSimpleName();// 获取类定义前的@Persistent AnnotationPersistent per = t.getAnnotation(Persistent.class);// 创建文件输出流ps = new PrintStream(new FileOutputStream(clazzName+ ".hbm.xml"));// 执行输出ps.println("<?xml version=\"1.0\"?>");ps.println("<!DOCTYPE hibernate-mapping PUBLIC");ps.println("  \"-//Hibernate/Hibernate "+ "Mapping DTD 3.0//EN\"");ps.println("    \"http://www.hibernate.org/dtd/"+ "hibernate-mapping-3.0.dtd\">");ps.println("<hibernate-mapping>");ps.print("    <class name=\"" + t);// 输出per的table()的值ps.println("\" table=\"" + per.table() + "\">");for (Element f : t.getEnclosedElements()) {// 只处理成员变量上的Annotationif (f.getKind() == ElementKind.FIELD)   // ①{// 获取成员变量定义前的@Id AnnotationId id = f.getAnnotation(Id.class);      // ②// 当@Id Annotation存在时输出<id.../>元素if (id != null) {ps.println("     <id name=\""+ f.getSimpleName()+ "\" column=\"" + id.column()+ "\" type=\"" + id.type()+ "\">");ps.println("       <generator class=\""+ id.generator() + "\"/>");ps.println("      </id>");}// 获取成员变量定义前的@Property AnnotationProperty p = f.getAnnotation(Property.class);  // ③// 当@Property Annotation存在时输出<property.../>元素if (p != null) {ps.println("        <property name=\""+ f.getSimpleName()+ "\" column=\"" + p.column()+ "\" type=\"" + p.type()+ "\"/>");}}}ps.println("   </class>");ps.println("</hibernate-mapping>");}} catch (Exception ex) {ex.printStackTrace();} finally {if (ps != null) {try {ps.close();} catch (Exception ex) {ex.printStackTrace();}}}return true;}
}

上面的 Annotation处理器其实非常简单,与前面通过反射来获取 Annotation信息不同的是,这个Annotation处理器使用 RoundEnvironment来获取 Annotation信息,RoundEnvironment 里包含了一个 getElementsAnnotatedWith() 方法,可根据 Annotation获取需要处理的程序单元,这个程序单元由 Element代表。Element里包含一个 getKind() 方法,该方法返回 Element所代表的程序单元,返回值可以是 ElementKind.CLASS(类)、ElementKind.FIELD(成员变量)…

除此之外,Element还包含一个 getEnclosedElements() 方法,该方法可用于获取该 Element里定义的所有程序单元,包括成员变量、方法、构造器、内部类等。

接下来程序只处理成员变量前面的 Annotation,因此程序先判断这个 Element必须是 ElementKind.FIELD(如上程序中①号代码所示)。

再接下来程序调用了 Element提供的 getAnnotation(Class clazz) 方法来获取修饰该 Element 的Annotation,如上程序中②③号部分就是获取成员变量上 Annotation对象的代码。获取到成员变量上的@ld、@Property之后,接下来就根据它们提供的信息执行输出。

run.cmd

rem rem是cmd的注释符号
rem 编译当前路径下的HibernateAnnotationProcessor.java 使用的是utf-8编码,这个根据实际情况修改
javac -encoding utf-8 .\HibernateAnnotationProcessor.javarem 使用HibernateAnnotationProcessor作为APT处理Person.java中的Annotation
javac -encoding utf-8 -processor HibernateAnnotationProcessor Person.java
cmd

当在cmd界面编译java文件时,可能会出现由于保存的文件采用的是UTF-8编码格式,并且源文件中包含中文那么javac则会因为这些中文而报错,说什么错误的gbk格式错误啥的,对于这种情况,我们在编译时,可以给编译指令携带上编码参数,javac -encoding 编码格式 源文件,如:

javac -encoding utf-8 Output.java

通过上面的命令编译 Person。java后,将可以看到在相同路径下生成了一个 Person.hbm.xml文件, 该文件就是根据 Person.java里的 Annotation生成的。该文件的内容如下:

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC"-//Hibernate/Hibernate Mapping DTD 3.0//EN""http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping><class name="Person" table="person_inf"><id name="id" column="person_id" type="integer"><generator class="identity"/></id><property name="name" column="person_name" type="string"/><property name="age" column="person_age" type="integer"/></class>
</hibernate-mapping>

对比上面XML文件中的粗体字部分与 Person.java中的 Annotation部分,它们是完全对应的,这即表明这份XML文件是根据 Person.java中的 Annotation生成的。从生成的这份XML文件可以看出,通过使用APT工具确实可以简化程序开发,程序员只需把一些关键信息通过 Annotation写在程序中,然后使用APT工具就可生成额外的文件。

java_Annotation相关推荐

  1. Java 注解指导手册 – 终极向导

    转载自  Java 注解指导手册 – 终极向导 译文出处: Toien Liu   原文出处:Dani Buiza 编者的话:注解是java的一个主要特性且每个java开发者都应该知道如何使用它. 我 ...

  2. Java注释教程– ULTIMATE指南(PDF下载)

    编者注:在本文中,我们提供了全面的Java注释教程. Java中的注释是一项主要功能,每个Java开发人员都应该知道如何使用它们. 我们在Java Code Geeks上提供了许多教程,例如创建自己的 ...

  3. Annotation实战【自定义AbstractProcessor】

    前言 在使用Java的过程中,每个开发人员都接触过@Override, @Deprecated等等各式各样的注解,这些东西是java最基础的一些原生定义好的annotation.本文通过一个实例演示如 ...

  4. excel批注不显示批注框_批注和批注处理器入门指南[解释]

    excel批注不显示批注框 在Java中,大多数情况下,批注和批注处理器都被一团谜团包围. 他们看起来像是为"专家"保留的主题. 最重要的是,我相信他们周围也有一些FUD. 这篇文 ...

  5. Android注解(Annotation)知识点总结整理

    Java提供了Annotations来对代码提供一些注解,以解释.约束代码,也可以称为编程的元数据.到底什么是Annotation呢,举几个大家熟悉的例子如@Override(表示重载).@Depre ...

最新文章

  1. 启程 - 《每日五分钟搞定大数据》
  2. [转贴]现在在做一个WEB的站内消息系统,从工具栏位置弹出一徐徐上升的窗口...
  3. php xml expat,PHP Expat :XML的解析器
  4. Spark1.4 和 Hive 1.1.1 启动错误
  5. python按键_【python】python控制鼠标和键盘
  6. Boost智能指针——scoped_ptr
  7. YOLOv1-YOLOv4
  8. script async和defer
  9. J2EE远程调试,远程debug你的线上Springboot项目
  10. systemback慎用:安装后无法启动
  11. 华硕主板放电才能点亮
  12. C++ PRIMER 5TH 课后题答案1.16
  13. java ssh框架 运行_SSH框架的简单示例(执行流程)
  14. SLAM领域的优秀作者与实验室汇总
  15. 机票大讲堂之机票的秘密
  16. Linux系统操作命令
  17. 易飞ERP--自定义报表之采购价格分析功能表
  18. powerpoint中如何使用母版
  19. 使用了可能未初始化的本地指针变量“xxx”
  20. 程序员必须知道的9大数据挖掘工具

热门文章

  1. html好友页面,好友列表.html
  2. 靓号,java正则表达式的实现
  3. 加多芬科技讲解目前趋势“不带钱包出门”已成趋势 无现金支付你准备好了吗?
  4. 学习win32汇编指令:lea和offset
  5. 什么是 GraphQL?
  6. 【FFMPEG】H264码流的两种组织方式:AnnexB和AVCC
  7. 杭电OJ 1129(C++)
  8. NOIP2013普及组 题解
  9. 原生M1/M2 Photoshop 2022 for Mac(PS2022)v23.4.2 中英文正式发布详情介绍安装教程
  10. 第十四届蓝桥杯C++B组题解(不完全对)