最近在做的项目,是一个C/S和B/S混合的项目,我主要负责设计开发框架部分,
最开始设计时,因时间仓促,没有细想,就草草上阵了,
现在项目快结束了,回过头来想想,还是有很多地方可以完善,这里说一下数据传输这一块。
因项目中B/S使用的是Struts2,所以C/S也通过Struts2提供的拦截器和Result扩展点进行了适配,
使得C/S数据传输序列化与反序列化对于业务逻辑透明化,目的是要让服务器端B/S和C/S写法一致。(客户端使用HttpClient实现)
Struts2整体设计还行,但确越来越臃肿,而且对RCP支持很不友好。
其实,C/S主流设计思路中,都是在Service层留出接口,并将接口部署到客户端,
中间通过RMI, Hessian, Burlap, WebService, HttpInvoker等任意方式生客户端Stub,
Stub实现中所有接口函数都远程代理服务器端相应Service实现,也就是客户端与服务器端对等体,
这样就可以通过接口“直接”调用服务器端函数,从而进行透明化数据传输。
B/S中的DWR也使用了这种方式。
这种设计的主要好处就是全部透明化,但将接口部署到客户端不是很方便,
而且服务器上的函数调用起来太方便,如果交互函数粒度较小,会使得业务逻辑向客户端倾斜,
保留Action门面,似乎还是很有必要的,这样可以保证客户端与服务器端交互粒度足够大,
但Action应该以数据为中心,而不是以控制为中心,
在RCP应用中,视图控制基本转移到客户端了,Action不应再对“跳转/切换”之类的过多关注,
WEB应用中的MVC框架,其返回值大多用来做控制,这也是它们不太适宜兼容RCP的问题所在。
按这个思路的设计如下:
[b]客户端与服务器端共享:[/b]
Action接口:(在Action门面层实现透明化对等体)
public interface Action {
Serializable execute(Serializable object);
}
[b]服务器端:[/b]
实现Action接口:
public class XxxAction implements Action {
private XxxService xxxService;
public void setXxxService(XxxService xxxService) {
this.xxxService = xxxService;
}
public Serializable execute(Serializable object) {
// ...
}
}
在spring的beans.xml中注册:
<bean id="xxxAction" class="com.xxx.action.XxxAction">
<property name="xxxService" ref="xxxService"/>
</bean>
[b]RCP应用客户端:[/b] (使用Swing/SWT做视图,序列化对象作传输)
在Java代码中,使用如:
// 返回的是代理,但看起来像拿到了服务器端的Action
Action xxxAction = ActionFacade.getAction("xxxAction");
// result和params为任意可序列化对象
Object result = xxxAction.execute(params);
[b]WEB应用客户端:[/b] (使用ExtJS做视图,JSON作传输)
在JSP页面中,使用如:
<!-- name属性为action在beans.xml中注册的id号 -->
<ext:action var="xxxAction" name="xxxAction" />
<ext:script>
<!-- result和params为json对象 -->
var result = xxxAction.execute(params);
</ext:script>
当然,服务器端拦截器的设计是必不可少的,
这种天然的截面,比AOP的自动代理用起来方便很多。
包括安全,检验,转换,控制都可以使用该截面统一实现。
拦截接口:
public interface ActionInterceptor {
Serializable intercept(Action action, Serializable object);
}
另外,ThreadLocal的ActionContext也是必要的,
用于封装交互过程的附属信息等。
如:
ActionContext.getContext().getRequest();
ActionContext.getContext().getSession();