您好,登錄后才能下訂單哦!
本篇內容介紹了“如何用Java反射提高開發效率的框架”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
基于spring的aop,定義一個注解做為切點,注釋在service層的分頁查詢方法上,聲明方法的返回結果POJO對象的哪個字段需要應用默認值規則。
通過aop在方法執行完成之后,給返回結果應用配置的默認值規則,再返回給前端,如下圖所示。
如果一次查詢返回含有20個元素的數組List<R>,要對每個元素R都反射查找一次Field,判斷該Field的值,如果為空則賦值為默認值,那就是重復做反射獲取Field,亂用反射,延長接口的響應時間。
對于默認值規則應用這個例子,我們可以有很好的做法,拿到R類中的name字段,即Field,只需要反射一次。就是在應用啟動時,執行包掃描獲取到R的Class對象,執行反射獲取R類型中所有的Field,然后使用Map<Class,Map<字段名,Field>>緩存在內存中,這樣用到的時候就能很快拿到。雖然會消耗點內存,但這是可以忽略的,因為整個系統應用到默認值規則的類就沒幾個。
我認為我設計的這套默認值規則是最優的方案。
1、使用aop實現代碼的解耦,默認值規則的應用邏輯不侵入業務代碼,隨時可對這個功能進行插拔。
2、同時,利用反射可以實現添加默認值規則不需要改動任何代碼,隨時添加隨時用,隨時修改隨時生效。
3、再者,由于默認值規則存在的很少改動這個特性,使用內存緩存,有效減少數據庫的查詢次數,也是對性能的一個提升。
…………… 利用反射提高工作效率 …………
業務場景簡介
今天,我繼續分享另一種反射的使用場景,包括如何設計實現。以此,回答本篇文章開頭的問題,怎樣才算用到刀刃上。
先了解點簡單的業務場景,這段內容主要介紹使用場景,看不懂沒關系,大概過一遍就行。
本次優化的是一個定時任務服務,它要做的就是調用很多第三方的接口拉取數據,并轉為平臺統一結構的數據,如將A、B、C…轉為P,存入到數據庫中。
假如我有一個產品,需要做推廣,那我就是上游廣告主,而我想投放到百度搜索、微信朋友圈展示,那么百度和微信就是下游渠道。但中間可能經過幾層渠道,而中間的每一個渠道對于下游來說都是一個廣告主,也都可以稱為網盟平臺,大家可能會對百度網盟廣告平臺有所耳聞。
那么作為一個網盟平臺,需要整合上游的Offer,批給下游去展示。整合上游的 Offer就是調用每個上游提供的api。但每個上游所返回的json數據都不一樣,字段名稱也不一樣。那就需要為每個api實現一個解析映射的方法。這就是重復勞動。如何減少這種重復勞動,就是提高工作效率。
簡單一句話,要做什么
與Mybatis實現的將查詢結果映射成pojo對象的過程相似。如果還覺得這段內容抽象,那就直接跳過吧,我改了好幾次,發現都好難表述清楚啊。
解析映射,就是將json數據解析后得到的java對象Response,之后,再獲取到Response中存放廣告信息數組(List)的字段的值,再遍歷該List,將每個元素由類型A轉為平臺統一的類型對象B,這個過程就是A to B,比如。
class A{
String a_name;
}
Class B{
String name;
}
實現 A==>to ==> B 就是:
A a;
B b;
b.setName(a.getA_name());
很顯然,直接使用反射copy字段的值是沒有用的,一是字段名不同,二是可能字段的類型也不同,三是還可能是下面這樣情況。
class A{
C c;
public static class C{
String c_name;
}
}
class B{
String name;
}
實現 A==> to ==> B 就是:
A a;
B b;
b.setName(a.getC().get C_name());
如何實現自動映射
如果可以添加一個注解,在注解中聲明映射規則,比如其中一條:class A中的name字段對應class B中的pkgName字段。
如果能根據注解聲明的一個個映射規則,完成自動映射,就可以不用每次都寫這些重復代碼了。是的,我要實現的就是這樣一個功能。
P.S:最后我還加入了插件功能,滿足一些需要做特殊處理的需求。
類結構樹的定義
知道我為啥叫它結構樹嗎?二叉樹不是二叉樹,n差數不是n叉樹,我不懂怎么叫,難道這是我自己發明的?哈哈。
以一個例子來講,不那么抽象。如圖,實現自動從TestResponse對象中,拿到List<RowsetBean> rowset的值。因為根據對接文檔,我知道這個字段正是廣告Offer集合,一個item代表一個廣告Offer。
要實現根據注解自動將TestResponse對象解析生成List<ProdCampaign>集合,那肯定要對TestResponse對象的結構了如指掌(下文將這一類由調用API返回的對象稱為Response對象)。所以需要將TestResponse類中的字段信息映射成一顆樹。
先不講那么多,來看下最終實現的效果,使用@ProdCampaignMapRules注解聲明映射規則。
正如上圖所示,我在RowsetBean類添加@ProdCampaignMapRules注解,意味著會自動將類型為RowsetBean的對象,映射生成ProdCampaign對象。
認真看,你會發現,TestRsponse類上面還有個注解@MapRulesComponent,它的作用只是聲明這個類支持使用自動映射。我在程序啟動時,會掃描所有被該注解注釋的Response,將這些Response解析成一棵棵類結構樹,緩存在內存中。
實現自動解析API返回的廣告Offer數組,映射生成平臺統一的ProdCampaign集合的步驟,繼續以上圖中的例子為例。
第一步:從類結構樹中,找到有@ProdCampaignMapRules的ClassNode,再得到這個ClassNode 所屬的FieldNode。找到的FieldNode就是Response中的List<RowsetBean> rowset字段。獲取rowset的值,這就得到了廣告Offer集合。
第二步:New一個List<ProdCampaign>,遍歷廣告Offer集合rowset,將每個RowsetBean類型的元素轉為平臺統一的ProdCampaign對象,并添加到List<ProdCampaign>集合中。
第三步:將每個RowsetBean轉為ProdCampaign的過程當中,需要new一個ProdCampaign對象,然后給這個對象里面的字段賦值。值從哪來?從RowsetBean對象中找到與之對應的字段,并獲取值,為ProdCampaign對象賦值。很好理解,不就是屬性拷貝。
第四步:如果是多層映射,如例子中的“offer.name"映射規則,就是要先獲取到RowsetBean對象中的offer字段,類型為OfferBean。再繼續下一層name的映射,name的映射就是獲取OfferBean的name字段。這就是一個兩層映射的例子。
我定義的結構體看起來很復雜。RuleMetaData,就是完成包掃描后每個Class生成的類結構樹。類中的字段信息用FieldNode存放,如果該字段的類型是非基本數據類型,則FieldNode的child是該字段所對應類型的ClassNode數據。
整棵結構樹就是一個RuleMetaData,根節點root肯定是一個ClassNode。非基本數據類型的FieldNode也有一個child指向一個ClassNode。很抽象?沒關系,繼續看,下面會有一張圖,形象的畫出了這棵結構數。
實現包掃描將Class解析成一顆結構樹
在應用啟動時,實現包掃描,找出所有被@MapRulesComponent注釋的類,解析生成結構樹RuleMetaData,舉個例子。
如圖,已經畫得很形象了,這棵樹的結構應該已經在你的腦子里。
結構樹的root節點,是該Response的第一個字段,存儲了這個字段的字段名和反射獲取到的Field,所屬類TestResponse。下個字段就是節點的nextField字段,由于該字段的類型是基本數據類型int,所以child的值為null。
到data字段的時候,由于其類型是非基本數據類型,而是一個內部類DtaBean,需要解析成一個ClassNode,同時要繼續反射取得DataBean的所有字段的FieldNode節點鏈。最后將data節點的child指向這個ClassNode。
在反射DataBean生成FieldNode鏈的時候,處理到rowset字段,發現其是一個List類型,那就獲取這個List的元素的類型,這個反射是支持的,能獲取到。
然后再繼續對RowsetBean繼續以上步驟。還有一點,就是RowsetBean被@ProdCampaignMapRules注解注釋,所以要獲取到@ProdCampaignMapRules注解,賦值給rowset的ClassNode節點的rule。
反射解析Response生成結構數,我是使用隊列加廣度優先遍歷方法。代碼如下圖所示。
包掃描如何實現我就不說了。
根據OfferBean的Class結構樹映射生成ProdCampaign
這一步我只介紹實現思路,每一步的具體實現,整體的實現就不貼代碼了。
1.首先使用jackson解析調用第三方API返回的json字符串,得到Response對象。
2.根據Response的Class從緩存中獲取其類結構樹。使用深度優先搜索,取得被@ProdCampaignMapRules注釋的ClassNode。這個很好判斷,因為在前面將類生成結構樹的步驟,已經取得@ProdCampaignMapRules注解并存放到了ClassNode的rule。
3.獲取到被@ProdCampaignMapRules注釋的ClassNode后,取得該ClassNode所屬的FieldNode。
4.取得該FieldNode后,獲取Field,反射取得值,這個值就是要找的目標,即存放廣告Offer的集合。
5.拿到存放廣告Offer的集合后,遍歷這個集合,將里面的每個元素,映射成平臺統一的存放廣告信息的ProdCampaign。最后就能獲取到ProdCampaign集合。
6.每個元素映射成ProdCampaign對象的過程,才是重點,還是很復雜的。因為需要支持多層映射,就是支持像使用mybatis拼寫sql一樣支持“.”操作,如“offer.geo.country”,就是要取得offer對象的geo字段的值,再獲取geo的country字段的值。有了類結構樹,多級映射并不難,只需要一個while循環即可。
7.對于tracklinkMap,是一個注解數組,類型是@TracklinkMap,用于聲明url需要哪些參數,怎么拼接。
“如何用Java反射提高開發效率的框架”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。