如何用Java反射提高开发效率的框架

本篇内容介绍了“如何用Java反射提高开发效率的框架”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

基于spring的aop,定义一个注解做为切点,注释在service层的分页查询方法上,声明方法的返回结果POJO对象的哪个字段需要应用默认值规则。

通过aop在方法执行完成之后,给返回结果应用配置的默认值规则,再返回给前端,如下图所示。


 

如何用Java反射提高开发效率的框架


 

如果一次查询返回含有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,存入到数据库中。

假如我有一个产品,需要做推广,那我就是上游广告主,而我想投放到百度搜索、微信朋友圈展示,那么百度和微信就是下游渠道。但中间可能经过几层渠道,而中间的每一个渠道对于下游来说都是一个广告主,也都可以称为网盟平台,大家可能会对百度网盟广告平台有所耳闻。

如何用Java反射提高开发效率的框架

那么作为一个网盟平台,需要整合上游的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。

如何用Java反射提高开发效率的框架
 

要实现根据注解自动将TestResponse对象解析生成List<ProdCampaign>集合,那肯定要对TestResponse对象的结构了如指掌(下文将这一类由调用API返回的对象称为Response对象)。所以需要将TestResponse类中的字段信息映射成一颗树。

先不讲那么多,来看下最终实现的效果,使用@ProdCampaignMapRules注解声明映射规则。

如何用Java反射提高开发效率的框架
 

正如上图所示,我在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数据。

如何用Java反射提高开发效率的框架

整棵结构树就是一个RuleMetaData,根节点root肯定是一个ClassNode。非基本数据类型的FieldNode也有一个child指向一个ClassNode。很抽象?没关系,继续看,下面会有一张图,形象的画出了这棵结构数。

实现包扫描将Class解析成一颗结构树

在应用启动时,实现包扫描,找出所有被@MapRulesComponent注释的类,解析生成结构树RuleMetaData,举个例子。

如何用Java反射提高开发效率的框架

如图,已经画得很形象了,这棵树的结构应该已经在你的脑子里。

结构树的root节点,是该Response的第一个字段,存储了这个字段的字段名和反射获取到的Field,所属类TestResponse。下个字段就是节点的nextField字段,由于该字段的类型是基本数据类型int,所以child的值为null。

到data字段的时候,由于其类型是非基本数据类型,而是一个内部类DtaBean,需要解析成一个ClassNode,同时要继续反射取得DataBean的所有字段的FieldNode节点链。最后将data节点的child指向这个ClassNode。

在反射DataBean生成FieldNode链的时候,处理到rowset字段,发现其是一个List类型,那就获取这个List的元素的类型,这个反射是支持的,能获取到。

如何用Java反射提高开发效率的框架

然后再继续对RowsetBean继续以上步骤。还有一点,就是RowsetBean被@ProdCampaignMapRules注解注释,所以要获取到@ProdCampaignMapRules注解,赋值给rowset的ClassNode节点的rule。

反射解析Response生成结构数,我是使用队列加广度优先遍历方法。代码如下图所示。

如何用Java反射提高开发效率的框架

如何用Java反射提高开发效率的框架

包扫描如何实现我就不说了。

根据OfferBean的Class结构树映射生成ProdCampaign

这一步我只介绍实现思路,每一步的具体实现,整体的实现就不贴代码了。

1.首先使用jackson解析调用第三方API返回的json字符串,得到Response对象。

2.根据Response的Class从缓存中获取其类结构树。使用深度优先搜索,取得被@ProdCampaignMapRules注释的ClassNode。这个很好判断,因为在前面将类生成结构树的步骤,已经取得@ProdCampaignMapRules注解并存放到了ClassNode的rule。

如何用Java反射提高开发效率的框架

3.获取到被@ProdCampaignMapRules注释的ClassNode后,取得该ClassNode所属的FieldNode。

4.取得该FieldNode后,获取Field,反射取得值,这个值就是要找的目标,即存放广告Offer的集合。

如何用Java反射提高开发效率的框架

5.拿到存放广告Offer的集合后,遍历这个集合,将里面的每个元素,映射成平台统一的存放广告信息的ProdCampaign。最后就能获取到ProdCampaign集合。

6.每个元素映射成ProdCampaign对象的过程,才是重点,还是很复杂的。因为需要支持多层映射,就是支持像使用mybatis拼写sql一样支持“.”操作,如“offer.geo.country”,就是要取得offer对象的geo字段的值,再获取geo的country字段的值。有了类结构树,多级映射并不难,只需要一个while循环即可。

如何用Java反射提高开发效率的框架
 

7.对于tracklinkMap,是一个注解数组,类型是@TracklinkMap,用于声明url需要哪些参数,怎么拼接。

如何用Java反射提高开发效率的框架

“如何用Java反射提高开发效率的框架”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注亿速云网站,小编将为大家输出更多高质量的实用文章!

原创文章,作者:254126420,如若转载,请注明出处:https://blog.ytso.com/tech/opensource/229989.html

(0)
上一篇 2022年1月15日 03:22
下一篇 2022年1月15日 03:23

相关推荐

发表回复

登录后才能评论