註解(Annotation),也叫元數據。一種代碼級別的說明。它是JDK1.5及之後版本引入的一個特性,與@Retention類、接口、枚舉是在同一個層次。它能夠聲明在包、類、字段、方法、局部變量、方法參數等的前面,用來對這些元素進行說明,註釋,利用發射能夠在運行時改變程序的邏輯。請往下看:java
應該有人會看不懂這句話吧,「元數據」過高大上了,好抽象,雲裏霧裏的,那咱們再看看元數據的定義。數據庫
元數據:爲描述數據的數據數組
綜合理解註解:註解就是Java爲類、接口、方法、字段、局部變量、方法參數能表達更豐富而添加的說明,就好像給它們(類、接口、方法、字段、局部變量、方法參數)加了一個標籤,好比一個方法是被重寫的,那麼Java代碼就是:安全
@Override public void doDispatch(HttpServletRequest req){ }
取一段代碼爲例來講明註解的語法(不要被它嚇到,接下來的篇幅會對他一點點的講解)app
package com.jv.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface MyAnnotation { String value() default ""; String[] name() default {}; }
下面開始一點點的講,最終獲得上面的註解eclipse
1.定義最簡單的註解ide
public @interface MyAnnotation {}
使用@interface就聲明瞭一個註解,而後就能夠在其餘地方使用這個註解,如:函數
package com.jv.annotation; @MyAnnotation public class MyObject { }
雖然這個註解如今尚未任何屬性或者其餘的修飾,但類或者它的元素被該註解修飾了,那麼他就變得不同了。好比在運行時就能夠掃描全部的類並判斷是否是擁有該註解,而後作特殊的操做測試
2.給註解加屬性ui
package com.jv.annotation; public @interface MyAnnotation { String value() default ""; String[] name() default {}; }
給註解加value和name兩個屬性,其中name是一個數組類型的,表示他能夠接受多個String值。當註解有了屬性,那麼它就攜帶了更可能是信息,在使用(經過反射得到註解或者註解的值)的時候就可拿它們作邏輯判斷
注:註解沒有方法
3.給註解添加註解
package com.jv.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface MyAnnotation { String value() default ""; String[] name() default {}; }
註解的註解就是用來講明被修飾的註解在不一樣功能上的限制
好比:@Target限制了註解能修飾那些元素(類、接口、方法、字段、局部變量、方法參數)
@Target是一種元註解,表示能夠用來修飾註解的註解
元註解有@Retention、@Documented、@Target、@Inherited、@Repeatable 5 種
@Retention
Retention 的英文意爲保留期的意思。當 @Retention 應用到一個註解上的時候,它解釋說明了這個註解的的存活時間。
- RetentionPolicy.SOURCE 註解被保留到源碼中,在編譯器工做時它將被丟棄。
- RetentionPolicy.CLASS 註解被保留到Java和編譯後的class文件中,但不會被加載到 JVM 中。
- RetentionPolicy.RUNTIME 註解被保留到Java和編譯後的class文件中,且會被加載到JVM,因此在程序運行時能夠獲取到它們,並作不少邏輯判斷
若是對類加載有興趣的同窗能夠查看Java類加載機制
@Documented
它的做用是可以將被@Documented 修飾的註解記錄到 Javadoc 中
@Target
限定註解能修飾的元素(類、接口、方法、字段、局部變量、方法參數)
- ElementType.ANNOTATION_TYPE 能夠給一個註解進行註解
- ElementType.CONSTRUCTOR 能夠給構造方法進行註解
- ElementType.FIELD 能夠給屬性進行註解
- ElementType.LOCAL_VARIABLE 能夠給局部變量進行註解
- ElementType.METHOD 能夠給方法進行註解
- ElementType.PACKAGE 能夠給一個包進行註解
- ElementType.PARAMETER 能夠給一個方法內的參數進行註解
- ElementType.TYPE 能夠給一個類進行註解,好比類、接口、枚舉
- ElementType.TYPE_PARAMETER 能夠給一個類或者方法的參數聲明進行註解(JDK1.8)
- ElementType.TYPE_USE 能夠給任何使用到類型的地方進行聲明(JDK1.8)
@Inherited
一個註解被@Inherited修飾後,那麼被修飾的註解再去修飾其餘類(A),A的子類B(B沒有被其餘註解修飾)會繼承A的註解
@Inherited @Retention(RetentionPolicy.RUNTIME) @interface MyAnnotation{} @MyAnnotation public class A {} public class B extends A {}
@Repeatable
它是 Java 1.8 新加入的註解,表示能夠重複修飾
@interface Cars { Car[] value(); } @Repeatable(Cars .class) @interface Car{ String value default ""; } @Car("AA") @Car("BB") @Car("CC") public class MyTest{ }
@Car具備一個value屬性,那屬性拿來幹嗎,又是怎麼使用的喃
它和類的成員變量同樣,能夠用來保存在使用註解時指定的值
public @interface MyAnnotation { String value() default ""; String[] name() default {}; }
定義了value 和 name屬性,而且使用default指定它們的默認值,如何使用它們喃?
@MyAnnotation(value="abc",name= {"123","456"}) public class MyObject { }
@Deprecated
用來修飾過期的元素,在使用高版本JDK時常常遇到該註解。編譯時會發出警告,通知開發人員它是已過期的類或者方法
提示子類中的方法是重寫的
忽略編譯器發出的警告,在添加了這個註解,eclipse左邊的小黃感嘆號就會消失
Jdk 1.7 加入的新註解,用於提醒開發者不要用參數作一些不安全的操做,它的存在會阻止編譯器產生 unchecked 這樣的警告,可是運行時程序可能會報錯。
Jdk 1.8 加入的新註解(函數式接口),但願瞭解的同窗能夠看看(強烈推薦)Java8 Lambda表達式
用一個例子來講明屬性或者方法上的註解如何被拿到,如何取得註解的屬性值
注:測試JDK爲1.8
1.定義註解
/* * 指定數據庫名稱 */ @Target({ElementType.TYPE,ElementType.TYPE_USE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface MyTable{ String value() default ""; } /* * 指明JavaBean中某一個屬性爲主鍵,且一個類中該註解只能有一個 */ @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface MyId { public String value() default "id"; } /** * 指定JavaBean中屬性對應的數據庫字段名 * @author jionsvolk * */ @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface MyField { public String value() default ""; }
2.用到Bean
@MyTable("tb_cust") public class Cust implements Serializable{ private static final long serialVersionUID = 1L; @MyId("custId") private Integer custId; @MyField("custName") private String name; private String address; public Integer getCustId() { return custId; } public void setCustId(Integer custId) { this.custId = custId; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public Cust(Integer custId, String name, String address) { super(); this.custId = custId; this.name = name; this.address = address; } public Cust() { super(); } }
public class MyEntry { private String key; private Object value; public boolean isNotEmpty() { if(key == null || key.equals("") || value == null) { return false; }else { return true; } } public String getKey() { return key; } public void setKey(String key) { this.key = key; } public Object getValue() { return value; } public void setValue(Object value) { this.value = value; } }
3.反射提取註解、提取方法、提取屬性
public class DBHelper { private static final String serialFieldName = "serialVersionUID"; public static String selectSql(Object obj) throws Exception{ FieldAnnotationHandler fah = new IdFieldAnnotationHandler(); StringBuilder sb = new StringBuilder(); //1.拿到Class對象 Class cl = obj.getClass(); //2.拿到數據庫表名的註解 MyTable myTable = (MyTable)cl.getAnnotation(MyTable.class); String tblName = myTable.value(); sb.append("select "); //3.獲取對象全部定義的屬性(包含私有屬性) Field[] fields = cl.getDeclaredFields(); //4.循環全部屬性,獲得註解名稱和屬性值 for(Field f : fields) { //4.1 序列化字段無需處理 if(f.getName().equals(serialFieldName)) { continue; } //4.2 拿到屬性上的全部註解 Annotation[] ans = f.getAnnotations(); //4.3 循環處理全部註解,獲得數據庫表的字段名和它對應的值 for(Annotation an : ans) { //使用責任鏈模式來處理每個屬性上的註解(由於我這裏假設只會出現MyId和MyField註解,因此只寫了兩個Handler) MyEntry entry = fah.handler(an , f , obj); if(entry.isNotEmpty()) { sb.append(entry.getKey()+","); break; } } } //5.剔除最後的","號 sb = new StringBuilder(sb.toString().substring(0, sb.toString().lastIndexOf(",")!=-1?sb.toString().lastIndexOf(","):sb.toString().length())); sb.append(" from ").append(tblName); //處理主鍵 MyEntry primaryEntry = ((IdFieldAnnotationHandler)fah).getPrimaryEntry(); if(primaryEntry.isNotEmpty()) { sb.append(" where ").append(primaryEntry.getKey()).append(" = ").append(primaryEntry.getValue()); } return sb.toString(); } }
4.處理屬性和值的Handler
抽象類
public abstract class FieldAnnotationHandler { FieldAnnotationHandler handler; public FieldAnnotationHandler getHandler() { return handler; } public void setHandler(FieldAnnotationHandler handler) { this.handler = handler; } public abstract MyEntry handler(Annotation an,Field f,Object obj) throws Exception; }
主鍵處理Handler
/* * 和其餘的Handler不同,增長了primaryEntry屬性,用於記住主鍵的信息 */ public class IdFieldAnnotationHandler extends FieldAnnotationHandler{ private MyEntry primaryEntry; public MyEntry getPrimaryEntry() { return primaryEntry; } public IdFieldAnnotationHandler(){ this.primaryEntry = new MyEntry(); this.handler = new MyFieldAnnotationHandler(); } @Override public MyEntry handler(Annotation an,Field f,Object obj) throws Exception{ MyEntry entry = new MyEntry(); if(an instanceof MyId) { MyId myId = (MyId)an; primaryEntry.setKey(myId.value()); if(primaryEntry.getKey() !=null && !primaryEntry.getKey().equals("")) { entry.setKey(primaryEntry.getKey()); Method method = obj.getClass().getMethod("get"+f.getName().substring(0, 1).toUpperCase() + f.getName().substring(1), null); entry.setValue(method.invoke(obj, null)); primaryEntry.setValue(entry.getValue()); return entry; }else { entry.setKey(f.getName()); entry.setValue(f.get(obj)); return entry; } }else { return handler.handler(an, f,obj); } } }
字段處理Handler
public class MyFieldAnnotationHandler extends FieldAnnotationHandler{ public MyFieldAnnotationHandler(){ } @Override public MyEntry handler(Annotation an,Field f,Object obj) throws Exception{ MyEntry entry = new MyEntry(); if(an instanceof MyField) { MyField myField = (MyField)an; String columnName = myField.value(); if(columnName != null && !columnName.equals("")) { entry.setKey(columnName); Method method = obj.getClass().getMethod("get"+f.getName().substring(0, 1).toUpperCase() + f.getName().substring(1), null); entry.setValue(method.invoke(obj, null)); return entry; }else { entry.setKey(f.getName()); entry.setValue(f.get(obj)); return entry; } }else { return new MyEntry(); } } }
測試類
public class TestSql { @Test public void test() throws Exception { System.out.println(DBHelper.selectSql((new Cust(123,"Messi","巴塞羅那")))); } }
輸出:
select custId,custName from tb_cust where custId = 123
我並無按照反射的API一一寫代碼測試,有興趣的朋友能夠看中文或者官方的API