摘要:所以,我實現了一個稱為的類來應用反射。現在流行的語言大都支持反射。這組內省主要是針對類進行操作的,能夠獲取類的屬性信息。可以看到,通過的內省機制,解決了的最關鍵的問題。在封裝反射的時候,會充分考慮到各種情況。
BeanMap
學習具體的技術工具的好辦法就是些Demo、造輪子。所以,我實現了一個稱為BeanMap的類來應用java反射API。
這個BeanMap的功能是將一個Bean包裝成Map來使用。對調用者來說,是以操作Map的方式來操作BeanMap,
但是,實際上的數據是存儲在被包裝的Bean對象中的。
這種思路類似適配器模式,可以讓你以Map的接口操作Bean對象。
但又有點像“視圖”思想,真正的數據是存儲在Bean對象中的,BeanMap只是對它進行操作的“視圖”。對BeanMap的所有操作都會反映在后面的Bean對象中。
下面是BeanMap的一個使用例子。
@Getter @Setter class Point { private Integer x = 2; private Integer y = 1; }
BeanMap map = new BeanMap(new Point()); map.put("x", 10); map.get("y");
示例中的Point這個Bean類擁有屬性x和y。但是被BeanMap包裝后,它就變成了一個擁有鍵"x"和鍵"y"的Map了。
那這個BeanMap類有什么實際應用呢?哈哈這只是我為了寫反射的DEMO自己設計出來的一個類。
java反射稍微思考下不難發現,BeanMap實現的關鍵點在于,
BeanMap接受外部傳入的鍵,這是一個字符串。之后,它得到找到Bean對象中對應的getter和setter,并操作Bean對象。
將這個要求向通用化的方向分析,也即提供一個與函數匹配的字符串,得到對該函數的引用。
通過反射,就能夠實現上面的要求。
現在流行的語言大都支持反射。反射能夠在運行時得到程序元數據,比如某類的信息。
還能夠根據這些元信息來修改程序狀態或邏輯。
由于反射是在 運行 時得到的信息,那么支持反射的語言也必然要在程序運行時將這些元信息存放在內存某處。
java語言提供了反射API,這里是官方完整的文檔:https://docs.oracle.com/javas... 。
對于反射出來的信息,Java的反射API將其以類的形式包裝提供。
Java的反射機制提供了4個類:
Class 類
Constructor 構造器
Field 屬性
Method 方法
現在,試圖利用反射API,得到一個POJO類的所有屬性名稱。如下:
private Listnames() { Method[] methods = bean.getClass().getMethods(); List result = new ArrayList<>(); for (Method getter : methods) { int mod = getter.getModifiers(); if (getter.getName().startsWith("get") && !Modifier.isPublic(mod)) { String name = getter.getName().substring("key".length()); name = name.substring(0, 1).toLowerCase() + key.substring(1); result.add(name); } } return result; }
上述代碼的思路很簡單,java提供的反射API,能夠得到該類的所有方法列表。按照約定,POJO類的屬性xxx的getter方法都命名為GetXxx。那么,遍歷這個表,找出所有getter,字符串處理下,就得到了所有的屬性。
通過調用對象的getClass()方法得到一個Class對象,也即反射出該對象的Class信息。Class::getMethods函數則是反射出該類的所有方法。
Method::getModifiers得到方法的修飾符信息。它是一個整數,我猜測是用位標記各個修飾符的,處理它需要用到位運算。不過可以使用Modifier這個類的工具方法去處理它。
不過,這里是想實現BeanMap。一種思路是,通過對反射出的列表進行一次處理,除了得到每個屬性的名稱外,還要得到它們的getter和setter。下面是一種粗暴的實現:
private List- fileds() { Method[] fields = bean.getClass().getMethods(); List
- result = new ArrayList<>(); for (Method getter : fields) { if (getter.getName().startsWith("get") && isVaildModifier(getter.getModifiers())) { String setterName = "set" + getter.getName().substring("get".length()); for (Method setter : fields) { if (setter.getName().equals(setterName) && isVaildModifier(setter.getModifiers())) { result.add(new Item(this.bean, getter, setter)); } } } } return result; }
當然,上面的實現思路缺點很多。
首先,是性能問題。上面的代碼雖然能實現功能,但是太暴力了。
雖然一個POJO類的方法頂多幾十個,但是考慮到在具體的實踐中,這些都是作為較為底層的基礎設施,項目中可能會頻繁被業務代碼調用,因此對其進行性能上的分析是有必要的。
其次,上面這段邏輯考慮的也不全面。上面的邏輯是對公開的getter進行進一步的處理的,那么,如果getter是static的呢?
如果getter被native修飾呢?如果是超類所擁有的屬性,那么該如何處理這種情況呢?
為了方便使用,java針對反射API進行了封裝,提供了一組內省API。
這組內省API主要是針對POJO類進行操作的,能夠獲取POJO類的屬性信息。
那么,有了jdk自帶的用于對Bean進行反射的工具后,上面的邏輯既可以簡化了:
private List- fileds() throws IntrospectionException { BeanInfo beanInfo = Introspector.getBeanInfo(List.class); return Stream.of(beanInfo.getPropertyDescriptors()).map((pd) -> { return new Item(this.bean, pd.getReadMethod(), pd.getWriteMethod()); }).collect(Collectors.toList()); }
使用getBeanInfo方法獲取某個Bean類的內省信息,這些信息封裝在BeanInfo對象中。
PropertyDescriptor是對Bean類中的一個屬性的封裝,通過它可以獲取該屬性的名稱、getter方法、setter方法等信息。
beanInfo::getPropertyDescriptors獲取Bean類的所有的PropertyDescriptor。
可以看到,通過java的內省機制,解決了BeanMap的最關鍵的問題。而且,使用java自帶的內省機制比自己通過反射API處理有以下好處:
內省API基于反射API進行的封裝,使用更高層次的接口,當然更省心,開發效率更高。
jdk在封裝反射API的時候,會充分考慮到各種情況。如考慮到繼承這一問題,Introspector::getBeanInfo函數可接收第二個參數,只反射出繼承鏈中該類到第二個參數指定類之間類的屬性。
內省API也充分考慮到了性能,其中擁有緩存機制,以提升性能。
最后解決了最關鍵的邏輯后,剩下的部分,就是對Map接口進行實現,填充一些封裝目的的代碼。在這里,我將核心的邏輯放在BeanMapImpl類中,而BeanMap僅僅負責實現Map接口,相關操作轉發到BeanMapImpl相應的方法中實現。這樣顯得程序結構更為清晰:
https://github.com/frapples/j...
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/71449.html
近期在維護公司項目的時候遇到一個問題,因為實體類中的 set 方法涉及到了業務邏輯,因此在給對象賦值的過程中不能夠使用 set 方法,為了實現功能,所以采用了反射的機制給對象屬性賦值,借此機會也了解了反射的一些具體用法和使用場景,分以下兩點對反射進行分析: 反射的優勢和劣勢 反射的應用場景 反射的優勢和劣勢 ??個人理解,反射機制實際上就是上帝模式,如果說方法的調用是 Java 正確的打開方式...
摘要:關于它的數據轉換使用了如下兩種機制隸屬于規范。這種類中的方法主要用于訪問私有的字段,且方法名符合某種命名規則。如果在兩個模塊之間傳遞信息,可以將信息封裝進中,這種對象稱為值對象,或。 每篇一句 千古以來要飯的沒有要早飯的,知道為什么嗎? 相關閱讀 【小家Spring】聊聊Spring中的數據轉換:Converter、ConversionService、TypeConverter、Pro...
摘要:從而能夠進一步深入了解框架。至此我們框架開發完成。雖然說閱讀源碼是了解框架的最終手段。但是框架作為一個生產框架,為了保證通用和穩定,源碼必定是高度抽象,且處理大量細節。下一篇文章應該會是徒手擼框架實現。 原文地址:https://www.xilidou.com/2018/... Spring 作為 J2ee 開發事實上的標準,是每個Java開發人員都需要了解的框架。但是Spring 的...
摘要:從層層委托的依賴關系可以看出,的依賴注入給屬性賦值是層層委托的最終給了內省機制,這是框架設計精妙處之一。當然分享到你的朋友圈讓更多小伙伴看到也是被作者本人許可的若對技術內容感興趣可以加入群交流高工架構師群。 每篇一句 具備了技術深度,遇到問題可以快速定位并從根本上解決。有了技術深度之后,學習其它技術可以更快,再深入其它技術也就不會害怕 相關閱讀 【小家Spring】聊聊Spring中的...
閱讀 2085·2023-04-25 19:15
閱讀 2258·2021-11-23 09:51
閱讀 1267·2021-11-17 09:33
閱讀 2169·2021-08-26 14:15
閱讀 2483·2019-08-30 15:54
閱讀 1585·2019-08-30 15:54
閱讀 2172·2019-08-30 12:50
閱讀 1135·2019-08-29 17:08