獲取未知類型對象的屬性通常有兩種方式:
一是通過自定義注解的方式,通過獲取被注解的屬性從而獲取屬性的值,這種方式也是Spring參數注入的重要實現手段
二是通過反射獲取屬性的名稱,通過屬性名從而獲取屬性,這種方式在開發時是比較簡便易實現的。
一、關於注解
1、自定義注解
首先定義一個@interface類型的注解接口
@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface ClassBeanId {
}
元注解的作用就是負責注解其他注解。
1.@Target,
2.@Retention,
3.@Documented,
4.@Inherited 這些類型和它們所支持的類在java.lang.annotation包中可以找到。下面我們看一下每個元注解的作用和相應分參數的使用說明。
@Target說明了Annotation所修飾的對象范圍:Annotation可被用於 packages、types(類、接口、枚舉、Annotation類型)、類型成員(方法、構造方法、成員變量、枚舉值)、方法參數和本地變量(如循環變量、catch參數)。在Annotation類型的聲明中使用了target可更加明晰其修飾的目標。
作用:用於描述注解的使用范圍(即:被描述的注解可以用在什么地方)
取值(ElementType)有:
1.CONSTRUCTOR:用於描述構造器
2.FIELD:用於描述域
3.LOCAL_VARIABLE:用於描述局部變量
4.METHOD:用於描述方法
5.PACKAGE:用於描述包
6.PARAMETER:用於描述參數
7.TYPE:用於描述類、接口(包括注解類型) 或enum聲明
@Retention定義了該Annotation被保留的時間長短:某些Annotation僅出現在源代碼中,而被編譯器丟棄;而另一些卻被編譯在class文件中;編譯在class文件中的Annotation可能會被虛擬機忽略,而另一些在class被裝載時將被讀取(請注意並不影響class的執行,因為Annotation與class在使用上是被分離的)。使用這個meta-Annotation可以對 Annotation的“生命周期”限制。
作用:表示需要在什么級別保存該注釋信息,用於描述注解的生命周期(即:被描述的注解在什么范圍內有效)
取值(RetentionPoicy)有:
1.SOURCE:在源文件中有效(即源文件保留)
2.CLASS:在class文件中有效(即class保留)
3.RUNTIME:在運行時有效(即運行時保留)
注:注解的的RetentionPolicy的屬性值是RUTIME,這樣注解處理器可以通過反射,獲取到該注解的屬性值,從而去做一些運行時的邏輯處理
Annotation類型里面的參數該怎么設定:
第一,只能用public或默認(default)這兩個訪問權修飾.例如,String value();這里把方法設為defaul默認類型;
第二,參數成員只能用基本類型byte,short,char,int,long,float,double,boolean八種基本數據類型和 String,Enum,Class,annotations等數據類型,以及這一些類型的數組.例如,String value();這里的參數成員就為String;
第三,如果只有一個參數成員,最好把參數名稱設為"value",后加小括號。
例如:
示例1:
自定義一個注解:
@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface ClassBeanId { public int id() default -1; }
使用這個注解:
public class ClassBean { @ClassBeanId(id = 20) //使用已定義的注解默認值為20。如果需要賦值,就需要用反射將id的值取出來並賦給當前類的id private int id; public int getId() { return id; } public void setId(int id) { this.id = id; } @Override public String toString() { return "ClassBean [id=" + id + "]"; } }
定義獲取該注解下的屬性
public static <T> void getData(T data) throws IllegalArgumentException, IllegalAccessException{ int id = 0; Class clazz = data.getClass(); Field[] fields = clazz.getDeclaredFields(); for(Field field : fields){ if(field.getAnnotation(ClassBeanId.class) != null){ field.setAccessible(true); id = field.getInt(data); } System.out.println("id : "+id); } }
將對象傳入該方法中,通過反射判斷該對象屬性是否添加了注解,從而獲取該對象的id屬性。
若要實現類似Spring框架中的參數注入,則需要重新定義一組方法,將注解中的參數傳入對象之中,方法如下:
public static <T> void getDataFromAnnotation(T data) throws IllegalArgumentException, IllegalAccessException{ int id = 0; Class clazz = data.getClass(); Field[] fields = clazz.getDeclaredFields(); for(Field field : fields){ if(field.getAnnotation(ClassBeanId.class) != null){ //判斷該屬性是否被注解 field.setAccessible(true); ClassBeanId classBeanId = field.getAnnotation(ClassBeanId.class); //實例該注解 field.setInt(data, classBeanId.id()); //獲取注解的值並賦值給傳入對象 } } }
雖然並非嚴格的Spring框架的實現機制,但是原理是相同的。
二、關於反射
通過反射獲取屬性的名稱,並對比屬性命名就可以獲取未知類型對象中某些屬性值。這是一種比較常見的做法。
public static <T> void getDataField(T data) throws IllegalArgumentException, IllegalAccessException{ Class clazz = data.getClass(); Field[] fields = clazz.getDeclaredFields(); for(Field field : fields){ String name = field.getName(); if(name.equals("id")){ field.setAccessible(true); field.setInt(data,29); } } }