当前位置: 首页 > 工具软件 > Struts4RCP > 使用案例 >

实现一个针对RCP应用的类似Struts的轻量级MVC框架

逄俊力
2023-12-01
趁着公司项目收尾,有些空闲,写了一些回顾性的文章:
[url]http://javatar.iteye.com/blog/258066[/url]
基于这篇文章的思考,周未写了一个简单的实现框架:
[url]http://struts4rcp.googlecode.com[/url]
服务器端已完成,包括:请求接收,序列化策略,Action实例化工厂策略,拦截器链,异常转换等。
客户端也基本实现,包括:配置加载,传输策略,传输状态事件,同步调用,异步调用,连接监控,连接状态事件,但还差传输队列的实现,以及队列分组,队列改变事件等的处理。

[b]1. 简介[/b]
[b](1) 说明[/b]
struts4rcp是一个适用于RCP/RIA应用的轻量级MVC框架,与Struts功能相似,提供远程MVC控制器支持,简化模型与视图的交互过程。
[list]
[*]MVC: Model View Controller
[*]RCP: Rich Client Platform
[*]RIA: Rich Internet Application
[*]Struts: 一个适用于Web页面"请求-响应"方式的MVC框架
[/list]
[b](2) 对比[/b]
常规C/S应用中,通常都采用透明化的远程方法调用方式与服务器端通讯,即:将Service接口同时部署于客户端,并在客户端生成Service接口的Stub实现,通过各种协议代理访问服务器端的Service实现,此类工具如:RMI, WebService, XML-RPC/JSON-RPC, HttpInvoker等。为什么还要加一层Action控制器?
[list]
[*]远程调用Service接口,相当于MVC中的视图直接调用了模型,增加控制器,使职责更清晰。
[*]Serivce域模型通常是无状态的,增加远程控制器,管理服务器端状态。
[*]Service接口粒度过细,使得业务逻辑向客户端倾斜,产生过多的请求次数,增加控制器,确保大粒度请求。
[*]客户端部署Service接口比较繁琐,增加控制器作为中介者,简化耦合。
[*]在控制器层,同样可以实现透明化调用,用户同样不需要关心交互层面的信息。
[*]在控制器中更易于处理天然的(不需要反射或字节码增强的)AOP截面和事件模型,方便拦截器,转换器,校验器,安全控制等的扩展。
[*]对于大量长期使用Struts的开发人员,转入RCP开发时,更易于理解和使用,降低学习成本。
[/list]
[b](3) 特性[/b]
[list]
[*]以数据为中心的控制器
[*]友好的执行过程拦截器
[*]完善的集成扩展点
[*]分组队列传输机制
[*]全面的事件通知
[/list]
[b]2. 使用[/b]
[b](1) 调用[/b]
[b](a) 服务器端[/b]
Action实现类:

public class LoginAction implements Action<Account, User> {

public User execute(Account account) throws Exception {
// ......
}

}

Spring配置:

<bean id="loginAction" class="com.xxx.LoginAction">
<property name="loginService" ref="loginService" />
</bean>

[b](b) 客户端[/b]
同步执行:

Account account = new Account("james", "123456"); // 传入数据模型
Action<Account, User> loginAction = Actions.getAction("loginAction"); // 对应Spring配置中的<bean id="loginAction">
User user = loginAction.execute(account); // 同步执行,并取得结果

异步执行:

Account account = new Account("james", "123456"); // 传入数据模型
Action<Account, User> loginAction = Actions.getAsyncAction("loginAction", new ActionCallback<User>() {
public void callback(User user) throws Exception {
// 异步执行完成后,通过此接口回传执行结果
}
});
loginAction.execute(account); // 异步执行,并立即返回null

同步阻塞执行:
[code]
Account account = new Account("james", "123456"); // 传入数据模型
Action<Account, User> loginAction = Actions.getBlockingAction("loginGroup", "loginAction"); // 相同组名的Action将相互阻塞
User user = loginAction.execute(account); // 同步执行并取得结果,当同组的其它Action未完成时,该执行过程进入队列等待,当前线程被阻塞,直到结果返回
[/code]
异步阻塞执行:
[code]
Account account = new Account("james", "123456"); // 传入数据模型
Action<Account, User> loginAction = Actions.getAsyncBlockingAction(("loginGroup", "loginAction", new ActionCallback<User>() {
public void callback(User user) throws Exception {
// 异步执行完成后,通过此接口回传执行结果
}
});
loginAction.execute(account); // 异步执行,当同组的其它Action未完成时,该执行过程进入队列等待,阻塞异步线程,但不阻塞当前线程
[/code]
[b](c) Web页面[/b]
同步执行:

var account = {username: "james", password: "123456", class: "com.xxx.Account"}; // 传入数据模型,若不指定class属性,表示使用Map类型
var loginAction = Actions.getAction("loginAction"); // 对应Spring配置中的<bean id="loginAction">
var user = loginAction.execute(account); // 同步执行并取得结果

异步执行:

var account = {username: "james", password: "123456", class: "com.xxx.Account"}; // 传入数据模型,若不指定class属性,表示使用Map类型
var loginAction = Actions.getAsyncAction("loginAction", function(user) {
// 异步执行完成后,通过此接口回传执行结果
});
loginAction.execute(account); // 异步执行

[b](2) 导入[/b]
[b](a) 服务器端[/b]
struts4rcp-server.jar

import com.googlecode.struts4rcp.*;
import com.googlecode.struts4rcp.server.*;

[b](b) 客户端[/b]
struts4rcp-client.jar

import com.googlecode.struts4rcp.*;
import com.googlecode.struts4rcp.client.*;

[b](c) Web页面[/b]
struts4rcp.js

<script type="text/javascript" src="struts4rcp.js"></script>

[b](3) 配置[/b]
[b](a) 服务器端 (web.xml)[/b]
最简配置:

<servlet>
<servlet-name>actionServlet</servlet-name>
<servlet-class>com.googlecode.struts4rcp.server.ActionServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>actionServlet</servlet-name>
<url-pattern>*.data</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>actionServlet</servlet-name>
<url-pattern>*.json</url-pattern>
</servlet-mapping>

全部配置:

<servlet>
<servlet-name>actionServlet</servlet-name>
<servlet-class>com.googlecode.struts4rcp.server.ActionServlet</servlet-class>
<init-param>
<param-name>serializers</param-name>
<param-value>
data: com.googlecode.struts4rcp.util.serialize.JavaSerializer,
json: com.googlecode.struts4rcp.util.serialize.JsonSerializer
</param-value>
</init-param>
<init-param>
<param-name>actionFactory</param-name>
<param-value>
com.googlecode.struts4rcp.server.factory.SpringActionFactory
</param-value>
</init-param>
<init-param>
<param-name>actionInterceptors</param-name>
<param-value>
com.xxx.XXXActionInterceptor,
com.yyy.YYYActionInterceptor
</param-value>
</init-param>
<init-param>
<param-name>exceptionConverters</param-name>
<param-value>
com.xxx.XXXExceptionConverter,
com.yyy.YYYExceptionConverter
</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>actionServlet</servlet-name>
<url-pattern>*.data</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>actionServlet</servlet-name>
<url-pattern>*.json</url-pattern>
</servlet-mapping>

Spring配置:

<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/beans.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.util.IntrospectorCleanupListener</listener-class>
</listener>

[b](b) 客户端 (transport.properties)[/b]
最简配置:

server.host=127.0.0.1
server.port=8080
context.path=xxx

全部配置:

transporter=com.googlecode.struts4rcp.client.transport.HttpClientTransporter
serializer=com.googlecode.struts4rcp.util.serialize.JavaSerializer
listeners=com.xxx.XXXListener,com.yyy.YYYListener
exceptionCatchers=com.xxx.XXXExceptionCatcher,com.yyy.YYYExceptionCatcher
server.host=127.0.0.1
server.port=8080
context.path=xxx
action.suffix=data

[b](c) Web页面 (struts4rcp.js)[/b]
最简配置:

// 不需要配置

全部配置:

Actions.suffix='json';

[b]3. 扩展[/b]
[b](1) 序列化器[/b]
[b]扩展点:[/b]
客户端(包括Web页面)与服务器端之间传递的数据格式转换。
[b]扩展接口:[/b]
[list]
[*] com.googlecode.struts4rcp.util.serialize.Serializer
[*] com.googlecode.struts4rcp.util.serialize.AbstractSerializer
[/list]
[b]内置实现:[/b]
[list]
[*] com.googlecode.struts4rcp.util.serialize.JavaSerializer
[*] com.googlecode.struts4rcp.util.serialize.JsonSerializer
[*] com.googlecode.struts4rcp.util.serialize.JBossSerializer
[*] com.googlecode.struts4rcp.util.serialize.XmlSerializer
[*] com.googlecode.struts4rcp.util.serialize.XStreamSerializer
[/list]
[b]注册方式:[/b]
[list]
[*] 客户端:struts4rcp.properties配置:serializer=com.xxx.XxxSerializer
[*] 服务器端:web.xml配置:
<init-param><param-name>serializers</param-name><param-value>data:com.xxx.XxxSerializer</param-value></init-param>

[/list]
[b](2) 服务器端Action实例化工厂[/b]
[b]扩展点:[/b]
服务器端Action实例的创建和装配。
[b]扩展接口:[/b]
[list]
[*] com.googlecode.struts4rcp.server.ActionFactory
[/list]
[b]内置实现:[/b]
[list]
[*] com.googlecode.struts4rcp.server.factory.SpringActionFactory
[*] com.googlecode.struts4rcp.server.factory.GuiceActionFactory
[*] com.googlecode.struts4rcp.server.factory.ReflectActionFactory
[/list]
[b]注册方式:[/b]
[list]
[*] ActionFactoryContext.getContext().setActionFactory(actionFactory);
[*] web.xml配置:
<init-param><param-name>actionFactory</param-name><param-value>com.xxx.XxxActionFactory</param-value></init-param>

[/list]
[b](3) 服务器端Action拦截器[/b]
[b]扩展点:[/b]
服务器端Action实例执行过程的前后拦截,可用于权限控制,前端检验等。
[b]扩展接口:[/b]
[list]
[*] com.googlecode.struts4rcp.server.ActionInterceptor
[/list]
[b]注册方式:[/b]
[list]
[*] ActionFactoryContext.getContext().addActionInterceptor(actionInterceptor);
[*] web.xml配置:
<init-param><param-name>actionInterceptors</param-name><param-value>com.xxx.XxxActionInterceptor</param-value></init-param>

[/list]
[b](4) 服务器端异常转换器[/b]
[b]扩展点:[/b]
转换客户端无法反序列化的服务器端异常,如:将Spring的事务异常转换为客户端识别的异常。
[b]扩展接口:[/b]
[list]
[*] com.googlecode.struts4rcp.server.ExceptionConverter
[/list]
[b]注册方式:[/b]
[list]
[*] ActionFactoryContext.getContext().addExceptionConverter(exceptionConverter);
[*] web.xml配置:
<init-param><param-name>exceptionConverters</param-name><param-value>com.xxx.XxxExceptionConverter</param-value></init-param>

[/list]
[b](5) 客户端传输器[/b]
[b]扩展点:[/b]
客户端与服务器端数据交换协议与策略实现。
[b]扩展接口:[/b]
[list]
[*] com.googlecode.struts4rcp.client.Transporter
[*] com.googlecode.struts4rcp.client.transport.AbstractTransporter
[*] com.googlecode.struts4rcp.client.transport.HttpTransporter
[/list]
[b]内置实现:[/b]
[list]
[*] com.googlecode.struts4rcp.client.transport.HttpClientTransporter
[*] com.googlecode.struts4rcp.client.transport.HttpURLConnectionTransporter
[/list]
[b]注册方式:[/b]
[list]
[*] struts4rcp.properties配置:transporter=com.xxx.XxxTransporter
[*] Actions.init(transporter, config);
[/list]
[b](6) 客户端异常捕获器[/b]
[b]扩展点:[/b]
当异步执行Action时,业务未处理的异常,由全局捕获器统一处理。
[b]扩展接口:[/b]
[list]
[*] com.googlecode.struts4rcp.client.ExceptionCatcher
[/list]
[b]注册方式:[/b]
[list]
[*] struts4rcp.properties配置:exceptionCatchers=com.xxx.XxxExceptionCatcher
[*] Actions.addExceptionCatcher(exceptionCatcher);
[/list]
[b](7) 客户端连接状态事件监听器[/b]
[b]扩展点:[/b]
客户端的网络状态改变时触发此事件。
[b]扩展接口:[/b]
[list]
[*] com.googlecode.struts4rcp.client.event.ConnectionListener
[*] com.googlecode.struts4rcp.client.event.ConnectionAdapter
[/list]
[b]注册方式:[/b]
[list]
[*] struts4rcp.properties配置:listeners=com.xxx.XXXConnectionListener
[*] Actions.getTransporter().addConnectionListener(listener);
[/list]
[b](8) 客户端传输状态事件监听器[/b]
[b]扩展点:[/b]
客户端正在与服务器传输数据时以及传输结束时触发此事件。
[b]扩展接口:[/b]
[list]
[*] com.googlecode.struts4rcp.client.event.TransportListener
[*] com.googlecode.struts4rcp.client.event.TransportAdapter
[/list]
[b]注册方式:[/b]
[list]
[*] struts4rcp.properties配置:listeners=com.xxx.XXXTransportListener
[*] Actions.getTransporter().addTransportListener(listener);
[/list]
[b](9) 客户端Action队列事件监听器[/b]
[b]扩展点:[/b]
客户端的Action队列中的任务数改变时触发此事件。
[b]扩展接口:[/b]
[list]
[*] com.googlecode.struts4rcp.client.event.ActionQueueListener
[*] com.googlecode.struts4rcp.client.event.ActionQueueAdapter
[/list]
[b]注册方式:[/b]
[list]
[*] struts4rcp.properties配置:listeners=com.xxx.XXXActionQueueListener
[*] Actions.getActionQueue().addActionQueueListener(listener);
[/list]
[b]4. 设计[/b]
[b](1) 部署图[/b]
[img]http://struts4rcp.googlecode.com/svn/trunk/docs/design/uml/deploy_diagram.gif[/img]
struts4rcp-client.jar组件:部署于客户端,包括:
[list]
[*] com.googlecode.struts4rcp包
[*] com.googlecode.struts4rcp.util包
[*] com.googlecode.struts4rcp.client包
[/list]
struts4rcp-server.jar组件:部署于服务器端,包括:
[list]
[*] com.googlecode.struts4rcp包
[*] com.googlecode.struts4rcp.util包
[*] com.googlecode.struts4rcp.server包
[/list]
[b](2) 类图[/b]
[img]http://struts4rcp.googlecode.com/svn/trunk/docs/design/uml/class_diagram.gif[/img]
包说明
[list]
[*] com.googlecode.struts4rcp包:客户端与服务器端交互面,共享模型。(中心接口:Action)
[*] com.googlecode.struts4rcp.client包:客户端实现。(入口类:Actions)
[*] com.googlecode.struts4rcp.server包:服务器端实现。(入口类:ActionServlet)
[*] com.googlecode.struts4rcp.util包:通用工具包,辅助实现。
[/list]
领域说明
[list]
[*] 黄色:服务域
[*] 红色:会话域
[*] 绿色:实体域
[/list]
[b](3) 序列图[/b]
[img]http://struts4rcp.googlecode.com/svn/trunk/docs/design/uml/sequence_diagram.gif[/img]
[list]
[*] Client:调用者
[*] Actions:客户端Action门面
[*] ActionProxy:客户端Action代理实现
[*] Transporter:客户端请求发送传输器(缺省采用HttpClientTransporter)
[*] Serializer:传输序列化器(缺省采用JavaSerializer)
[*] ActionServlet:服务器端请求接收器
[*] ActionFactory:服务器端Action实例化工厂(缺省采用SpringActionFactory)
[*] ActionIntercepter:服务器端Action执行过程拦截器链
[*] Action:服务器端Action实例(调用目标)
[/list]
项目地址:
[url]http://struts4rcp.googlecode.com[/url]
SVN地址:
[url]http://struts4rcp.googlecode.com/svn/trunk/code/struts4rcp[/url]
 类似资料: