从决定读ApacheOFBiz源码到现在不知不觉一年就过去了。这一年因为各种原因,导致源码读得断断续续。其实最大的问题还是因为无法深刻得理解里面的一些东西,导致热情骤减。直到最近,公司在开发的一个“应用快速开发平台”引发了我的一些思考,所以决定再把源码拿出来重新阅读。到最近对其架构设计近乎迷恋。
个人认为对于ApacheOFBiz的剖析可以分成三大块来进行:技术、业务、数据库设计。这三块个个都是非常顶尖的水准,每个方向深入进去都可以学到很多东西。之前只是对OFBiz各个部分的单独解析,现在是时候写一篇文章来串讲它的各个部分。当然,这篇主要还是涉及到OFBiz的技术这一块。
框架简介
这张图是OFBiz的整体框架图。从图中可以看到OFBiz的所有app都构建在其framework之上。而其framework的核心就是ServiceEngine(服务引擎)以及EntityEngine(实体引擎)。其实体引擎对于数据库的种类以及拓扑结构的支持都非常健全。它支持本地数据库与远端数据库并存并且支持多达8种主流数据库。
OFBiz技术串讲
下面以一个客户端请求的处理过程来看OFBiz各个组件是如何交互以及衔接的。
(1)客户端浏览器向web服务器发出一个请求(http/https),请求会被web容器接收并作相应的处理(比如参数的封装等)。
(2)请求被路由到一个代理servlet中,该servlet会分析请求是发往哪个app的,然后再到该项目的下的controller.xml配置文件中去匹配request-map配置项,该配置项用于只是OFBiz如何处理这个请求。通常的处理过程是先进行安全检查以及权限确认,然后触发某个“事件”或者服务调用,最后会以一个view作为响应。如果是以一个view作为响应的话,OFBiz会去view-map中匹配该视图,每一个视图view都有它对应的handler。
(3)OFBiz会用配置的handler来处理该view。handler的作用主要用于渲染页面元素,并将需要展示的数据跟页面元素合并。
(4)数据准备。前端的请求归根到底还是请求对后端数据的操作。而ofbiz中用于获取数据的方式十分丰富。考虑到这里对业务逻辑而言,是代码量占比很大的地方,因此OFBiz尝试利用动态语言来编写这部分代码。主要是想利用动态语言的简洁、代码量少、快速开发的优势。随着java语言的发展,OFBiz选择并且替代了多种不同的JVM脚本语言,比如:beanshell,Groovy。采用脚本语言来编写跟操作实体相关的业务逻辑代码,可谓有利有弊:
弊端:动态语言(弱类型语言)固有的类型安全性的缺失以及纯解释执行性能跟Java这种解释+编译型语言还是无法比拟。
优势:大量传统行业,不需要那么高的性能要求;即便需要也可以用提升硬件来改善;脚本语言在编程效率、逻辑简洁性都是Java冗长的代码所望其项背的;脚本语言更贴近人类语言的表达,更适合实现DSL,来表述业务逻辑(而且OFBiz的下一步目标也将增强对Groovy实现DSL的支持)。
(5)OFBiz的viewhandler通过模板引擎绑定页面元素与数据后,渲染出最终的输出流,通过http响应给客户端浏览器。
页面渲染
Apache OFBiz使用XML+Widget的布局,来将页面切分成一个个Widget以增强各个widget的灵活性以及复用性。
这些widget可以互相嵌套形成一个decorator-widget产生模板,在widget可以直接编写html标签,或者引用各种服务端模板引擎支持的模板文件(比如freemaker)。
前端的内容通常是HTML+数据。widget并没有忽略数据这部分。只是一种布局技术,它最终会由webcontainer转化为html,只不过数据的处理(CRUD)通常位于服务器端。而这些动作都被抽象成为了widget中的“action”。一个screen通常包含各种其他组件widget的引用,这些组件widget可以是:form,screen,menu等。
权限检查
OFBiz中采用的是角色+安全组的授权模型。
从图中可以看出常用的几个权限有:_ADMIN(管理员权限),_VIEW(浏览权限,为最小权限),_CREATE(创建权限),_UPDATE(编辑权限),_DELETE(删除权限)。因此如果controller.xml中配置了授权检查时,将会进行上图的权限检查流程。一个请求在处理之前,会检查其是否需要先进行登录。如果登录验证通过,会获取该会员的security group。而security group又是security permission的集合。进而可以判断用户是否有某个操作的权限。
请求事件
因为OFBiz使用了公共代理的servlet,因此对每个请求而言,其处理逻辑就没有独享的servlet了。这里OFBiz引入了Event的机制来处理每个请求需要执行的特定的操作逻辑。
每收到一个请求,如果有特殊的处理,就触发一个event。上图是event的触发机制。它的配置在controller.xml文件下的request-map配置项内。
服务引擎
OFBiz执行服务基于其自身实现的服务引擎框架。该服务引擎借鉴了《CoreJ2EE Pattern》里的“BusinessDelegate”模式。
调用程序通过服务引擎框架调用服务后,服务引擎首先将调用服务的参数提取并构建服务调用的上下文。然后选择合适的dispatcher,它其实就是businessdelegate。由他选择合适的引擎来执行服务。OFBiz会先判断服务有没有“特殊性”,比如它是否是异步的?是否是递归执行的。如果是,那么会选择它内置的JobScheduler来执行。还有它是否是带SECA(ServiceEvent Control Action)的?如果是SECA,那么会先执行SECA然后选择已配置的特定引擎来执行真正的目标服务,而在这个过程中会有一个Map来充当执行的上下文,用于存储中间结果、错误信息以及最终结果。执行完成之后,上下文对象会在调用栈中层层回退,并将最终的执行结果返回给调用端。
服务事件控制响应器
所谓SECA是Service Event Control Action的单词首字母的缩写形式。可以简单得理解成“服务编排”(可能会执行多个服务,但是某些服务需要满足特定的执行条件)。
比如上面一个带SECA的服务X,在服务引擎执行之前,会先处理其ECA(这里先调用一个action,它需要执行服务B)。当ECA处理完成之后,会将控制权返回给执行引擎。执行引擎会根据服务B的执行结果来判断是否会调用真正的服务X。SECA被用于在OFBiz中替代了其原有的规则引擎以及工作流引擎,可见其灵活性是足以满足复杂业务支撑的。
写在最后
更多的技术分析,请看之前的系列文章以及后续的持续更新。