在轻量流程引擎接口设计中提到触发业务事件需要调用事件的deliver方法来触发并交付业务事件,流程引擎最终会调用业务服务组件的事件处理方法,例如对买家确认收货事件,会调用收货组件的确认收货方法。采用这种显式交付业务事件的方式,开发人员需要new一个对应的业务事件对象,然后调用该事件的deliver。这种方式不是很友好,在没有采用流程引擎之前,开发人员直接调用收货组件接口来进行确认收货。采用流程引擎后,开发人员希望仍希望采用这种面向业务组件接口的编程方式,但流程引擎是采用事件驱动的,流程引擎根据事件来调用对应的业务服务组件,也就是不会让开发人员来直接调用业务服务组件。 因此为了沿用先前的调用方式,需要有一种机制来将完成业务方法调用到业务事件的转换(业务方法->业务事件),通过这种转换来达到隐式交付业务事件的目的。为了完成这种转换,需要增加一个间接层,这里采用代理模式(Proxy设计模式)来实现间接层,完成方法调用到业务事件的转换,更优雅的方式是直接使用spring aop。这里我们自己用Java动态代理而不是直接用AOP。
在转换时,需要建立方法调用参数和业务事件属性之间的对应关系(映射关系),在技术上在采用annotation来注解方法,建立方法参数和事件属性之间的对应关系。
先简要介绍下Java动态代理。
Java动态代理
代理模式是一种常用的设计模式,其目的是为了控制对某个真实对象的访问。代理对象和真实对象一般实现了相同的接口。客户端在调用代理对象的方法时,代理对象负责将调用委托给真实对象,在委托的前后,代理可以进行一些控制或预处理工作。
Java动态代理的核心是java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口,动态代理是实现AOP的一种方式。注意此Proxy不是代理模式中的代理类,而是生成代理对象的工厂。客户端在调用代理对象时,需要先调用Proxy类的newProxyInstance静态方法生成代理对象实例,在调用该方法时,需要传入真实对象实现的接口和InvocationHandler的实现类对象,然后newProxyInstance会返回一个实现了这些接口的代理对象。由于是在JVM虚拟机运行过程中生成代理类,不需要事先定义代理类的类文件,因此叫动态代理。
InvocationHandler是调用处理器接口。无论客户端调用代理对象的哪个接口方法,最终均会调用到invoke方法上,这是动态代理机制内部实现的。在该接口实现类的invoke方法中集中统一处理在代理对象上的方法调用,实现对真实对象的委托访问。
InvocationHandler接口的invoke方法签名如下:
/* proxy就是Proxy类的newProxyInstance工厂方法返回的代理对象实例,method是客户端调用该代理对象的方法表示,args就是方法的参数。
*/
public Object invoke(Object proxy, Method method,Object[] args);
Proxy类的newProxyInstance方法签名如下:
/*loader为类装载器,用于加载动态生成的代理类、 interfaces为接口数组、
h为 InvocationHandler接口实现。
返回创建的代理对象实例。
*/
static Object newProxyInstance(ClassLoader loader, Class[] interfaces,
InvocationHandler h);
BusinessEventAttr annotation
BusinessEventAttr为业务事件annotation注解,作用于业务组件接口方法(前文中的南向接口)上,建立方法参数和事件属性之间的对应关系。建立对应关系后,就可从业务组件接口方法参数中提取事件属性。 下面是用于买家确认收货业务组件接口的注解:
interface BuyerConfirmReceptionService{
@BusinessEventAttr(businessKey="$inputScope[0]",
roleId="$inputScope[1].id",eventType="buyerConfirmReceptionEvent")
ConfirmReceptionResult confirm(Integer tradeId,RoleInfo roleInfo);
}
注解的属性通常是常量或表达式。
上面的注解属性businessKey=”$inputScope[0]”,表示businessKey属性值是confirm方法的第一个输入参数tradeId(交易ID,通常交易ID和订单ID相同)。businessKey代表业务ID,业务ID和流程实例ID一一对应,对应关系可由流程引擎维护,此处之所以不用流程实例ID是为了避免业务组件和流程引擎耦合。
roleId="$inputScope[1].id"表示角色id是confirm方法的第二个参数对象roleInfo的id属性;eventType为事件类型。
BusinessEventAttr其他属性,如流程名称、是否同步事件等就不列出了。
采用注解的方式表达接口方法中的事件信息后,南向接口的定义更为自由,其事件处理方法中的参数可以有多个,例如上面的confirm方法有两个参数。
业务对象代理工厂类
客户端使用业务服务代理工厂类来生成业务服务代理。为了方便客户端编程,该代理工厂类对java动态代理api进行了封装,简化客户端调用。该工厂类定义如下:
public class BusinessServiceProxyFactory {
@SuppressWarnings("unchecked")
public static <T> T getProxy(Class<T> intf) {
return (T) Proxy.newProxyInstance(BusinessServiceProxyFactory.class.getClassLoader(),new Class[]{intf},
new InvocationHandler() {
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
//获得注解实例
BusinessEventAttr eventAttr = method.getAnnotation(BusinessEventAttr.class);
//完成业务方法调用到业务事件的转换,根据eventAttr和args调用populateEvent拼装 //业务事件,完成方法调用到业务事件的转换。
BusinessEvent bzEvent = populateEvent(eventAttr,args);
//获取流程引擎
FlowEngine flowEngine = FlowServicesLocator.getFlowEngine();
/*调用startFlow驱动流程,提交业务事件。流程引擎会根据bzEvent中的业务ID找到对应的流程实例ID,然后根据该流程实例所处的当前节点和事件类型,调用该节点上配置的南向接口上的事件
处理方法(即业务服务层的业务服务组件方法),调用完成后,进行流程流转,返回调用
结果。
*/
return flowEngine.startFlow(bzEvent);
} // end invoke
}); //end new InvocationHandler()
}
}
BusinessServiceProxyFactory类只有一个方法getProxy(Class<T> intf),传入业务服务组件接口,返回
一个实现了该接口的动态代理实例,对返回采用了范型类型,这样客户端调用时不需要cast。getProxy其实只包含一行代码,就是调用Proxy.newProxyInstance方法,newProxyInstance方法中的 InvocationHandler的实例为匿名类。
这个BusinessServiceProxyFactory也可封装为spring 中的factorybean,它返回业务服务实例(是一个代理)。
在invoke方法中完成业务方法调用到业务事件的转换,然后启动流程(流程启动后,会调用事件处理器,最后进行流程流转)。
在轻量流程引擎接口设计中提到的BusinessEvent需要将BusinessDataDto businessData属性改为 Object args[ ];
客户端调用
以某交易流程中的买家确认收货为例说明客户端的调用。买家签收物流送来的宝贝后,打开该订单web页面,点击页面中的"确认收货"按钮。系统将调用Action控制类的execute方法或doXXX方法,在execute该方法中调用买家确认收货服务的confirm方法,execute方法示例如下:
//todo进行一些权限验证或数据组装工作
...
//调用业务服务代理工厂的getProxy工厂方法得到买家确认收货服务实例,该实例是一个动态产生的代理
BuyerConfirmReceptionService confirmService =
BusinessServiceProxyFactory.getProxy(BuyerConfirmReceptionService.class);
/*和未采用流程引擎之前一样,仍调用BuyerConfirmReceptionService业务接口的confirm方法, confirmService不真正指向业务服务层的确认收货组件,它是一个代理,作为面具来伪装,让开发人员觉得是调用确认收货服务。对confirm方法的调用会转到上面的 InvocationHandler匿名类的invoke方法中,从而将confirm方法调用转换为确认收货的业务事件。
在返回的result结果中包含流程实例的最新流转状态,也可提供一个接口来专门查询流程实例状态。
*/
ConfirmReceptionResult result = confirmService.confirm(tradeId,roleInfo);
...
如上节业务对象代理工厂中所述,在InvocationHandler匿名类的invoke方法中调用flowEngine.startFlow时,流程引擎将会调用业务逻辑层的买家确认收货组件的confirm方法(流程定义文件中的事件处理器方法)。业务服务层的买家确认收货组件实现BuyerConfirmReceptionService接口,在该业务服务层组件的confirm方法中,开发人员只需要考虑如何处理买家确认收货逻辑,不需要关心流程流转,也不需要调用流程引擎api;在action类中也不需考虑流程流转和调用流程引擎api,也就是流程引擎api不会侵入其他层的代码中,实现了业务逻辑层和流程层的完美解藕和隔离。
流程引擎和流程定义文件
采用DSL来定义流程(包含节点定义、事件处理器、流转条件),流程DSL对流程api进行封装(创建流程实例api、设置修改流程变量api等)。
流程定义文件除了包含节点定义、事件处理器、流转条件外,还可以在其中定义流程脚本(使用Java或groovy动态语言编写的代码片段):修改或设置流程变量的脚本、action脚本、流出操作、流入操作等。
通过流程DSL和这些流程脚本,把流程相关的逻辑全部封装在流程层,保证了这些流程逻辑不会侵入到业务服务层,业务服务层也就不需要调用这些流程api了。例如在开始节点中处理下单事件的业务组件(生成订单组件)不需要调用流程api来创建流程实例,创建流程实例是由流程层负责调用流程api来创建的,生成订单的组件不关心流程实例的创建。
流程引擎提供了对多种流程模式的支持,如and-join模式。
相关推荐
- 弹性扩展:使用分布式架构,根据业务需求动态调整资源,应对节假日或特殊事件的流量高峰。 - 实时监控与报警:实时监控系统性能指标,快速响应异常,确保服务稳定。 2. **人人车供应链系统技术架构演进**: ...
面向微服务的软件开发方法是近年来软件工程领域的重要研究趋势,它源于面向服务架构(SOA)的概念,但更强调服务的小型化、独立性和可部署性。微服务架构允许软件系统由一组小型、自治的服务构成,每个服务专注于...
在C++中实现业务管理系统,开发者可以利用C++的强大性能、面向对象特性以及丰富的库支持。 首先,C++作为一门系统级编程语言,具备高效性和灵活性,使得它在处理大规模数据和复杂逻辑时表现出色。在业务管理系统中...
面向未来,JSLEE将继续探索如何更好地适应云化、虚拟化的网络环境,强化其在5G、物联网等新兴领域的应用,推动业务逻辑的智能化、自动化,实现网络服务的快速迭代与无缝交付,为用户提供更加个性化、高质量的体验。
C#支持类、接口、继承、多态等面向对象特性,使得代码结构清晰,易于维护。 系统主要包含两个核心模块:员工管理和业务管理。员工管理模块旨在帮助企业管理其人力资源,可能包括员工信息录入、查询、更新和删除等...
在软件开发过程中,需求规格说明书是一份至关重要的文档,它详细描述了软件的功能、性能、接口、设计约束等各个方面的需求,为项目的实施提供了明确的指导。本资源集合包含了不同版本的规格说明书模板,包括GB8567_...
面向对象程序设计倡导的四大基本原则是封装变化、针对接口编程、多用组合、少用继承以及依赖抽象而不是具体类。这些原则帮助开发人员构建出更加灵活、可复用和易于维护的系统。封装变化使得系统的可变部分独立,减少...
面向对象的核心要素包括:对象、类、封装、继承、多态、接口和消息传递。 1. 封装是面向对象的核心特性之一,它将数据(属性)和操作数据的方法(行为)结合在一起,构成了类的两个主要部分。封装提供了数据隐藏和...
PaaS平台提供业务交付、资源接入和能力管理;SaaS平台主要面向应用管理和用户交互;终端平台则是业务与用户家庭宽带网络融合的关键接口。 5. 数据服务与大数据平台:云服务平台中的数据服务包括从数据获取到清洗的...
面向服务架构(Service-Oriented Architecture,简称SOA)是一种设计模式,旨在通过将业务功能解耦并封装成独立的服务,实现业务与技术的分离,从而提高企业的业务敏捷性。在SOA中,服务是核心元素,它们是业务流程...
面向服务架构设计(SOA,Service-Oriented Architecture)是一种软件设计模式,旨在通过网络将松散耦合的、粗粒度的应用组件分布式部署、组合和使用,以满足灵活、可扩展和可重用的需求。服务层是SOA的核心,允许...
- 目标:openCloud致力于提供开放的分布式云服务,不仅是一个软件,更是一个容器,通过弹性事件驱动服务框架适应不同领域的业务需求。服务包括软件模式、SaaS模式、PaaS模式以及DevOps模式的交付。 - 用户群体:...
### 业务网络的奠基石——SDP:服务交付平台 #### 概述 随着电信行业的不断发展,运营商面临着从传统服务向综合性服务提供商转型的重大挑战。在这个过程中,业务层的重要性日益凸显,而SDP(Service Delivery ...
这些文档共同构成了业务分析师在软件开发中的工作框架,从需求分析到系统设计,再到测试和维护,每一个环节都有相应的文档支持,确保项目的顺利进行和高质量交付。作为业务分析师,掌握并熟练运用这些文档,能有效...
标题中的“云原生技术在2B交付中的实践”告诉我们,这篇文档将讨论的是云原生技术如何应用于2B(面向企业)软件交付过程中的实际操作。在当前的产业互联网升级背景下,SaaS(软件即服务)服务模式发展迅速,但2B领域...
7. 会话bean接口:会话bean的组件业务接口和home接口的实现通常由EJB容器处理,但接口定义和业务逻辑仍需由开发者完成。 8. RUP中的包:包是组织模型元素的方式,可以包含设计元素并作为交付单元。但在RUP中,迭代...
它将软件开发过程分为不同的阶段,包括初始、细化、构建和交付。RUP具有迭代的本质,允许项目在不同阶段重新评估需求和设计。 5. 迭代增量开发:迭代增量开发是一种软件开发方法,涉及重复循环地开发软件,每次迭代...
Node.js基于JavaScript,以其非阻塞I/O和事件驱动特性在高并发场景中表现出色。 系统架构设计是业务系统开发的核心环节。常见的架构模式有单体架构、微服务架构和面向服务架构(SOA)。单体架构将所有功能集成在一...