Java開發反射機制的實戰經驗總結
前言
我在實際項目當中有經常用到反射機制,故而將學會的反射用法做一些匯總筆記,當做以後復盤所用。
存在這樣一個類:
package com.example.demo; import com.alibaba.fastjson.annotation.JSONField; public class User { private String name; @Value( value ="age_a") private String age; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getAge() { return age; } public void setAge(String age) { this.age = age; } }
一、創建Class的三種方式
1 – Class clazz = Class.forName(“com.example.demo.User”);
註意一點,這裡的forName(“xxx”)的類名需要全名,且為接口或類,否則加載不瞭。
2 – User user = new User();
Class clazz2 = user.getClass();
3 – Class clazz3 = User.class;
以上三種方式,都可以獲取到類User的Class對象,通過Class,即可以開始玩反射瞭。
二、反射獲取類的所有屬性和屬性類型
Class clazz = User.class; Field[] fields = clazz.getDeclaredFields(); for (Field field : fields) { System.out.println("屬性名:"+field.getName()); System.out.println("屬性的類型:"+field.getGenericType().getTypeName()); }
打印輸出User的屬性和屬性類型——
屬性名:name
屬性的類型:java.lang.String
屬性名:age
屬性的類型:java.lang.String
利用反射獲取到類的字段屬性後,是不是可以利用反射來創建一個對象呢?答案是肯定的。
例如,可以類似下面代碼,通過反射得到的字段屬性,進而創建一個對象。
Map<String,Object> fileds = new HashMap<>(); fileds.put("name","張三"); fileds.put("age","10"); Object o = User.class.newInstance(); Field[] fields = o.getClass().getDeclaredFields(); for (Field field : fields) { //設置後可用反射訪問訪問私有變量 field.setAccessible(true); //通過反射給屬性賦值 field.set(o,fileds.get(field.getName())); } User user1 = (User) o; System.out.println(user1.toString());
什麼場景下可能需要這樣做的呢?像一些內部數據與外部數據字段的映射,就可以通過類似的字段反射方式,將源數據映射給目標數據,進而得到可以插入數據庫的目標對象。
三、反射動態修改類屬性的註解值
註意一點,我們在設置User類時,對其中一個字段加瞭註解:@Value( value =”age_a”)。這是一種設置值的註解,既然是設置值,是否還可以在代碼運行過程中,根據不同情況來動態修改呢?
字段上的註解,其實都存放在一個memberValues屬性裡,這是一個map,可以這樣來獲取——
Field[] fields = User.class.getDeclaredFields(); for (Field field : fields) { //設置後可用反射訪問訪問私有變量 if ("age".equals(field.getName() )){ field.setAccessible(true); //獲取 annotation 這個代理實例所持有的 InvocationHandler InvocationHandler invocationHandler = Proxy.getInvocationHandler(field.getAnnotation(Value.class)); // 獲取 InvocationHandler 的 memberValues 字段 Field memberValues = invocationHandler.getClass().getDeclaredField("memberValues"); memberValues.setAccessible(true); Map<String, Object> values = (Map<String, Object>) memberValues.get(invocationHandler); System.out.println(values); } }
debug打斷點,可以看到——
這個Map<String,Object>存儲的是該@註解裡的所有屬性值,這裡,@Value隻有一個value屬性——
public @interface Value { String value(); }
若把它換成類似@JSONField(name=”age_a”),把上邊的代碼稍微修改下,如:
Field[] fields = User.class.getDeclaredFields(); for (Field field : fields) { if ("age".equals(field.getName() )){ field.setAccessible(true); InvocationHandler invocationHandler = Proxy.getInvocationHandler(field.getAnnotation(JSONField.class)); ...... } }
@JSONField註解的內部屬性有如下方式——
再運行剛剛的代碼,可以看到,這裡Map<String,Object>獲取存儲到的,便是這個註解裡所有的屬性與對應的屬性值。
到瞭這一步,回到先前上邊的問題,若要動態改變這個註解的值,怎麼處理呢?
其實,很簡單,隻需要直接進行值設置就可以瞭,例如——
InvocationHandler invocationHandler = Proxy.getInvocationHandler(field.getAnnotation(Value.class)); Field memberValues = invocationHandler.getClass().getDeclaredField("memberValues"); memberValues.setAccessible(true); Map<String, Object> values = (Map<String, Object>) memberValues.get(invocationHandler); values.put("value","new_age"); memberValues.setAccessible(false);
隻是,註意一點是,這裡的key需要對應上註解裡是屬性值。
四、反射獲取類的方法及調用方式
Object o=User.class.newInstance(); //通過反射獲取到User的setAge方法,後面的String.class表示這個setAge方法的參數類型,若有多個,則按順序列出 //同時,若為其他類型,如List,Long,則為List.class,Long.class Method m = (Method) o.getClass().getMethod("setAge",String.class); m.invoke(o,"name"); User user = (User) o; System.out.println(user);
打印可見,age已為name,說明setAge調用成功瞭。
這類使用場景,在代理當中出現比較多。
最後,通過反射實現一個Map轉成對象的封裝工具——
public Object MapToObject(Object object,Map<String, Object> map) throws IllegalAccessException { Class cla = object.getClass(); Field[] fields = cla.getDeclaredFields(); for(Field field:fields){ field.setAccessible(true); if("serialVersionUID".equals(field.getName()))continue; if(map.get(field.getName())!=null) { Object value=map.get(field.getName()); value=convertValType(value,field.getType()); field.set(object, value); } } return object; } private static Object convertValType(Object value, Class<?> fieldTypeClass) { Object o = null; if (Long.class.getName().equals(fieldTypeClass.getName()) || long.class.getName().equals(fieldTypeClass.getName())) { o = Long.parseLong(value.toString()); } else if (Integer.class.getName().equals(fieldTypeClass.getName()) || int.class.getName().equals(fieldTypeClass.getName())) { o = Integer.parseInt(value.toString()); } else if (Float.class.getName().equals(fieldTypeClass.getName()) || float.class.getName().equals(fieldTypeClass.getName())) { o = Float.parseFloat(value.toString()); } else if (Double.class.getName().equals(fieldTypeClass.getName()) || double.class.getName().equals(fieldTypeClass.getName())) { o = Double.parseDouble(value.toString()); } else { retVal = o; } return retVal; }
總結
到此這篇關於Java反射機制的文章就介紹到這瞭,更多相關Java開發反射機制內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!