1. 主要作用
- 編譯期檢查
@SuppressWarnings, @Deprecated 和 @Override 都具有編譯檢查作用。
- 結合反射,實現更多運行時特性
框架實現中經常使用的方法。
- 生成幫助文檔
通過給 Annotation 註解加上 @Documented 標籤,能使該 Annotation 標籤出現在 javadoc 中。
2. 主要組成部分
package java.lang.annotation;
public interface Annotation {
boolean equals(Object obj);
int hashCode();
String toString();
Class<? extends Annotation> annotationType(); /* 返回Annotation 對應的真實 class 對象 */
}
package java.lang.annotation;
public enum RetentionPolicy {
SOURCE, /* Annotation信息僅存在於編譯器處理期間,編譯器處理完之後就沒有該Annotation信息了 */
CLASS, /* 編譯器將Annotation存儲於類對應的.class文件中。默認行爲 */
RUNTIME /* 編譯器將Annotation存儲於class文件中,並且可由JVM讀入 */
}
每個 annotation 都唯一對應一個 RetentionPolicy(保留策略)。使用元註解 @Retention 來指定。
package java.lang.annotation;
public enum ElementType {
TYPE, /* 類、接口(包括註釋類型)或枚舉聲明 */
FIELD, /* 字段聲明(包括枚舉常量) */
METHOD, /* 方法聲明 */
PARAMETER, /* 參數聲明 */
CONSTRUCTOR, /* 構造方法聲明 */
LOCAL_VARIABLE, /* 局部變量聲明 */
ANNOTATION_TYPE, /* 註釋類型聲明 */
PACKAGE /* 包聲明 */
}
每個 annotation 對應 1~N 個 ElementType。使用元註解 @Target 來指定,如果不指定則可以作用於以上所有域中。
3. 兩個特性
兩個特性都是針對 RUNNTIME 保留策略而言的。
3.1 傳遞性
所謂傳遞性,並非語言級特級,但在 spring 中大量應用。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Indexed
public @interface Component {
String value() default "";
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
@AliasFor(annotation = Component.class)
String value() default "";
boolean proxyBeanMethods() default true;
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import({WebMvcSecurityConfiguration.class})
@EnableGlobalAuthentication
@Configuration
@Deprecated
public @interface EnableWebMvcSecurity {
}
如上:@EnableWebMvcSecurity 被 @Configuration 標註; @Configuration 又被 @Component 標註。
所謂傳遞性其實是通過遞歸解析出來的。
示例:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
public @interface FirstLevel {
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@FirstLevel
public @interface SecondLevel {
}
@SecondLevel
public class TransitivityTest {
public static void main(String[] args) {
Set<Annotation> set = new HashSet<>();
Set<Annotation> allAnnotation = getAllAnnotation(TransitivityTest.class, set);
for (Annotation annotation : allAnnotation) {
System.out.println(annotation.annotationType().getName());
}
}
public static Set<Annotation> getAllAnnotation(AnnotatedElement element, Set<Annotation> set) {
// 獲取當前元素上的 annotations
Annotation[] declaredAnnotations = element.getDeclaredAnnotations();
for (Annotation annotation : declaredAnnotations) {
// 找出 非內置的 annotation, 進行遞歸解析
if (annotation != null && annotation.annotationType() != null && !annotation.annotationType().getName().startsWith("java.lang.annotation") && set.add(annotation)) { // visited性能優化用。
getAllAnnotation(annotation.annotationType(), set);
}
}
return set;
}
}
輸出結果:
com.yjh.study.annotation.SecondLevel
com.yjh.study.annotation.FirstLevel
3.2 繼承性
繼承性指的是:當交類 Parent 被 Annotation 標註後,其子類不用再顯示用 Annotation 標註,但是可以使用 Annotation 所帶來的特性。
繼承性只有 Annotation 被 @Inherited 標註後,才能生效。
示例:
沒有使用 @Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@FirstLevel
//@Inherited
public @interface SecondLevel {
}
public class ExtendTest {
@SecondLevel
private static class Parent {
}
private static class Child extends Parent {
}
public static void main(String[] args) {
Class<Parent> parentClass = Parent.class;
System.out.println("父類被標註 :" + parentClass.isAnnotationPresent(SecondLevel.class));
Class<Child> childClass = Child.class;
System.out.println("子類被標註 :" + childClass.isAnnotationPresent(SecondLevel.class));
}
}
輸出結果:
父類被標註 :true
子類被標註 :false
使用 @Inherited 後
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@FirstLevel
@Inherited
public @interface SecondLevel {
}
public class ExtendTest {
@SecondLevel
private static class Parent {
}
private static class Child extends Parent {
}
public static void main(String[] args) {
Class<Parent> parentClass = Parent.class;
System.out.println("父類被標註 :" + parentClass.isAnnotationPresent(SecondLevel.class));
Class<Child> childClass = Child.class;
System.out.println("子類被標註 :" + childClass.isAnnotationPresent(SecondLevel.class));
}
}
輸出結果:
父類被標註 :true
子類被標註 :true