Jerry最近的工作和SAP某云产品的扩展性设计相关,因此借这个机会,把我过去工作中积累的SAP产品扩展技术相关的知识做一个梳理和回顾。
文章目录
SAP产品标准
SAP Field Extensibility简述
SAP Side-by-Side Extensibility简述
SAP In-App Extensibility介绍
SAP Business Addin增强概念在多种SAP产品中的应用
ABAP类面向切片编程方式(Aspect Oriented Programming)
SAP Commerce扩展方式简述
SAP Fiori UI扩展方式简述
展望未来
下面是文章正文。
SAP产品在发布到市场上之前,都必须经历一系列严格的产品标准(Product Standards)相关测试。
这些产品标准包含但不局限于:
- 功能正确性(Functional Correctness)
- 性能(Performance)
- 安全性(Security)
- 全球化(Globalization)
- 业务配置性(Business Configuration)
- 可扩展性(Extensibility)
- 生命周期管理(Software Lifecyle)
- 可访问性设计(Accessibility)

其中SAP产品的可扩展性(Extensibility), 又可细分为字段级别的可扩展性(Field Extensibility)和流程级别(Process Extensibility)的可扩展性。当然二者有时也没有明确的区分界限,比如客户实际应用场景中,一旦创建了新的扩展字段后,通常也期望该字段参与到业务流程中去,即所谓端到端的扩展场景(End-to-End Extension Scenario).
Jerry之前写过一篇文章介绍了SAP产品字段级别可扩展性(Field Extensibility)的设计原理:SAP产品的Field Extensibility,本文则介绍SAP产品流程级别的可扩展性。
SAP产品的两种扩展方式:In-App和Side-by-Side Extensibility
以Jerry工作的SAP C/4HANA套件为例,在SAP帮助文档里,我们可以首先选择以In-App Extensibility还是以Side-by-Side的方式来扩展:

选定扩展类型后,再从下拉菜单里选择具体的产品名称,即得到该产品针对选定的扩展类型,SAP所推荐的扩展方式。

所谓In-App Extensibility,指通过SAP扩展工具创建出的Enhancement(增强),同被增强的SAP标准产品运行在同一服务器上。更准确地说,增强实现同被增强的SAP应用运行于同一会话(Session)内。
与之相对的是Side-by-Side Extensibility,增强实现同被增强的SAP应用通常运行在不同的服务器上。以Jerry之前文章 基于SAP Kyma的订单编排增强介绍 里提及的场景为例,SAP Commerce将Order.Created事件注册到SAP Cloud Platform Extension Factory(Kyma)上,二次开发人员在SAP云平台上创建Lambda Function,订阅这个事件,实现发送邮件的逻辑。运行时每当SAP Commerce有订单创建,Order.Created事件发布到SAP云平台上,Lambda Function被触发,自动发送邮件。
通过Side-by-Side Extensibility,可以实现咨询公司Gartner早在2014年就提出的双模IT(Bimodel)概念,即一方面通过SAP S/4HANA作为数字化核心,以支撑企业稳定可靠运作;另一方面,通过SAP云平台所架构的数字化创新平台,借助包括人工智能、区块链、大数据分析等前沿科技,对S/4HANA进行Side-by-Side扩展,帮助客户实现快速的产品/服务乃至商业模式的创新。

关于Side-by-Side Extensibility,Jerry之前的文章曾做过简单介绍:
还在用ABAP进行SAP产品的二次开发?来了解下这种全新的二次开发理念吧

本文余下部分着重回顾SAP In-App这种扩展方式。
在讨论SAP产品扩展时,我们有必要区分这组概念的差异:Enhancement(增强)和Modification(修改). 后者是直接对SAP产品的源代码进行修改,在SAP产品升级或者Patch导入系统时,这些本地修改会被覆盖,故SAP不推荐通过Modification的方式进行二次开发。而SAP增强技术则不会存在当产品升级时被覆盖的问题,因此将SAP增强技术描述为一种具备Upgrade Safe或者Modification Free特性的技术.

SAP Business Addin增强概念在多种SAP产品中的应用
以ABAP作为技术栈的On-Premises产品,其增强技术源远流长。尽管具体技术实现有差异,但思路都一致:SAP事先在标准程序里预留一些Hook,二次开发人员可以实现这些Hook,将自己的业务逻辑代码编写在里面。 这些Hook所在SAP标准程序里的位置,称之为增强点(Enhancement Point). 当标准程序执行到这些Hook时,系统如果检测到有Partners实现了这些Hook,就调用之,从而实现了业务流程的扩展。
这些增强方式技术上没有太多噱头,但却体现了德国制造一贯的作风:严谨实用,低调高效。可以说SAP早期基于ABAP技术栈的产品能在全世界取得成功,称霸ERP软件领域,这些增强技术功不可没。

从时间线来说,国内很多SAP中文博客将ABAP增强技术划分为三代:User Exit,Function Enhancement和Business Addin. 前两种方式广泛用于早期的SAP产品中,现在已很少提及。Business Addin(有时简称BAdI),从诞生至今一直是SAP ABAP On-Premises产品增强技术的主力军,并且在ABAP Cloud产品如SAP Cloud for Customer和S/4HANA Cloud中也能看到其身影。
Business Addin技术又分为使用CL_EXITHANDLER进行增强管理和调用的传统方式(Classical BAdI)和使用ABAP关键字GET BADI和CALL BADI实现这两种方式。二者区别在于前者是在ABAP应用层面管理和调用增强,而后者两个关键字的实现位于ABAP Kernel中,性能优于传统方式。
换言之,在传统BAdI增强方式里,对于SAP预留的增强点里,运行时到底包含哪些有效增强实现的逻辑,是编写在ABAP层的CL_EXITHANDLER里,所有ABAP开发人员都能调试这些ABAP代码:

而GET BADI和CALL BADI实现在ABAP Kernel层,只有SAP内部人员才能看到这些关键字的C语言实现代码。

GET BADI关键字执行完毕后,满足Filter条件且状态为Active的增强实现实例被ABAP Kernel填充到内表IMPS里:

正因为ABAP新式增强其强大的扩展功能,在基于ABAP的Cloud产品里也出现了它的身影。
在SAP Cloud for Customer里,二次开发人员无法直接用SAPGUI编写ABAP增强代码,却仍旧能用SAP Cloud Application Studio创建增强:

下图下拉菜单显示的就是CustomerQuote这个BO预留的增强点:

在Studio里用ABAP脚本语言编写增强实现,保存激活后,会在ABAP后台自动生成BAdI增强体和根据ABAP脚本语言编译成的ABAP原生代码。

到了S/4HANA Cloud,SAP仍然不想放弃给二次开发人员提供编写ABAP BAdI增强实现的功能,只不过编写工具从SAPGUI和SAP Cloud Application Studio迁移到了浏览器中。
客户在S/4HANA Cloud的Fiori Launchpad里,进入Custom Logic这个tile:

新建一个Enhancement Implementation:

选定Business Context后,同样能从Enhancement Option即可用增强点列表里,选择合适的增强点创建增强实现:


同SAP Cloud for Customer编写的ABAP脚本不同,在SAP S/4HANA Cloud Custom Logic里,编写的是原生的ABAP代码:

关于浏览器里如何实现上图所示的ABAP语法高亮,请参考Jerry的文章:ABAP开发环境语法高亮的那些事儿。
ABAP类面向切片编程方式(Aspect Oriented Programming)
相对Java编程人员,ABAP开发人员平时可能不会接触到面向切片编程(Aspect Oriented Programming,简称AOP)这个概念。

面向切片编程可以看成对面向对象编程思维的一种补充,广泛应用在基于Spring框架的Java应用中,比如SAP Commerce.
利用AOP,可以对组成业务逻辑的各部分进行隔离,降低各部分间的耦合度,以及避免非业务逻辑代码侵入到业务逻辑代码里。
由于ABAP语言特性和Java的差异,SAP官方从未提及ABAP对AOP的支持,所以Jerry本文目录里也采用“类AOP”的字眼来描述。
在ABAP里如果想要统计一个方法的运行时间,最常用的办法是在方法实现体的头部开启一个计时器,在实现体末尾关闭计时器。伪代码如下:

下图是SAP Gateway处理OData请求的框架代码。在处理开始之前打开计时器:

请求处理完毕后关闭定时器:

这样的写法,开关计时器这些基础设施性质的代码就侵入到了OData请求处理的业务代码里。
除了性能统计外,权限检查,日志记录,事务处理等任务也几乎是任何应用必须编码实现的非业务逻辑模块代码。
借助AOP理念,可以优雅地避免非业务逻辑代码对业务逻辑代码的侵入(有时也称污染)。

使用AOP编程范式,业务模块的编写只关注业务逻辑本身,仅此而已。权限检查,日志记录,性能检测这些基础设施级别的关注点,通过不同的AOP实现技术,在不修改业务模块源代码的前提下,像切面(Aspect)一样编织(Weave)到业务模块里。
ABAP缺少Java那样对AOP的完善支持,ABAP平台提供的Pre/Post/Overwrite Exit,可以在一定程度上实现类似Java AOP的效果,即某ABAP方法的Pre-Exit增强,能够自动在该方法调用之前被调用;Post-Exit增强,自动在该方法调用之后被调用。Pre和Post-Exit增强的存储和生命周期管理,均独立于被增强方法本身。

限于文章篇幅,ABAP这种类AOP技术和Java AOP的比较,有机会Jerry单独写一篇文章介绍。
在SAP Business by Design和SAP Cloud for Customer的Cloud Application Studio里,以标准Business Object节点作为粒度,为二次开发人员暴露了很多增强点,比如在某BO节点发生修改之后,保存之前,从数据库加载到应用之后,执行某Action之前,都能编写自定义逻辑。

本来ABAP Pre-Exit和Post-Exit是实现这些自定义逻辑的最佳载体,但是在Jerry工作的2010年的时候,这些Exit无法实现多租户隔离(Multi-tenant isolation),即租户A上创建的Exit,在其他所有租户上也都可见,因此无法用在诸如SAP Business by Design,SAP Cloud for Customer这些需要支持多租户隔离特性的SAP云产品上。

关于SAP Cloud for Customer Extensibility设计的更多细节,请参考我的同事Xu Boris的文章:SAP Cloud for Customer Extensibility的设计与实现。
SAP Commerce扩展方式简述
SAP Commerce的服务层是根正苗红的基于Spring的实现,因此可以充分发挥Java Spring AOP带来的高可扩展性。
关于Spring AOP在SAP Commerce中的应用,请参考SAP帮助文档.

除了Spring AOP之外,SAP Commerce的高可扩展性还体现在其以Extension为基础的模块化架构上。
Jerry第一次学习SAP Commerce时,曾经被其Extension这个单词的字面意思所迷惑。其实在SAP Commerce上下文里,Extension和ABAP里的Extension含义有所不同——后者多指二次开发人员基于SAP标准程序做的增强,而前者是Commerce里一个更加宽泛的概念:
An extension is an encapsulated piece of software that extends SAP Commerce functionality by either modifying existing features, or introduction new features.
SAP Commerce的业务层,平台层和基础设施层的很多标准功能,均通过Extension作为载体来实现。一个Extension就是SAP Commerce里一个最小粒度的功能模块,从开发角度上看就是一个导入到IDE后的Java工程文件夹:


按照SAP帮助文档上的步骤,二次开发人员可以创建新的Extension,实现了自己的自定义业务逻辑后,再按照向导将其合并到SAP Commerce中去,从而实现功能扩展需求。

在SAP Commerce config文件夹下的localextensions.xml, 声明了运行时会加载的Extension列表。由此看出,SAP Commerce里无论标准Extension还是二次开发人员自建的Extension,在加载时都处于同等地位,这是我觉得SAP Commerce在Extensibility上不同于ABAP产品的一个有趣之处。

SAP Fiori UI扩展方式简述
本文描述的SAP Fiori UI,仅限于基于SAP UI5框架实现的前台页面。采用React,Vue,Angular等技术实现的Fiori UI不在本文讨论范围内,您可以通过Jerry这些文章了解更多细节:
基于SAP UI5框架实现的Fiori UI,从实现方式又可以分为前端开发人员手动编写的UI,以及通过框架比如SAP Fiori Elements自动生成的UI两种。
前者的典型例子是SAP CRM Fiori的标准应用,Jerry之前工作过的SAP成都研究院CRM开发团队曾经负责过这几个Fiori应用的开发和维护:

这些Fiori标准应用的XML视图和Controller的JavaScript代码均是我们人工编写的,我们在XML视图里,预留了Partners能够插入自定义UI元素的Extension Point:

二次开发人员通过下图所示的UI5 View Extension,将自定义UI元素插入Fiori UI的SAP标准XML视图预留的Extension Point里:

而JavaScript实现的SAP Fiori UI标准控制器里,我们也为二次开发人员预留了进行流程逻辑增强的所谓Extension Hook:如下图第933行所示:

上图的Hook在Partners的UI控制器里实现代码如下:

我当时担任SAP CRM Fiori客户项目Dev Angel时,曾经建议项目的二次开发人员,用这种方式完成了很多端到端级别的增强开发,我把其中一些案例写在了SAP社区的博客系列里.
无论XML视图里的Extension Point,还是JavaScript控制器里的Extension Hook,设计理念同前文介绍的ABAP Business Addin如出一辙。
这种理念也延续到了基于SAP Fiori Elements自动生成的UI里:

关于如何使用Extension Point对SAP Fiori Elements UI进行扩展,请参考SAP帮助文档.
展望未来
随着SAP Cloud Platform Extension Factory的问世,越来越多的客户选择将自定义逻辑实现在基于Serverless架构的Lambda Function里,通过Side-by-Side的方式对SAP云产品进行扩展。而微服务架构下的SAP云产品,如何对这些基于Lambda Function实现的客户增强进行统一管理和调用,是一个令人浮想联翩的话题,Jerry将来有机会的话会继续介绍。

感谢阅读。
要获取更多Jerry的原创文章,请关注公众号"汪子熙":

更多阅读
ABAP专题
网友评论