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