函数式接口在Jalor3中的应用
对于一个标准的Jalor3应用,其Project主要分为如下几个,这样做的目的就是为了使系统尽量模块化,每个模块负责对应的职责,相关之间松耦合。
在一次业务请求中(一个struts的Action方法调用),我们实际上是有很多逻辑要处理的,比如参数检验、第三方接口调用、业务处理等。在之前的开发过程中,开发人员把这些逻辑全放在一个struts的Action方法中,具体时序图如下:
这样导致的问题就是事务的完整性无法保证。所以,很自然能想到的就是把这个复杂的方法逻辑移到SessionBean的方法中,这样就可以保证事务的完整性。而按照Jalor3的开发模式,最终变为把这个方法的逻辑放到一个Business方法中,SessionBean是通过delegate给Business完成的。具体的时序图如下:
但马上问题就来了,第三方的调用,耦合了很多web容器的API,如Servlet相关的HttpServletRequest、HttpSession等,当然,这些类如果只是为了调用getParameter、getAttribute来获取相关键-值对,也很好办,在Action方法中将他们取出来,存在Map中,再往后传递即可(实际上我们也这样做了)。但还有其它的第三方调用,如调用PRM的Siebel接口,这部分代码如果挪到Business去处理,根本就调不通。这时候,很容易设想的就是:前端的这部分调用逻辑如果能像一个普通的参数一样,直接传递到后端,需要的时候再执行,问题就解决了。但是,将一段处理逻辑作为参数传递,只有函数式编程才能实现,比如,在JavaScript中,我们常用的闭包就是这样的:
[code lang="javascript"]
$ (function(){
//页面加载完毕后,进行相关的页面初始化处理
});
[/code]
当然,我们想到Java 8的Lambda表达式,可我们Jalor3在生产环境的JVM都是Java 6的,根本满足不了要求。经过一番探索,我们发现,函数式编程实际上也是定义了一个Function<T,R>接口,这个接口通过泛型指定传入的参数和返回值。仿照这个接口,我们也定义了一个类似的接口,如下:
[code lang="java"]
public inter fa ce Function {
/**
* 回调接口
* @param params 传入的参数
* @return 返回处理结果(键值对的形式)
*/
Map call(Map params);
}
[/code]
这是一个比较通用的接口,一个方法调用,无论有多少个参数,都可以用key-value的形式表示,返回值也一样。这样,我们可以在Action中通过匿名内部类的方式构建一个Callback实例,一直往后传,需要在Action里完成调用的逻辑通过实现call方法完成。params参数通过Business在运行时传入,真正的调用通过在Business中调用Callback实施的call方法完成(即回调)。具体调用时序如下:
前端Action调用示例如下:
后端Business回调如下:
这样,我们就在Jalor3中实现了近似的函数式编程。解开了web层与业务逻辑层在API层页面的耦合。当然,这里也有一定的前提条件,就是前端的web代码和后端的Business代码运行在同一个JVM中,否则,在运行时还是会报错。对于我们自己当前的应用,不存在跨JVM的问题,所以,这样的代码能正常行。本文在一定程度上来说,只是从形式上解开了前后端的耦合,而要真正解开这个耦合,需要从前期的设计开始,就要充分考虑到这些问题,比如,在一个事务中,有多少调用,哪些调用是第三方的,那些调用是本地的,只有把所有的这些调用分析清楚,确定相应的交互顺序,才能真正实现事务的完整和前后端分离。
作者:卓德方
- 点赞
- 收藏
- 关注作者
评论(0)