http://dubbo.apache.org/zh/docs/v2.7/dev/principals/general-knowledge/API 与 SPI 分散框架或组件通常有两类客户,一个是使用者,一个是扩展者。API (Application Programming Interface) 是给使用者用的,而 SPI (Service Provide Interface) 是给扩展者用的。在设计时,只管把它们隔脱离,而不要混在一起。
也就是说,使用者是看不到扩展者写的实现的。好比:一个 Web 框架,它有一个 API 接口叫 Action,内里有个 execute() 方法,是给使用者用来写业务逻辑的。然后,Web 框架有一个 SPI 接口给扩展者控制输出方式,好比用 velocity 模板输出还是用 json 输出等。如果这个 Web 框架使用一个都继续 Action 的 VelocityAction 和一个 JsonAction 做为扩展方式,要用 velocity 模板输出的就继续 VelocityAction,要用 json 输出的就继续 JsonAction,这就是 API 和 SPI 没有分散的反面例子,SPI 接口混在了 API 接口中。
合理的方式是,有一个单独的 Renderer 接口,有 VelocityRenderer 和 JsonRenderer 实现,Web 框架将 Action 的输出转交给 Renderer 接口做渲染输出。服务域/实体域/会话域分散任何框架或组件,总会有焦点领域模型,好比:Spring 的 Bean,Struts 的 Action,Dubbo 的 Service,Napoli 的 Queue 等等。
这个焦点领域模型及其组成部门称为实体域,它代表着我们要操作的目的自己。实体域通常是线程宁静的,不管是通过稳定类,同步状态,或复制的方式。服务域也就是行为域,它是组件的功效集,同时也卖力实体域和会话域的生命周期治理, 好比 Spring 的 ApplicationContext,Dubbo 的 ServiceManager 等。
服务域的工具通常会比力重,而且是线程宁静的,并以单一实例服务于所有挪用。什么是会话?就是一次交互历程。会话中重要的观点是上下文,什么是上下文?好比我们说:“老地方见”,这里的“老地方”就是上下文信息。
为什么说“老地方”对方会知道,因为我们前面界说了“老地方”的详细内容。所以说,上下文通常持有交互历程中的状态变量等。
会话工具通常较轻,每次请求都重新建立实例,请求竣事后销毁。简而言之:把元信息交由实体域持有,把一次请求中的暂时状态由会话域持有,由服务域贯串整个历程。
在重要的历程上设置拦截接口如果你要写个远程挪用框架,那远程挪用的历程应该有一个统一的拦截接口。如果你要写一个 ORM 框架,那至少 SQL 的执行历程,Mapping 历程要有拦截接口;如果你要写一个 Web 框架,那请求的执行历程应该要有拦截接口,等等。没有哪个公用的框架可以 Cover 住所有需求,允许外置行为,是框架的基本扩展方式。
这样,如果有人想在远程挪用前,验证下令牌,验证下黑白名单,统计下日志;如果有人想在 SQL 执行前加下分页包装,做下数据权限控制,统计下 SQL 执行时间;如果有人想在请求执行前检查下角色,包装下输入输出流,统计下请求量,等等,就可以自行完成,而不用侵入框架内部。拦截接口,通常是把历程自己用一个工具封装起来,传给拦截器链,好比:远程挪用主历程为 invoke(),那拦截器接口通常为 invoke(Invocation),Invocation 工具封装了原来要执行历程的上下文,而且 Invocation 里有一个 invoke() 方法,由拦截器决议什么时候执行,同时,Invocation 也代表拦截器行为自己,这样上一拦截器的 Invocation 其实是包装的下一拦截器的历程,直到最后一个拦截器的 Invocation 是包装的最终的 invoke() 历程;同理,SQL 主历程为 execute(),那拦截器接口通常为 execute(Execution),原理一样。固然,实现方式可以任意,上面只是举例。重要的状态的变换发送事件并留出监听接口这里先要讲一个事件和上面拦截器的区别,拦截器是干预历程的,它是历程的一部门,是基于历程行为的,而事件是基于状态数据的,任何行为改变的相同状态,对事件应该是一致的。
事件通常是事后通知,是一个 Callback 接口,方法名通常是已往式的,好比 onChanged()。好比远程挪用框架,当网络断开或连上应该发出一个事件,当泛起错误也可以思量发出一个事件,这样外围应用就有可能视察到框架内部的变化,做相应适应。扩展接口职责尽可能单一,具有可组合性好比,远程挪用框架它的协议是可以替换的。
如果只提供一个总的扩展接口,固然可以做到切换协议,但协议支持是可以细分为底层通讯,序列化,动态署理方式等等。如果将接口拆细,正交剖析,会更便于扩展者复用已有逻辑,而只是替换某部门实现计谋。固然这个剖析的粒度需要掌握好。微核插件式,平等看待第三方大凡生长的比力好的框架,都遵守微核的理念。
Eclipse 的微核是 OSGi, Spring 的微核是 BeanFactory,Maven 的微核是 Plexus。通常焦点是不应该带有功效性的,而是一个生命周期和集成容器,这样各功效可以通过相同的方式交互及扩展,而且任何功效都可以被替换。
如果做不到微核,至少要平等看待第三方,即原作者能实现的功效,扩展者应该可以通过扩展的方式全部做到。原作者要把自己也看成扩展者,这样才气保证框架的可连续性及由内向外的稳定性。不要控制外部工具的生命周期好比上面说的 Action 使用接口和 Renderer 扩展接口。
框架如果让使用者或扩展者把 Action 或 Renderer 实现类的类名或类元信息报上来,然后在内部通过反射 newInstance() 建立一个实例,这样框架就控制了 Action 或 Renderer 实现类的生命周期,Action 或 Renderer 的生老病死,框架都自己做了,外部扩展或集成都无能为力。好的措施是让使用者或扩展者把 Action 或 Renderer 实现类的实例报上来,框架只是使用这些实例,这些工具是怎么建立的,怎么销毁的,都和框架无关,框架最多提供工具类辅助治理,而不是绝对控制。
可设置一定可编程,并保持友好的 CoC 约定因为使用情况的不确定因素许多,框架总会有一些设置,一般都市到 classpath 直扫某个指命名称的设置,或者启动时允许指定设置路径。做为一个通用框架,应该做到通常能设置文件做的一定要能通过编程方式举行,否则当使用者需要将你的框架与另一个框架集成时就会带来许多不须要的贫苦。另外,尽可能做一个尺度约定,如果用户按某种约定做事时,就不需要该设置项。好比:设置模板位置,你可以约定,如果放在 templates 目录下就不用配了,如果你想换个目录,就设置下。
区分下令与查询,明确前置条件与后置条件这个是契约式设计的一部门,只管遵守有返回值的方法是查询方法,void 返回的方法是下令。查询方法通常是幂等性的,无副作用的,也就是不改变任何状态,调 n 次效果都是一样的,好比 get 某个属性值,或查询一条数据库记载。下令是指有副作用的,也就是会修改状态,好比 set 某个值,或 update 某条数据库记载。如果你的方法即做了修改状态的操作,又做了查询返回,如果可能,将其拆成写读分散的两个方法,好比:User deleteUser(id),删除用户并返回被删除的用户,思量改为 getUser() 和 void 的 deleteUser()。
另外,每个方法都只管前置断言传入参数的正当性,后置断言返回效果的正当性,并文档化。增量式扩展,而不要扩充原始焦点观点 我们平台的产物越来越多,产物的功效也越来越多。平台的产物为了适应各 BU 和部门以及产物线的需求,势必会将许多不相干的功效凑在一起,客户可以选择性的使用。
为了兼容更多的需求,每个产物,每个框架,都在不停的扩展,而我们经常会选择一些扩展的扩展方式,也就是将新旧功效扩展成一个通用实现。我想讨论是,有些情况下也可以思量增量式的扩展方式,也就是保留原功效的简朴性,新功效独立实现。
我最近一直做漫衍式服务框架的开发,就拿我们项目中的问题开涮吧。好比:远程挪用框架,肯定少不了序列化功效,功效很简朴,就是把流转成工具,工具转成流。但因有些地方可能会使用 osgi,这样序列化时,IO 所在的 ClassLoader 可能和业务方的 ClassLoader 是隔离的。
需要将流转换成 byte[] 数组,然后传给业务方的 ClassLoader 举行序列化。为了适应 osgi 需求,把原来非 osgi 与 osgi 的场景扩展了一下,这样,不管是不是 osgi 情况,都先将流转成 byte[] 数组,拷贝一次。然而,大部门场景都用不上 osgi,却为 osgi 支付了价格。
而如果接纳增量式扩展方式,非 osgi 的代码原封不动,再加一个 osgi 的实现,要用 osgi 的时候,直接依赖 osgi 实现即可。再好比:最开始,远程服务都是基于接口方法,举行透明化挪用的。这样,扩展接口就是, invoke(Method method, Object[] args),厥后,有了无接口挪用的需求,就是没有接口方法也能挪用,并将 POJO 工具都转换成 Map 表现。因为 Method 工具是不能直接 new 出来的,我们不自觉选了一个扩展式扩展,把扩展接口改成了 invoke(String methodName, String[] parameterTypes, String returnTypes, Object[] args),导致不管是不是无接口挪用,都得把 parameterTypes 从 Class[] 转成 String[]。
如果选用增量式扩展,应该是保持原有接口稳定,增加一个 GeneralService 接口,内里有一个通用的 invoke() 方法,和其它正常业务上的接口一样的挪用方式,扩展接口也不用变,只是 GeneralServiceImpl 的 invoke() 实现会将收到的挪用转给目的接口,这样就能将新功效增量到旧功效上,并保持原来结构的简朴性。再再好比:无状态消息发送,很简朴,序列化一个工具发已往就行。厥后有了同步消息发送需求,需要一个 Request/Response 举行配对,接纳扩展式扩展,自然想到,无状态消息其实是一个没有 Response 的 Request,所以在 Request 里加一个 boolean 状态,表现要不要返回 Response。
如果再来一个会话消息发送需求,那就再加一个 Session 交互,然后发现,原来同步消息发送是会话消息的一种特殊情况,所有场景都传 Session,不需要 Session 的地方无视即可。如果接纳增量式扩展,无状态消息发送原封不动,同步消息发送,在无状态消息基础上加一个 Request/Response 处置惩罚,会话消息发送,再加一个 SessionRequest/SessionResponse 处置惩罚。
本文关键词:亚搏手机在线登录入口,一些,设,计上,的,基本知识,http,dubbo.apache.org
本文来源:亚博全站APP登录-www.hualingmm.com