從JDK5開始,Java增長對註解的支持,註解能夠在編譯,類加載和運行時被讀取,並執行相應一些定義好的處理。經過註解能夠在不改變原有代碼和邏輯的狀況下進行一些其餘的補充操做。java
在java中系統爲咱們預置了一部分註解,咱們能夠經過這些註解來定義其餘註解的做用和有效範圍等特性。mysql
@Target用於說明Annotation所修飾的對象範圍,所能修飾的範圍都被定義在枚舉類ElementType
中。sql
public enum ElementType { TYPE,//表示能夠用於類,接口,註解或者枚舉定義中 FIELD,//字段 METHOD,//方法(不包括構造方法) PARAMETER,//方法的參數 CONSTRUCTOR,//構造方法上 LOCAL_VARIABLE,//局部變量 ANNOTATION_TYPE,//只能用在註解上 PACKAGE,//做用包上 package-info.java TYPE_PARAMETER,//表示註解能寫在類型變量(泛型參數)的聲明語句中如 List<Integer> list = new @Save ArrayList<>(); TYPE_USE //表示註解能寫在使用類型的任何語句中(聲明語句、泛型和強制轉換語句中的類型)。 }
@Target(ElementType.TYPE_PARAMETER) public @interface Save { } public class Test<@Save T> { List<Integer> list = new @Save ArrayList<>();//僅用於展現能夠用到的地方 }
Retention 定義了該Annotation被保留的時間長短:表示須要在什麼級別保存註解信息,用於描述註解的生命週期(即被描述的註解在什麼範圍內有效),取值被定義在枚舉類RetentionPolicy
中:數據庫
public enum RetentionPolicy { SOURCE,//表示在源代碼時有效,編譯後的文件沒有該註解,通常該類註解僅用於標識如@SuppressWarnings CLASS, //默認行爲 自定義註解若是沒有顯示的聲明則默認爲該行爲 在編譯時不會被拋棄,可是會被虛擬機拋棄 RUNTIME //保留到運行時,能夠經過反射來獲取 通常該類註解會影響系統的運行 }
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Documented { }
從註解定義能夠看到該註解用在註解定義上。框架
@Documented 用於描述其它類型的 annotation 應該被做爲被標註的程序成員的公共 API,因
此能夠被如javadoc之類的工具文檔化。可是實際使用並很少,有其餘更好的替代。ide
@Inherited是一個標記註解,@Inherited表示被其標註的類型是被繼承的。若是一
個使用了@Inherited 修飾的 annotation 類型被用於一個 class,則這個 annotation 將被用於該class 的子類。工具
簡單來講就是在子類中若是想要獲取父類被那些註解修飾,那麼子類能拿到的僅僅是被@Inherited標註過得註解。而其餘沒有使用 @Inherited的註解是沒法再子類獲取的。this
上面介紹的幾種元註解是在咱們進行自定義註解的時候會用到的,而下面咱們介紹幾種平時業務開發會常常使用的註解。代理
@Deprecated用來描述在當前系統中已經被廢棄不推薦使用的類或方法等。日誌
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
若是咱們使用了被@Deprecated標註的類或方法等,在進行編譯的時候會顯示相應的提示信息。
@Override是咱們使用很頻繁的一個註解,因爲重寫的操做僅存在於方法中,因此@Override也只能對方法進行標註。
@Override功能主要是用來校驗當前被標註的方法是否爲重寫方法,平時咱們在繼承抽象類或實現接口時都應使用該註解來標註被重寫的方法。
@SuppressWarnings用於可選擇的抑制編譯器在編譯時產生警告信息。
//做用範圍 @Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@SuppressWarnings可選擇的值有不少:
自定義一個註解及其簡單,使用@interface關鍵字便可完成。同時咱們須要肯定咱們定義的註解使用範圍和其具體用途,根據此來肯定使用元註解的哪些參數來修飾咱們定義的註解。
這裏咱們定義一個@Log註解用於在對數據庫進行增刪改時插入一條日誌進行記錄。因爲該註解須要在運行時對數據庫進行修改,因此很明顯做用有效期爲RUNTIME,做用範圍可使方法也可使類。
@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD, ElementType.TYPE}) public @interface Log { String value();//value的值爲本次操做類型:insert,delete等 }
定義接口並實現:
public interface UserService { @Log(OperationType.INSERT) void save(User user); }
public class LogProxyExt implements InvocationHandler { private Object obj; static Connection connection = null; public static Connection getConnection() throws ClassNotFoundException, SQLException { if(connection == null){ Class.forName("com.mysql.jdbc.Driver"); connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/fuxi?serverTimezone=UTC", "root", "root"); } return connection; } public LogProxyExt(Object obj) { this.obj = obj; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //查找方法上是否存在該註解 Log annotation = method.getAnnotation(Log.class); if(annotation != null){ User user = (User) args[0]; if(user!=null && StringUtils.isNotBlank(user.getName())){ connection = getConnection(); PreparedStatement statement = connection.prepareStatement("insert into log(`log_detail`,`operation`) values(?,?)"); statement.setString(1, user.getName()); statement.setString(2, annotation.value()); statement.executeUpdate(); } } //查找類上是否存在該註解 proxy.getClass().getAnnotation(Log.class); //對類上的註解進行解析 //todo Object invoke = method.invoke(obj, args); return invoke; } public static void main(String[] args) { UserService operat = new UserServiceImpl(); LogProxyExt ext = new LogProxyExt(operat); //這裏不能是具體的類,必須是接口 不然會拋出類轉換異常 UserService proxy = (UserService) Proxy.newProxyInstance(operat.getClass().getClassLoader(), operat.getClass().getInterfaces(), ext); User user = new User(); user.setName("lisi"); proxy.save(user); } }
上述解析經過JDK動態代理進行實現,對UserService接口進行代理,保證在UserService中任意位置使用@Log註解都能完成插入日誌操做。實際使用能夠配合Spring Aop或者攔截器之類的對全局請求進行處理。
註解是一個頗有用的特性,在現有的框架中大多數都已經支持或正在支持註解。使用註解能夠大大提高咱們平時的開發速度,保證代碼的簡潔性。除了框架和JDK自帶的註解以外,咱們也能夠經過自定義註解來對咱們的代碼功能進行檢查和加強。