<h4>前序:</h4>
很開心下了班以后坐在電腦前面寫這篇文章。注解Annotation我們常常見到jdk中的@override、@Deprecated、@SuppressWarnings 還有我本人認為非常優秀的開源項目spring中的IOC的@controller、@autowired和AOP的@Around @Before @After還有hibernate等我就不都枚舉了。既然用的地方這么多所以我們就還是有必要揭開它的面紗看看它到底是怎么玩的。
<h4>1、屬性全解</h4>
Java中提供了四種元注解,專門負責注解其他的注解,分別如下:
<pre>@Retention @Target @Documented @Inheried </pre>
先拿@override 的源碼來作為一個切入點:
<pre>
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
</pre>
@Target:說明了Annotation所修飾的對象范圍,取值在一個枚舉ElementType中,查看源碼(局限于1.7。since1.8以后又新增了2個)
<pre>
public enum ElementType {
TYPE, 用于描述類、接口(包括注解類型) 或enum聲明
FIELD, 成員變量、對象、屬性(包括enum實例)
METHOD, 方法聲明
PARAMETER, 參數聲明
CONSTRUCTOR, 構造器聲明
LOCAL_VARIABLE, 局部變量聲明
ANNOTATION_TYPE, 元注解,有興趣的可以看看@Retention、@Target、@Documented、@Inheried的@Target(ElementType.ANNOTATION_TYPE)都是如此
PACKAGE 包聲明
}
</pre>
@Retention:表示需要在什么級別保存該注釋信息(生命周期)。可選的RetentionPoicy參數包括:
<pre>
public enum RetentionPolicy {
SOURCE, 停留在java源文件,編譯器被丟掉
CLASS, 停留在class文件中,但會被JVM丟棄(默認)
RUNTIME 內存中的字節碼,JVM將在運行時也保留注解,因此可以通過反射機制讀取注解的信息,所以我們自己開發注解的話一般都是RUNTIME,這樣的話就可以通過注解來搞搞
}
</pre>
@Documented:一個簡單的Annotations標記注解,表示是否將注解信息添加在java文檔中。這個我們實際使用的會相對很少,因為我們大多會用在功能實現上。
@Inherited:指定Annotation具有繼承性,上個例子:
<pre>
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface InheritedDemo {
}
//父類使用了Inherited注解
@InheritedDemo
public class Father {
}
//子類驗證對于InheritedDemo注解是否有傳遞性
public class Child extends Father {
public static void main(String[] args) {
//輸出為true
System.out.println(Child.class.isAnnotationPresent(InheritedDemo.class));
}
}
</pre>
<h4>2、編寫自己的注解</h4>
記得最早期的時候在一家金融公司有用到注解的一個場景是,定義類中的每個屬性的名稱、類型及描述,好吧我們就開始著手寫代碼吧:
<pre>
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface FieldDesc {
//定義數據類型枚舉
enum DataType{String,Integer,Long,Boolean,Double};
//名稱
String name() ;
//數據類型
DataType dataType();
//中文描述
String desc() default "";
}
</pre>
<pre>
public class AnnationStudent {
@FieldDesc(name = "id", dataType = DataType.Integer, desc = "id")
private int id;
@FieldDesc(name = "name", dataType = DataType.String, desc = "名稱")
private String name;
@FieldDesc(name = "age", dataType = DataType.Integer, desc = "年齡")
private int age;
@FieldDesc(name = "telNo", dataType = DataType.String, desc = "手機號碼")
private String telNo;
public static void main(String[] args) {
Field[] fieldList = AnnationStudent.class.getDeclaredFields();
if (ArrayUtils.isNotEmpty(fieldList)) {
for (Field field : fieldList) {
Annotation[] annotationList = field.getAnnotations();
if (ArrayUtils.isNotEmpty(annotationList))
for (Annotation annotation : annotationList) {
if (annotation instanceof FieldDesc) {
FieldDesc desc = (FieldDesc) annotation;
System.out.println(desc.name() + " ### " + desc.dataType() + " ### " + desc.desc());
}
}
}
}
}
}
輸出結果為:
id ### Integer ### id
name ### String ### 名稱
age ### Integer ### 年齡
telNo ### String ### 手機號碼
</pre>
這應該是一個完整的注解過程了吧。還有一個小技巧就是當我們給注解的屬性賦值時,如果里面有個value屬性,我們使用注解時可以不指定名稱,自動會指定到value賦值:
<pre>
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Test {
String value() default "";
String name() default "";
}
@Test("dadawd")
public class TE { <b>編譯通過,如果Test 沒有叫value的屬性時編譯會報錯</b>
}
</pre>
這點在spring中的注解里面很常見。
<h4>3、談談注解在類似hibernate等框架中的應用</h4>
也許不一定是hibernate,但它們定義pojo實體的實現思想是一樣的:
<pre>
@Entity
@Database(name="TestDB")
@Table(name="test_table")
public class TestEntity {
@Id
@Column(name="ID")
@GeneratedValue(strategy = GenerationType.AUTO)
@Type(value=Types.BIGINT)
private Long iD;</br>
@Column(name="Name")
@Type(value=Types.String)
private Long name;</br>
@Column(name="SourceType")
@Type(value=Types.INTEGER)
private Integer sourceType;</br>
public Long getiD() {
return iD;
}
public void setiD(Long iD) {
this.iD = iD;
}
public Long getName() {
return name;
}
public void setName(Long name) {
this.name = name;
}
public Integer getSourceType() {
return sourceType;
}
public void setSourceType(Integer sourceType) {
this.sourceType = sourceType;
}
}
</pre>
如何使用定義的@Database、@Table呢?
<pre>
//<b>獲取數據庫名</b>
public String getDatabaseName() {
Database db = clazz.getAnnotation(Database.class);
if (db != null && db.name() != null)
return db.name();
throw new RuntimeException("The entity must configure Database annotation.");
}
//<b>獲取表名</b>
public String getTableName() {
Table table = clazz.getAnnotation(Table.class);
if (table != null && table.name() != null)
return table.name();
Entity entity = clazz.getAnnotation(Entity.class);
if ( entity != null && (!entity.name().isEmpty()) )
return entity.name();
return clazz.getSimpleName();
}
</pre>
這就是注解的使用所在。
<h4>4、結束語</h4>
我所理解的注解就是打標簽下定義,比如我們常說某人是屌絲、高富帥、白富美,當知道某人是高富帥你又能反向推斷出來是長的高、有錢、又帥這么一類人。