注解(Annotation)java
一、Annotation的工做原理:spring
JDK5.0中提供了注解的功能,容许开发者定义和使用本身的注解类型。该功能由一个定义注解类型的语法和描述一个注解声明的语法,读取注解的API,一个使用注解修饰的class文件和一个注解处理工具组成。编程
Annotation并不直接影响代码的语义,可是他能够被看作是程序的工具或者类库。它会反过来对正在运行的程序语义有所影响。数组
Annotation能够从源文件、class文件或者在运行时经过反射机制多种方式被读取。框架
有如下几种:ide
1,生成文档。这是最多见的,也是java 最先提供的注解。函数
2,跟踪代码依赖性,实现替代配置文件功能。测试
比较常见的是spring 2.5 开始的基于注解配置。做用就是减小配置。如今的框架基本都 使用了这种配置来减小配置文件的数量。网站
3,在编译时进行格式检查。
如@override 放在方法前,若是你这个方法并非覆盖了超类方法,则编译时就能检查 出。
*@interface用来声明一个注解,其中的每个方法其实是声明了一个配置参数。
*方法的名称就是参数的名称,返回值类型就是参数的类型。
*能够经过default来声明参数的默认值。
Java.lang.annotation
包 java.lang.annotation 中包含全部已定义或自定义注解所需用到的原注解和接口。如接口 java.lang.annotation.Annotation 是全部注解继承的接口,而且是自动继承,不须要定义时指定,相似于全部类都自动继承Object。
该包同时定义了四个元注解;
注释类型 Override
@Target(value=METHOD)
@Retention(value=SOURCE)
public @interface Override
@Override注解表示子类要重写父类的对应方法。
下面是一个使用@Override注解的例子:
class A {
private String id;
A(String id){
this.id = id;
}
@Override
public String toString() {
return id;
}
}
注释类型 Deprecated
@Documented
@Retention(value=RUNTIME)
public @interface Deprecated
@Deprecated注解表示方法是不被建议使用的。
下面是一个使用@Deprecated注解的例子:
class A {
private String id;
A(String id){
this.id = id;
}
@Deprecated
public void execute(){
System.out.println(id);
}
public static void main(String[] args) {
A a = new A("a123");
a.execute();
}
}
注释类型 SuppressWarnings
@Target(value={TYPE,FIELD,METHOD,PARAMETER,CONSTRUCTOR,LOCAL_VARIABLE})
@Retention(value=SOURCE)
public @interface SuppressWarnings
指示应该在注释元素(以及包含在该注释元素中的全部程序元素)中取消显示指定的编译器警告。注意,在给定元素中取消显示的警告集是全部包含元素中取消显示的警告的超集。例如,若是注释一个类来取消显示某个警告,同时注释一个方法来取消显示另外一个警告,那么将在此方法中同时取消显示这两个警告。
下面是一个使用@SuppressWarnings注解的例子:
@SuppressWarnings("unchecked")
public static void main(String[] args) {
List list = new ArrayList();
list.add("abc");
}
使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口,由编译程序自动完成其余细节。在定义注解时,不能继承其余的注解或接口。
1,自定义最简单的注解:
public @interface MyAnnotation {
}
使用自定义注解:
public class AnnotationTest2 {
@MyAnnotation
public void execute(){
System.out.println("method");
}
}
2,添加变量:
public @interface MyAnnotation {
String value1();
}
使用自定义注解:
public class AnnotationTest2 {
@MyAnnotation(value1="abc")
public void execute(){
System.out.println("method");
}
}
添加默认值:
public @interface MyAnnotation {
String value1() default "abc";
}
三、多变量使用枚举:
public @interface MyAnnotation {
String value1() default "abc";
MyEnum value2() default MyEnum.Sunny;
}
enum MyEnum{
Sunny,Rainy
}
使用自定义注解:
public class AnnotationTest2 {
@MyAnnotation(value1="a", value2=MyEnum.Sunny)
public void execute(){
System.out.println("method");
}
}
四、数组变量:
public @interface MyAnnotation {
String[] value1() default "abc";
}
使用自定义注解:
public class AnnotationTest2 {
@MyAnnotation(value1={"a","b"})
public void execute(){
System.out.println("method");
}
}
当注解中使用的属性名为value时,对其赋值时能够不指定属性的名称而直接写上属性值接口;除了value意外的变量名都须要使用name=value的方式赋值。
五、限定注解的使用:
限定注解使用@Target。
@Documented
@Retention(value=RUNTIME)
@Target(value=ANNOTATION_TYPE)
public @interface Target
指示注释类型所适用的程序元素的种类。若是注释类型声明中不存在 Target 元注释,则声明的类型能够用在任一程序元素上。若是存在这样的元注释,则编译器强制实施指定的使用限制。 例如,此元注释指示该声明类型是其自身,即元注释类型。它只能用在注释类型声明上:
@Target(ElementType.ANNOTATION_TYPE)
public @interface MetaAnnotationType {
...
}
此元注释指示该声明类型只可做为复杂注释类型声明中的成员类型使用。它不能直接用于注释:
@Target({})
public @interface MemberType {
...
}
这是一个编译时错误,它代表一个 ElementType 常量在 Target 注释中出现了不仅一次。例如,如下元注释是非法的:
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.FIELD})
public @interface Bogus {
...
}
public enum ElementType
extends Enum<ElementType>
程序元素类型。此枚举类型的常量提供了 Java 程序中声明的元素的简单分类。
这些常量与 Target 元注释类型一块儿使用,以指定在什么状况下使用注释类型是合法的。
ANNOTATION_TYPE
注释类型声明
CONSTRUCTOR
构造方法声明
FIELD
字段声明(包括枚举常量)
LOCAL_VARIABLE
局部变量声明
METHOD
方法声明
PACKAGE
包声明
PARAMETER
参数声明
TYPE
类、接口(包括注释类型)或枚举声明
注解的使用限定的例子:
@Target(ElementType.METHOD)
public @interface MyAnnotation {
String[] value1() default "abc";
}
元注解是指注解的注解。包括 @Retention @Target @Document @Inherited四种。
@Target 表示该注解用于什么地方,可能的值在枚举类 ElemenetType 中,包括: @Target(ElementType.TYPE) //接口、类、枚举、注解
@Target(ElementType.FIELD) //字段、枚举的常量
@Target(ElementType.METHOD) //方法
@Target(ElementType.PARAMETER) //方法参数
@Target(ElementType.CONSTRUCTOR) //构造函数
@Target(ElementType.LOCAL_VARIABLE)//局部变量
@Target(ElementType.ANNOTATION_TYPE)//注解
@Target(ElementType.PACKAGE) //包 packag注解必须在package-info.java 中声明
@Retention 表示在什么级别保存该注解信息。可选的参数值在枚举类型 RetentionPolicy 中,包括:
@Retention(RetentionPolicy.SOURCE)
//注解仅存在于源码中,在class字节码文件中不包含
@Retention(RetentionPolicy.CLASS)
// 默认的保留策略,注解会在class字节码文件中存在,但运行时没法得到,
@Retention(RetentionPolicy.RUNTIME)
// 注解会在class字节码文件中存在,在运行时能够经过反射获取到
@Documented 将此注解包含在 javadoc 中 ,它表明着此注解会被javadoc工具提取成文 档。在doc文档中的内容会由于此注解的信息内容不一样而不一样。至关与 @see,@param 等。 @Inherited 容许子类继承父类中的注解
@Target用来声明注解能够被添加在哪些类型的元素上,如类型、方法和域等。
例:
@Target({TYPE,METHOD,FIELD,CONSTRUCTOR})
public @interface TestA {
//这里定义了一个空的注解。
}
这个类专门用来测试注解使用
@TestA //使用了类注解
public class UserAnnotation {
@TestA //使用了类成员注解
private Integer age;
@TestA //使用了构造方法注解
public UserAnnotation(){
}
@TestA //使用了类方法注解
public void a(){
@TestA //使用了局部变量注解
Map m = new HashMap(0);
}
@TestA //使用了方法参数注解
public void b(@TestA Integer a){
}
}
@Retention用来声明注解的保留策略,有CLASS、RUNTIME和SOURCE这三种,
分别表示注解保存在类文件、JVM运行时刻和源代码中。
只有当声明为RUNTIME的时候,才可以在运行时刻经过反射API来获取到注解的信息。
无属性注解
@Retention 参数 RetentionPolicy。这个注解尚未特殊的属性值。 简单演示下如何使用:
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
/*
* 定义注解 Test
* 首先使用ElementType.TYPE(须要在package-info.java中声明)
* 运行级别定为 运行时,以便后面测试解析
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TestA {}
有属性注解
@interface用来声明一个注解,其中的每个方法其实是声明了一个配置参数。方法的名称就是参数的名称,返回值类型就是参数的类型(返回值类型只能是基本类型、Class、String、enum)。能够经过default来声明参数的默认值。
代码:
/*
* 定义注解 Test
* 为方便测试:注解目标为类 方法,属性及构造方法
* 注解中含有三个元素 id ,name和 gid;
* id 元素 有默认值 0
*/
@Target({TYPE,METHOD,FIELD,CONSTRUCTOR})
@Retention(RetentionPolicy.RUNTIME)
public @interface TestA {
String name();
int id() default 0;
Class<Long> gid();}
测试类
import java.util.HashMap;
import java.util.Map;
/**
* 这个类专门用来测试注解使用
*/
@TestA(name="type",gid=Long.class) //类成员注解
public class UserAnnotation {
@TestA(name="param",id=1,gid=Long.class) //类成员注解
private Integer age;
@TestA (name="construct",id=2,gid=Long.class)//构造方法注解
public UserAnnotation(){}
@TestA(name="public method",id=3,gid=Long.class) //类方法注解
public void a(){
Map<String,String> m = new HashMap<String,String>(0);
}
@TestA(name="protected method",id=4,gid=Long.class) //类方法注解
protected void b(){
Map<String,String> m = new HashMap<String,String>(0);
}
@TestA(name="private method",id=5,gid=Long.class) //类方法注解
private void c(){
Map<String,String> m = new HashMap<String,String>(0);
}
public void b(Integer a){ }
}
读取定义注解内容
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
public class ParseAnnotation {
/**
*A 简单打印出UserAnnotation 类中所使用到的类注解
* 该方法只打印了 Type 类型的注解
* @throws ClassNotFoundException
*/
public static void parseTypeAnnotation() throws ClassNotFoundException {
Class clazz = Class.forName("com.tmser.annotation.UserAnnotation");
Annotation[] annotations = clazz.getAnnotations();
for (Annotation annotation : annotations) {
TestA testA = (TestA)annotation;
System.out.println("id= \""+testA.id()+"\"; name= \""+testA.name()+"\"; gid = "+testA.gid());
}
}
//结果为id="0";name="type";gid =classjava.lang.Long
/*
* B简单打印出UserAnnotation 类中所使用到的方法注解
* 该方法只打印了 Method 类型的注解
* @throws ClassNotFoundException
*/
public static void parseMethodAnnotation(){
Method[] methods = UserAnnotation.class.getDeclaredMethods();
for (Method method : methods) {
// 判断方法中是否有指定注解类型的注解
boolean hasAnnotation = method.isAnnotationPresent(TestA.class);
if (hasAnnotation) {
//根据注解类型返回方法的指定类型注解
TestA annotation = method.getAnnotation(TestA.class);
System.out.println("method = " + method.getName()
+ " ; id = " + annotation.id() + " ; description = "
+ annotation.name() + "; gid= "+annotation.gid());
}
}
}
method = c ; id = 5 ; description = private method; gid= class java.lang.Long
method = a ; id = 3 ; description = public method; gid= class java.lang.Long
method = b ; id = 4 ; description = protected method; gid= class java.lang.Long
/**
* C简单打印出UserAnnotation 类中所使用到的构造方法注解
* 该方法只打印了 Method 类型的注解
* @throws ClassNotFoundException
*/
public static void parseConstructAnnotation(){
Constructor[] constructors = UserAnnotation.class.getConstructors();
for (Constructor constructor : constructors) {
//判断构造方法中是否有指定注解类型的注解
boolean hasAnnotation = constructor.isAnnotationPresent(TestA.class);
if (hasAnnotation) {
//根据注解类型返回方法的指定类型注解
TestA annotation =(TestA) constructor.getAnnotation(TestA.class);
System.out.println("constructor = " + constructor.getName()
+ " ; id = " + annotation.id() + " ; description = "
+ annotation.name() + "; gid= "+annotation.gid());
}
}
}
constructor=com.tmser.annotation.UserAnnotation;id=2;description =construct;gid=classjava.lang.Long
public static void main(String[] args) throws ClassNotFoundException {
parseTypeAnnotation();
parseMethodAnnotation();
parseConstructAnnotation();// field是同样的,省略之
}
}
在帮助文档中加入注解:
要想在制做JavaDoc文件的同时将注解信息加入到API文件中,可使用java.lang.annotation.Documented。
在自定义注解中声明构建注解文档:
@Documented
public @interface MyAnnotation {
String[] value1() default "abc";
}
使用自定义注解:
public class AnnotationTest2 {
@MyAnnotation(value1={"a","b"})
public void execute(){
System.out.println("method");
}
}
在注解中使用继承:
默认状况下注解并不会被继承到子类中,能够在自定义注解时加上java.lang.annotation.Inherited注解声明使用继承。
@Documented
@Retention(value=RUNTIME)
@Target(value=ANNOTATION_TYPE)
public @interface Inherited
指示注释类型被自动继承。若是在注释类型声明中存在 Inherited 元注释,而且用户在某一类声明中查询该注释类型,同时该类声明中没有此类型的注释,则将在该类的超类中自动查询该注释类型。此过程会重复进行,直到找到此类型的注释或到达了该类层次结构的顶层 (Object) 为止。若是没有超类具备该类型的注释,则查询将指示当前类没有这样的注释。
注意,若是使用注释类型注释类之外的任何事物,此元注释类型都是无效的。还要注意,此元注释仅促成从超类继承注释;对已实现接口的注释无效。
下面是使用反射读取RUNTIME保留策略的Annotation信息的例子:
@Retention(RetentionPolicy.RUNTIME) public @interface MyAnnotation { String[] value1() default "abc"; }
|
使用自定义注解: public class AnnotationTest2 { @MyAnnotation(value1={"a","b"}) @Deprecated public void execute(){ System.out.println("method"); } }
|
读取注解中的信息: public static void main(String[] args) throws SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException { AnnotationTest2 annotationTest2 = new AnnotationTest2(); //获取AnnotationTest2的Class实例 Class<AnnotationTest2> c = AnnotationTest2.class; //获取须要处理的方法Method实例 Method method = c.getMethod("execute", new Class[]{}); //判断该方法是否包含MyAnnotation注解 if(method.isAnnotationPresent(MyAnnotation.class)){ //获取该方法的MyAnnotation注解实例 MyAnnotation myAnnotation = method.getAnnotation(MyAnnotation.class); //执行该方法 method.invoke(annotationTest2, new Object[]{}); //获取myAnnotation String[] value1 = myAnnotation.value1(); System.out.println(value1[0]); } //获取方法上的全部注解 Annotation[] annotations = method.getAnnotations(); for(Annotation annotation : annotations){ System.out.println(annotation); } }
|
@Documented @Target({ElementType.TYPE,ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface Yts { public enum YtsType{util,entity,service,model}; public YtsType classType() default YtsType.util; }
|
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) @Inherited public @interface HelloWorld { public String name()default ""; }
|
public class ParseAnnotation { public void parseMethod(Class clazz) throws IllegalArgumentException, IllegalAccessException,InvocationTargetException,SecurityException, NoSuchMethodException, InstantiationException{ Object obj = clazz.getConstructor(new Class[]{}).newInstance(new Object[]{}); for(Method method : clazz.getDeclaredMethods()){ HelloWorld say = method.getAnnotation(HelloWorld.class); String name = ""; if(say != null){ name = say.name(); method.invoke(obj, name); } Yts yts = (Yts)method.getAnnotation(Yts.class); if(yts != null){ if(YtsType.util.equals(yts.classType())){ System.out.println("this is a util method"); }else{ System.out.println("this is a other method"); } } } }
@SuppressWarnings("unchecked") public void parseType(Class clazz) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException{ Yts yts = (Yts) clazz.getAnnotation(Yts.class); if(yts != null){ if(YtsType.util.equals(yts.classType())){ System.out.println("this is a util class"); }else{ System.out.println("this is a other class"); } } }
} |
@Yts(classType =YtsType.util) public class SayHell {
@HelloWorld(name = " 小明 ") @Yts public void sayHello(String name){ if(name == null || name.equals("")){ System.out.println("hello world!"); }else{ System.out.println(name + "say hello world!"); } } }
|
public static void main(String[] args) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, SecurityException, NoSuchMethodException, InstantiationException { ParseAnnotation parse = new ParseAnnotation(); parse.parseMethod(SayHell.class); parse.parseType(SayHell.class); } |
1. 要用好注解,必须熟悉java 的反射机制,从上面的例子能够看出,注解的解析彻底依赖于反射。
2. 不要滥用注解。日常咱们编程过程不多接触和使用注解,只有作设计,且不想让设计有过多的配置时。
了解更多详情请登陆超人学院网站http://www.crxy.cn或者每周日晚八点半相约免费公开课 https://ke.qq.com/course/53102#term_id=100145289 具体详情请联系QQ2435014406