1. 什么是注解
我们在使用Spring框架、Junit或者TestNG框架的时候,经常看到注解,有方法上注解,也有类上的注解,比如:@Autowired、@Controller、@Test等。
为什么需要加这些注解?『注解』和『XML』两种不同的配置模式,争论了好多年了,各有各的优劣,注解可以提供更大的便捷性,易于维护修改,但耦合度高,而 XML 相对于注解则是相反的。
这些注解又是如何发挥作用的?下面我们一一道来。
2.注解的使用
2.1注解的定义
下面的代码就创建了一个名为Check的注解:
package com.test.annotation;
public @interface Check {
}
我们就可以在类定义的地方使用@Check对这个类进行注解了,不过现在这个注解还不能正常工作,我们还需要用到元注解。
2.2 元注解
元注解就是作用到注解上的注解
常用的元注解有5种:
2.2.1 @Retention
当@Retention作用到一个注解上的时候,说明了一个注解的存活时间。 取值的枚举:
| 枚举值 | 含义 |
|---|---|
| RetentionPolicy.SOURCE | 注解只在源码阶段保留,在编译器进行编译时它将被丢弃忽视 |
| RetentionPolicy.CLASS | 注解只被保留到编译进行的时候,它并不会被加载到 JVM 中 |
| RetentionPolicy.RUNTIME | 注解可以保留到程序运行的时候,它会被加载进入到 JVM 中,所以在程序运行时可以获取到它们 |
package com.test.annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface Check {
}
2.2.2 @Documented
这个元注解与文档有关,它的作用是能够将注解中的元素包含到 Javadoc 中去。
2.2.3 @Target
当@Target注解作用到一个注解上的时候,他限制了这个注解的运用的地方,比如只能作用于类上或者方法上或者局部变量等。
| 枚举值 | 含义 |
|---|---|
| ElementType.ANNOTATION_TYPE | 可以给一个注解进行注解 |
| ElementType.CONSTRUCTOR | 可以给构造方法进行注解 |
| ElementType.FIELD | 可以给属性进行注解 |
| ElementType.LOCAL_VARIABLE | 可以给局部变量进行注解 |
| ElementType.METHOD | 可以给方法进行注解 |
| ElementType.PACKAGE | 可以给一个包进行注解 |
| ElementType.PARAMETER | 可以给一个方法内的参数进行注解 |
| ElementType.TYPE | 可以给一个类型进行注解,比如类、接口、枚举 |
2.2.4 @Inherited
一个超类被 @Inherited 注解过的注解进行注解的话,那么如果它的子类没有被任何注解作用的话,那么这个子类就继承了超类的注解。
2.2.5 @Repeatable
可多次使用的注解
@interface Persons {
Person[] value();
}
@Repeatable(Persons.class)
@interface Person{
String role default "";
}
@Person(role="artist")
@Person(role="coder")
@Person(role="PM")
public class SuperMan{
}
注意上面的代码,@Repeatable 注解了 Person。而 @Repeatable 后面括号中的类相当于一个容器注解。
什么是容器注解呢?就是用来存放其它注解的地方。它本身也是一个注解。
2.3 注解的提取
注解通过反射获取。
package com.service;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
@TestAnnotation(msg="hello")
public class Test {
@CheckQA(value="hi")
int a;
@Perform
public void testMethod(){
System.out.println("hello, testMethod");
}
public static void main(String[] args) {
Test test = new Test();
// 获取类上的注解
boolean hasAnnotation = Test.class.isAnnotationPresent(TestAnnotation.class);
if ( hasAnnotation ) {
//获取类的注解
TestAnnotation testAnnotation = Test.class.getAnnotation(TestAnnotation.class);
System.out.println("id:"+testAnnotation.id());
System.out.println("msg:"+testAnnotation.msg());
}
try {
//获取一个成员变量上的注解
Field a = Test.class.getDeclaredField("a");
a.setAccessible(true);
CheckQA check = a.getAnnotation(CheckQA.class);
if ( check != null ) {
System.out.println("check value:"+check.value());
}
// 获取方法中的注解
Method[] methods = Test.class.getDeclaredMethods();
for(Method m : methods){
System.out.println(m.getName());
if(m.isAnnotationPresent(Perform.class)){
m.getAnnotation(Perform.class).annotationType().getSimpleName();
m.setAccessible(true);
m.invoke(test, null);
}
}
} catch (Exception e) {
e.printStackTrace();
System.out.println(e.getMessage());
}
}
}
2.4 注解的属性
注解的属性就是注解的成员变量。
注解只有成员变量没有方法。
2.4.1 注解中属性的声明方法
package com.service;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface CheckQA {
public String value() default "hello";
}
注解中成员变量的声明:作用范围 返回值类型 成员变量名称()
返回值类型:8 种基本数据类型外加 类、接口、注解及它们的数组
成员变量可以设置默认值:如 default "hello"
2.4.2 注解中属性的赋值
@TestAnnotation(msg="hello")
public class Test {
@CheckQA(value="hi")
int a;
@Perform
public void testMethod(){
System.out.println("hello, testMethod");
}
}
注解中属性的赋值方法:注解的成员变量名称=值,多个成员变量之间用逗号隔开
如果注解中只有一个成员变量,那么赋值的时候可以直接写到括号内
如果一个注解没有成员变量或者成员变量已经都设置了默认值,那么可以不用写括号