# 注解定义
-
注解就是一个标识符。JDK1.5之后增加了对元数据的支持(元素据的概念见下文解释);也就是我们之前所用到的注解(Annotation),本质就是一个标识符。注解的标识符可以在Java程序在编译,加载,运行期间被读取到,然后做相应的处理。
-
元数据的概念
元数据(Metadata),又称中介数据、中继数据,为描述数据的数据(data about data),主要是描述数据属性(property)的信息,用来支持如指示存储位置、历史数据、资源查找、文件记录等功能。元数据算是一种电子式目录,为了达到编制目录的目的,必须在描述并收藏数据的内容或特色,进而达成协助数据检索的目的。都柏林核心集(Dublin Core Metadata Initiative,DCMI)是元数据的一种应用,是1995年2月由国际图书馆电脑中心(OCLC)和美国国家超级计算应用中心(National Center for Supercomputing Applications,NCSA)所联合赞助的研讨会,在邀请52位来自图书馆员、电脑专家,共同制定规格,创建一套描述网络上电子文件之特征。 -
Annotation接口
Annotation接口是所有注解类的父接口,可以通过反射获取某一个类的注解信息,然后通过相关的操作获取该注解的元数据信息进行相关的处理。
开发第一个注解
编写一个注解非常简单,和普通的类接口没有任何区别,只是多添加了一个符号@,如下:
注意:注解里面只能含有元数据,元素据类型的变量必须在变量名之后加上(),小括号里面写上元数据的信息。
注解可以使用在包,类,成员变量,方法之上,例如:
注解不会影响到程序的运行。
如何使注解生效
想要使用注解生效必须使用JDK提供的元注解。通过使用原注解,可以限制注解修饰类,方法,还是属性。JDK提供的元注解如下:
- Retention
@Retention注解只能修饰注解,表示被修饰的注解的保存时间,保存时间由Retention里面的变量RetentionPolicy的值决定。RetentionPolic是一个枚举类,里面有三个枚举值分别是:Class,Runtime,Source。如果使用Class则表示类编译完成之后把注解信息放入class文件,类运行的时候不能获取注解信息。如果是Runtime则表示类运行的时候可以获取注解的信息。如果是Source 则表示类在编译的时候自动丢弃注解信息。
以下是:RetentionPolicy的源码:
/*
* Copyright (c) 2003, 2004, Oracle and/or its affiliates. All rights reserved.
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
*
*
*/
package java.lang.annotation;
/**
* Annotation retention policy. The constants of this enumerated type
* describe the various policies for retaining annotations. They are used
* in conjunction with the {@link Retention} meta-annotation type to specify
* how long annotations are to be retained.
* 翻译:注解@Retention的策略。是描述注解@Retention的生存时间的不同策略的枚举值常量。通常配合@Retention注解使用去决定注解保留的时间。
*
* @author Joshua Bloch
* @since 1.5 JDK1.5版本以后
*/
public enum RetentionPolicy {
/**
* Annotations are to be discarded by the compiler.
* 翻译:编译器直接丢弃该注解信息
*/
SOURCE,
/**
* Annotations are to be recorded in the class file by the compiler
* but need not be retained by the VM at run time. This is the default
* behavior.
* 翻译:注解会被编译器保留在class文件中,但是在程序运行的时候
* 此注解不能被JVM保留;这是默认的行为。
*/
CLASS,
/**
* Annotations are to be recorded in the class file by the compiler and
* retained by the VM at run time, so they may be read reflectively.
*翻译:注解会被编译器保留在class文件中,而且在程序运行的时候任然
保留注解信息,这样依赖就可以通过反射技术读取注解信息。
* @see java.lang.reflect.AnnotatedElement
*/
RUNTIME
}
- Target
@Target注解只能修饰注解,里面有一个ElementType数组类型的变量,指定修饰的程序单元类型(类,方法,成员变量,注解等。。),指定修饰的程序单元以后,就限制了注解的使用范围。
Target 的ElementType的源码分析:
Target注解里面有一个ElementType类型的数组,表示@Target
注解能够使用的范围由该ElementTpye类型的数组决定,@Target的源码如下:
接着分析:ElementType的源码如下:
/*
* Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*/
package java.lang.annotation;
/**
* The constants of this enumerated type provide a simple classification of the
* syntactic locations where annotations may appear in a Java program. These
* constants are used in {@link Target java.lang.annotation.Target}
* meta-annotations to specify where it is legal to write annotations of a
* given type.
* 翻译:这些枚举常量提供了Java程序可以出现的地方提供了一个简单的语义分类;这些常量通常是用于Target原注解里面详细描述了一个注解的使用地方是否合法。
*
* <p>The syntactic locations where annotations may appear are split into
* <em>declaration contexts</em> , where annotations apply to declarations, and
* <em>type contexts</em> , where annotations apply to types used in
* declarations and expressions.
*
* <p>The constants {@link #ANNOTATION_TYPE} , {@link #CONSTRUCTOR} , {@link
* #FIELD} , {@link #LOCAL_VARIABLE} , {@link #METHOD} , {@link #PACKAGE} ,
* {@link #PARAMETER} , {@link #TYPE} , and {@link #TYPE_PARAMETER} correspond
* to the declaration contexts in JLS 9.6.4.1.
*
* <p>For example, an annotation whose type is meta-annotated with
* {@code @Target(ElementType.FIELD)} may only be written as a modifier for a
* field declaration.
*
* <p>The constant {@link #TYPE_USE} corresponds to the 15 type contexts in JLS
* 4.11, as well as to two declaration contexts: type declarations (including
* annotation type declarations) and type parameter declarations.
*
* <p>For example, an annotation whose type is meta-annotated with
* {@code @Target(ElementType.TYPE_USE)} may be written on the type of a field
* (or within the type of the field, if it is a nested, parameterized, or array
* type), and may also appear as a modifier for, say, a class declaration.
*
* <p>The {@code TYPE_USE} constant includes type declarations and type
* parameter declarations as a convenience for designers of type checkers which
* give semantics to annotation types. For example, if the annotation type
* {@code NonNull} is meta-annotated with
* {@code @Target(ElementType.TYPE_USE)}, then {@code @NonNull}
* {@code class C {...}} could be treated by a type checker as indicating that
* all variables of class {@code C} are non-null, while still allowing
* variables of other classes to be non-null or not non-null based on whether
* {@code @NonNull} appears at the variable's declaration.
*
* @author Joshua Bloch
* @since 1.5
* @jls 9.6.4.1 @Target
* @jls 4.1 The Kinds of Types and Values
*/
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
}
- Document
@Document只能修饰注解,当使用被@Document修饰的注解之后,被修饰的程序单元所在的类使用javadoc命令导出Java文档的时候可以看见被修饰的注解的信息。 - Inherited
@Inhertied只能修饰注解,表示被修饰的注解的所修饰的程序单元所在的类的子类自动自动添加上父类的注解。
5个基本的注解
- @Override
如果子类的方法使用了@Override注解表示该方法一定是重写父类方法 - @Deprecated
如果一个方法上面使用了@Deprecated表示这个方法已经过时,会带有删除线。 - @SurpressWarnings
取消编译警告 - @Savevarags
Java7专门抑制堆污染的注解 - @FuntionalInterface
如果一个接口被@FuntionalInterface注解修饰以后这个接口就是函数是接口,这是jdk1.8以上的版本的一个新特性;所谓的函数是接口就是一个接口里面只能有一个抽象方法。如果一个接口是函数式接口,那么这个接口的子类实例可以使用lamada表达式创建对象。
自定义注解
需求:写一个注解在某一个方法运行的时候自动获取项目下面配置文件的内容
- 在项目下面创建一个config.properties文件
- 书写注解类
- 编写读取配置文件的类 MyConfigure
package com.tedu.cn;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.RandomAccessFile;
import java.util.HashMap;
import java.util.Map;
public class ReadConfigure {
public static Map<String,String> readyConf(){
Map<String,String> config=null;
//实现文件随机读取
File f=new File("MyConfigure.properties");
try {
RandomAccessFile raf=new RandomAccessFile(f,"rw");
config=new HashMap<>();
String username=raf.readLine();
String password=raf.readLine();
String url=raf.readLine();
String driver=raf.readLine();
config.put("username",username.substring(username.indexOf("=")+1));
config.put("password",password.substring(password.indexOf("=")+1));
config.put("url",url.substring(url.indexOf("=")+1));
config.put("driver",driver.substring(driver.indexOf("=")+1));
} catch (Exception e) {
e.printStackTrace();
}
return config;
}
}
- 书写Init类
package com.tedu.cn.init;
import com.tedu.cn.Configure;
import com.tedu.cn.ReadConfigure;
import java.util.Map;
public class Init {
@Configure(inof = "测试加载class路径下的文件")
public Map<String,String> load(){
return ReadConfigure.readyConf();
}
}
- 书写driver类通过反射动态读取配置信息给driver类的相关字段
package com.tedu.cn;
import com.tedu.cn.init.Init;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;
public class Driver {
private String userName;
private String password;
private String url;
private String driver;
/**
* 加载配置文件信息
*/
public void initDriver(){
try {
Class clazz= Init.class;
Object ob=clazz.newInstance();
//获取该clazz对象的所有@Configure修饰的方法
Method m=clazz.getMethod("load");
//判断该方法是否有@Configure注解
Configure cf=m.getAnnotation(Configure.class);
if(cf!=null){
Object map=m.invoke(ob);
//由于目标方法的返回值是一个HashMap,所以可以进行强转操作
if(map instanceof Map){
Map<String,String>maps= (Map<String,String>) map;
this.driver=maps.get("driver");
this.password=maps.get("password");
this.url=maps.get("url");
this.userName=maps.get("username");
}
}
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Driver driver=new Driver();
//初始化配置
driver.initDriver();
System.out.println("数据库的用户名是:"+driver.userName);
System.out.println("数据库的用密码是:"+driver.password);
System.out.println("数据库的url是:"+driver.url);
System.out.println("数据库的驱动是:"+driver.driver);
}
}
- 测试如下:
"C:\Program Files\Java\jdk1.8.0_161\bin\java.exe" "-javaagent:D:\IDE\IntelliJ IDEA 2018.3\lib\idea_rt.jar=62301:D:\IDE\IntelliJ IDEA 2018.3\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_161\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\rt.jar;D:\黔南工作空间\annotation_testing\out\production\annotation_testing;E:\repository\junit\junit\4.12\junit-4.12.jar;E:\repository\org\hamcrest\hamcrest-core\1.3\hamcrest-core-1.3.jar" com.tedu.cn.Driver
数据库的用户名是:root
数据库的用密码是:admin
数据库的url是:jdbc:mysql://127.0.0.1/dbname
数据库的驱动是:com.jdbc.driver
Process finished with exit code 0
标题开发注解的步骤
- 创建注解Java文件。
- 在方法,或者类之上使用注解
- 编写注解驱动类,通过反射技术获取注解相关信息
- 测试注解