注解-Annotation
问题:
- 什么是注解;
- 为什么要引入注解
- 注解是如何工作的
- 如何编写自定义的注解(通过例子)
- 什么情况下可以使用注解
- 最新注解和ADF(应用开发框架)。
一.概述
从 JDK 5.0 开始, Java 增加了对元数据(MetaData) 的支持, 也就是 Annotation(注解)
一句话:注解就是一种描述数据的数据。
1)元数据 MetaData:即一种描述数据的数据
2)注解 Annotation:代码里的特殊标记, 这些标记可以在编译、类加载、运行时被读取,并执行相应的处理。 通过使用 Annotation, 程序员可以在不改变原有逻辑的情况下, 在源文件中嵌入一些补充信息。
3)Annotation 可以像修饰符一样被使用, 可用于修饰包,类, 构造器, 方法, 成员变量, 参数, 局部变量的声明, 这些信息被保存在 Annotation 的 “name=value” 对中
4)一些应用场景:
在JavaSE中,注解的使用目的比较简单,例如标记过时的功能,忽略警告等。在JavaEE/Android中注解占据了更重要的角色,例如用来配置应用程序的任何切面,代替java EE旧版中所遗留的繁冗代码和XML配置等。
举例:
@Override
public String toString() {
return "Override";
}
上面重写了toString()方法,并使用了@Override注解。但是,即使我不使用@Override注解标记代码,程序也能够正常执行。
那么,该注解表示什么?这么写有什么好处吗?
- @Override告诉编译器这个方法是一个重写方法(描述方法的元数据),如果父类中不存在该方法,编译器便会报错,提示该方法没有重写父类中的方法。
- 如果不小心拼写错误,例如将toString()写成了toStrring(),而且没有使用@Override注解,那程序依然能编译运行。但运行结果会与期望大不相同。
- 现在我们了解了什么是注解,并且使用注解有助于阅读程序。
二.为什么要引入注解?
使用Annotation之前(甚至在使用之后),XML被广泛的应用于描述元数据。
- 注解:是一种分散式的元数据,与源代码紧绑定。
- xml:是一种集中式的元数据,与源代码无绑定
两者各有利弊:
- 假如你想为应用设置很多的常量或参数,这种情况下,XML是一个很好的选择,因为它不会同特定的代码相连。
- 如果你想把某个方法声明为服务,那么使用Annotation会更好一些,因为这种情况下需要注解和方法紧密耦合起来,开发人员也必须认识到这点。
- 此外,Annotation定义了一种标准的描述元数据的方式。
注解的主要用途:
- 生成文档,通过代码里标识的元数据生成javadoc文档。
- 编译检查,通过代码里标识的元数据让编译器在编译期间进行检查验证。
- 编译时动态处理,编译时通过代码里标识的元数据动态处理,例如动态生成代码。
- 运行时动态处理,运行时通过代码里标识的元数据动态处理,例如使用反射注入实例
目前,许多框架将XML和Annotation两种方式结合使用,平衡两者之间的利弊。
三.注解的定义
注解的使用:使用 Annotation 时要在其前面增加 @ 符号, 并把该 Annotation 当成一个修饰符使用。用于修饰它支持的程序元素。
- JDK内置的基本注解类型(3个)
- 自定义注解类型[注解的简单定义]
- 元注解:对注解进行注解(4个)
@Documented –注解是否将包含在JavaDoc中
@Retention –什么时候使用该注解
@Target –注解用于什么地方
@Inherited – 是否允许子类继承该注解 - 利用反射获取注解信息
1.JDK的注解类型
1.JDK内置的基本注解
-
@Override: 编译时检查当前这个方法是否满足重写的条件, 该注解只能用于方法
-
@Deprecated: 用于表示某个程序元素(类, 方法等)已过时
-
@SuppressWarnings: 抑制编译器警告
- 可以修饰:类, 构造器, 方法, 成员变量, 参数, 局部变量
2.单元测试中应用的注解
作用:用于替代配置文件
- @Test 应用在方法上:public修饰的类中public修饰的方法
- @Before 修饰的方法:会在每个测试方法执行之前执行
- @After 修饰的方法:会在每个测试方法执行之后执行
- @BeforeClass 要求被修饰的方法是静态的,在所有Test方法[测试方法]之前执行一次
- @AfterClass 要求被修饰的方法是静态的,在所有Test方法[测试方法]之后执行一次
案例:
public class Demo2 {
@BeforeClass
public static void beforeClass(){
System.out.println("beforeClass....");
}
@AfterClass
public static void afterClass(){
System.out.println("afterClass...");
}
@Before
public void before(){
System.out.println("before all test.....");
}
@After
public void after(){
System.out.println("after all test...");
}
@Test
public void test1(){
System.out.println("测试方法1");
}
@Test
public void test2(){
System.out.println("测试方法2");
}
//如果执行全部测试单元的话输出结果为
// beforeClass....
// before all test.....
// 测试方法1
// after all test...
// before all test.....
// 测试方法2
// after all test...
// afterClass...
}
2.元注解
元注解:对注解的解释或设置——加在注解的定义上(注解的注解)
2.1 元注解的类型
4个元注解都是在jdk的java.lang.annotation包下面
1)@Retention: 保留策略——设置该注解在什么时刻有效;使用 RetentionPolicy 枚举类型的参数
public @interface Retention {
/**
* Returns the retention policy.
* @return the retention policy
*/
RetentionPolicy value();
}
2)@Target:目标——决定可以用来修饰什么数据;使用 ElementType 枚举类型的参数
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();
}
3)@Documented:能否在生成的帮助文档中显示 文档注释
public @interface Documented {}
4)@Inherited:继承
@Inherited 元注解是一个标记注解,@Inherited阐述了某个被标注的类型是被继承的。如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类
注意:@Inherited annotation类型是被标注过的class的子类所继承。类并不从它所实现的接口继承annotation,方法并不从它所重载的方法继承annotation。
3.注解的自定义
1.语法:
[@元注解]
[修饰符] @interface 注解名{}
2.自定义注解的参数设置
public @interface MyAnnotation{
String name() default “annotation";
}
1)注解成员的定义格式:
-
数据类型 名称() [default 值];
String value() default “java”;
String[] value() default “java”; -->数组参数
2)使用时的传参格式:
- @元注解(名称=值 [,参数2] )
- @元注解(名称={值1,值2} )
3)当参数名称是 value() ,并且只有一个参数时:
- 传参时 value= 是可以省略的;
- 否则不可省略名称;
@interfaceAuthor{
String value();
}
//注解的使用
@Author("Yashwant")
public void someMethod() {
}
4)当参数是数组类型时:
- 参数只有一个值时,使用 名称 = 值 即可;
- 参数有多个值时,使用 名称 = {值……}
3)参数的类型可以是:
- 八大基本数据类型
- String
- 枚举
- 以上的数组类型
3.案例
// 定义一个可以注解在METHOD上的注解
@Target({ElementType.METHOD})
//保留策略:运行时
@Retention(RetentionPolicy.RUNTIME)
public @interface MyMethod {
// 定义注解的一个元素 并给定默认值
String value() default "我是定义在方法上注解的元素value的默认值";
}
四.注解的使用
前面描述了注解是如何定义的,但注解是如何使用,更重要的的是其是如何发挥作用的呢?
1.注解的作用机制
通过反射,获取对应元素上的注解,根据注解内容以及元素内容进行判断。
@Test
public void testAnnotation() {
//1.获取对应类的Class对象
Class calzz = Class.forName("全类名");
//2.获取Class对象的注解们
Annotation[] annotations = clazz.getAnnotations();
//3.遍历打印注解的简单名称
for (Annotation annotation : annotations) {
//1)获取该注解的Class对象
Class<? extends Annotation> annotationType = annotation.annotationType();
//2)打印注解Class对象的简单名称
System.out.println(annotationType.getSimpleName());
}
}
2.Annotation类(java.lang.reflect.Annotation):
Annotation类型定义了Annotation的名字、类型、成员默认值。
一个Annotation类型可以说是一个特殊的java接口,它的成员变量是受限制的,而声明Annotation类型时需要使用新语法。当我们通过java反射API访问Annotation时,返回值将是一个实现了该 annotation类型接口的对象,通过访问这个对象我们能方便的访问到其Annotation成员。
3.注解处理器类库(java.lang.reflect.AnnotatedElement)
Java使用Annotation接口来代表程序元素前面的注解,该接口是所有Annotation类型的父接口。
除此之外,Java在java.lang.reflect 包下新增了AnnotatedElement接口,该接口代表程序中可以接受注解的程序元素,该接口主要有如下几个实现类(如下图所示):
- Class:类定义
- Constructor:构造器定义
- Field:累的成员变量定义
- Method:类的方法定义
- Package:类的包定义
java.lang.reflect 包所有提供的反射API,扩充了读取运行时Annotation信息的能力。当一个Annotation类型被定义为运行时的Annotation后,该注解才能是运行时可见,当class文件被装载时被保存在class文件中的Annotation才会被虚拟机读取。
而AnnotatedElement 接口是所有程序元素(Class、Method和Constructor)的父接口,所以程序通过反射获取了某个类的Class对象、Method对象……之后,程序就可以调用该对象实现的AnnotatedElement接口的四个方法,来访问Annotation信息:
- T getAnnotation(Class annotationClass): 返回改程序元素上存在的、指定类型的注解,如果该类型注解不存在,则返回null。
- Annotation[] getAnnotations():返回该程序元素上存在的所有注解。
- boolean is AnnotationPresent(Class<?extends Annotation> annotationClass):判断该程序元素上是否包含指定类型的注解,存在则返回true,否则返回false.
- Annotation[] getDeclaredAnnotations():返回直接存在于此元素上的所有注释。与此接口中的其他方法不同,该方法将忽略继承的注释。(如果没有注释直接存在于此元素上,则返回长度为零的一个数组)
4.注解的使用案例:
https://blog.csdn.net/zhang0558/article/details/52643016