最近作項目時發現Spring框架運用了大量自定義註解,因此回過頭來有必要深刻了解和整理關於註解的知識。java
註解是java在1.5版本引入的新特性,隨後一些開源組件也引入此項特性,如Spring,Hibernate,Mybatis。JPA規範更是全註解式程序員
的。如下用到的示例代碼均來自於<<Java編程思想第四版>>。編程
註解也被稱爲元數據。咱們能夠在代碼中添加信息,而後能夠在須要的時候使用這些數據。經過在源代碼級別保存註解,使得代碼變得整潔便於維護。註解是語言級別的概念,享有編譯期的類型檢查保護。使用擴展的annotationAPI或者字節碼工具類庫,程序員擁有對源代碼以及字節碼強大的檢查和操做能力。
數組
1.基本語法
框架
import java.lang.annotation.*; @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface Test {}
沒有元素的註解稱爲標記註解(marker annotation),如以上定義的@Test,下面定義了一個包含兩個元素的註解:
ide
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface UseCase { public int id(); public String description() default "no description"; }
2.元註解工具
元註解負責註解其餘註解。Java內置了四種元註解:
測試
@Target | 表示該註解能夠用於什麼地方。可能的ElementType參數包括:ui CONSTRUCTOR:構造器的聲明spa FIELD:域聲明(包括enum實例) LOCAL_VARIABLE:局部變量聲明 METHOD:方法聲明 PACKAGE:包生明VM PARAMETER:參數聲明 TYPE:類、接口(包括註解類型)或enum聲明 |
@Retention | 表示須要在生麼級別保存該註解信息。可選的RetentionPolicy參數包括: SOURCE:註解將被編譯器丟棄。 CLASS:註解在class文件中可用,但會被VM丟棄。 RUNTIME: VM將在運行期也保留註解,所以能夠經過反射機制讀取註解的信息。 |
@Documented | 將此註解包含在Javadoc中。 |
@Inherited | 容許子類繼承父類中的註解。 |
3.基本註解
Java同時內置了三種基本註解:
@Override 表示當前的方法定義將覆蓋超類中的方法
@Deprecated 若是使用了註解爲它的元素,編譯器會發出警告信息
@SuppressWarnings 關閉編譯器警告信息
4.註解中的元素
註解中的元素可用類型有:全部基本類型(int,float,boolean等),不容許包裝類型,String,Class,enum,Annotation,以上類型的數組
默認值限制
註解中的元素不能有不肯定的值。要麼具備默認值,要麼在使用註解時提供元素的值。
對於非基本類型的元素,不管是在使用註解時,仍是在定義註解並設定元素默認值時,都不能爲null。
註解不支持繼承: 即不能使用關鍵字extends來繼承某個@interface。
下面的PasswordUtils類中有三個方法使用了@UseCase
import java.util.List; public class PasswordUtils { @UseCase(id = 47, description = "Passwords must contain at least one numeric") public boolean validatePassword(String password) { return (password.matches("\\w*\\d\\w*")); } @UseCase(id = 48) public String encryptPassword(String password) { return new StringBuilder(password).reverse().toString(); } @UseCase(id = 49, description = "New passwords can't equal previously used ones") public boolean checkForNewPassword( List<String> prevPasswords, String password) { return !prevPasswords.contains(password); } }
下面是一個簡單的註解處理器,它讀取PassWordUtils類,並使用反射機制查找@UseCase標記。提供一組id值用於測試,它會列出找到的用例和缺失的用例。
import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collections; import java.util.List; public class UseCaseTracker { public static void trackUseCases(List<Integer> useCases, Class<?> cl) { for(Method m : cl.getDeclaredMethods()) { UseCase uc = m.getAnnotation(UseCase.class); if(uc != null) { System.out.println("Found Use Case:" + uc.id() + " " + uc.description()); useCases.remove(new Integer(uc.id())); } } for(int i : useCases) { System.out.println("Warning: Missing use case-" + i); } } public static void main(String[] args) { List<Integer> useCases = new ArrayList<Integer>(); Collections.addAll(useCases, 47, 48, 49, 50); trackUseCases(useCases, PasswordUtils.class); } } /* Output: Found Use Case:47 Passwords must contain at least one numeric Found Use Case:48 no description Found Use Case:49 New passwords can't equal previously used ones Warning: Missing use case-50 */