即时通讯应用控制器与GWT - RPC实现解析
1. 视图定位与交互
在即时通讯应用中,窗口的定位和交互是基础功能。应用通过调用
DOM.getIntAttribute
获取
scrollTop
和
scrollLeft
属性,计算窗口的顶部和左侧位置。之后,使用CSS的
top
和
left
属性来设置这些位置,同时需要将
position
属性设置为
absolute
,这样浏览器就能正确渲染这些位置,因为该属性表明
top
和
left
的位置值是相对于页面左上角的。
MessengerView
在重新排列
ChatWindowView
实例或使用
show
方法显示
ChatWindowView
实例时会调用
setPosition
方法。
show
方法通过
setPosition
方法设置视图的宽度、高度和坐标,并将可见性设置为
true
,同时将键盘焦点设置到消息输入的
TextBox
小部件上,方便用户直接输入。
应用有四个视图,它们共同构建了一个界面,将应用的模型展示给用户,并允许模型在同一页面的其他用户之间传输。
MessengerView
管理子视图,包括用于获取用户显示名称的
SignInView
、显示当前页面用户联系人列表的
ContactListView
以及允许用户相互收发消息的
ChatWindowView
。这些视图的目标是通过显示最小化的界面来补充其他网页功能。
2. 控制器概述
即时通讯应用的控制器负责从视图接收事件、与服务器通信以及操作应用的模型对象。与视图和模型的交互相对直接,但实现控制器的难点在于为即时通讯服务器创建RPC接口,并在不直接支持基于事件协议的技术之上创建该协议。幸运的是,使用GWT - RPC实现可以节省时间,避免设计协议,并且为Java开发者提供直观的服务器通信方法。
控制器在应用生命周期中处理的事件序列如下:
1. 启动应用,设置主视图。
MessengerView
首先显示
SignInView
,等待用户登录。
2. 用户输入登录名后,控制器创建
ContactList
模型对象,并将登录名发送到服务器,服务器会向其他连接的客户端广播该用户已上线。
3. 登录步骤完成后,控制器通知视图显示联系人列表。
4. 控制器开始接收服务器的事件,如其他联系人上线或离线,并将这些事件处理后反映在视图中。
5. 用户发送消息时,控制器从视图接收事件,将消息转发到服务器,服务器再将消息发送给收件人。反之,当其他客户端向当前用户发送消息时,控制器接收消息事件并传递给视图进行渲染。
实现控制器可分为三个部分:
1.
入口点
:负责处理启动调用,创建与服务器的连接,并将视图附加到HTML宿主文档。
2.
客户端RPC接口
:实现远程通信,以响应视图的事件。
3.
服务器端RPC接口实现
:处理来自多个客户端的连接并转发事件。
以下是入口点在
Messenger
类中的实现代码:
public class Messenger implements EntryPoint{
public void onModuleLoad() {
MessengerServiceClientImpl messengerService =
new MessengerServiceClientImpl(GWT.getModuleBaseURL()+"messenger");
RootPanel.get("messengerView").add( messengerService.getView() );
}
}
这个类实现了GWT的
EntryPoint
接口及其
onModuleLoad
方法。当GWT加载应用时,首先调用该方法。方法中的代码创建了
MessengerServiceClientImpl
类的实例,该类负责与信使服务器和应用视图进行交互。
MessengerView
实例作为
MessengerServiceClientImpl
类的成员被实例化。在
Messenger
类的
EntryPoint
方法中,从
MessengerServiceClientImpl
实例中获取视图,并使用GWT的
RootPanel
类将其插入到宿主HTML页面中。
MessengerServiceClientImpl
类使用GWT - RPC库与服务器进行通信。
3. 使用GWT - RPC
Google Web Toolkit提供了许多工具用于以编程方式调用服务器。GWT - RPC是一种用于网络通信的附加技术,它基于标准浏览器功能,提供了对网络协议的高级抽象,是GWT独有的。RPC实现的目标是在客户端提供可调用的方法,这些方法最终会调用服务器上的类似方法,从而隐藏协议细节,减少开发者学习新技术的成本和编码错误。
GWT - RPC的工作原理是自动为服务器接口提供一个代理对象。客户端应用通过调用代理对象的方法与服务器通信,服务器处理来自代理的调用并将其分发到相应的Java方法实现。任何返回值都会发送回客户端的代理,客户端应用提供一个回调对象来接收服务器的返回值或失败消息。
以下是GWT - RPC涉及的类和接口关系:
| 客户端 | 服务器 |
| ---- | ---- |
|
MessengerServiceClientImpl
|
MessengerServiceImpl
|
|
MessengerService proxy
|
RemoteServiceServlet
|
|
MessengerServiceAsync
、
ServiceDefTarget
接口 |
MessengerService
接口 |
客户端生成的代理对象实现了
MessengerServiceAsync
接口和
ServiceDefTarget
接口,服务器端的
MessengerServiceImpl
实例实现了
MessengerService
接口并扩展了GWT的
RemoteServiceServlet
。
4. 远程服务接口
MessengerService
是一个普通的Java接口,用于定义与服务器进行远程通信的方法,其定义如下:
public interface MessengerService extends RemoteService {
void signIn( String name );
void signOut();
void sendMessage( Contact to, Message message );
}
GWT远程接口必须扩展GWT的
RemoteService
接口,该接口为空,仅作为标记,告知GWT编译器该接口将用于RPC通信。对于我们的应用,该接口定义了三个方法:
-
signIn
:用户在视图中提供显示名称时,控制器调用此方法,服务器实现该方法并向其他客户端广播该用户已上线。
-
signOut
:与
signIn
方法相反,通知所有连接的客户端该用户已离线。
-
sendMessage
:在服务器上实现,用于向指定联系人发送消息。该方法可以接受普通Java对象作为参数,也可以返回普通Java对象或基本值。要支持发送Java对象,需要在要发送的类上实现GWT的
IsSerializable
接口或Java的
Serializable
接口,并提供无参构造函数。例如:
public class Contact implements IsSerializable{
// 类的具体实现
}
当GWT编译器看到类上的该接口时,会生成将该类实例序列化以用于RPC实现的代码。
5. 远程服务Servlet类
服务器端的
MessengerServiceImpl
类实现了
MessengerService
接口,并且扩展了GWT的
RemoteServiceServlet
,而不是
HttpServlet
。
RemoteServiceServlet
类是一个
HttpServlet
,它会做一些额外的工作,将客户端的RPC调用转换为对类中方法实现的调用,使得在服务器上实现
MessengerService
变得简单干净,无需编写RPC代码。以下是一个空的服务器端RPC实现示例:
public class MessengerServiceImpl
extends RemoteServiceServlet implements MessengerService {
public void signIn( String name ){
}
public void signOut(){
}
public void sendMessage( Contact to, Message message ){
}
}
将以下行添加到应用的模块文件中,可将该Servlet注册到GWT的嵌入式Tomcat服务器:
<servlet path="/messenger"
class="com.gwtapps.messenger.server.MessengerServiceImpl"/>
客户端代码需要引用
/messenger
路径来连接服务器。在部署到Servlet容器时,也需要在
web.xml
文件中使用匹配的路径。
6. 使用异步接口
由于方法调用必须是异步的,因此不能直接使用服务器的
MessengerService
接口。异步接口
MessengerServiceAsync
与
MessengerService
接口匹配,但每个方法没有返回值,并且必须接受一个回调对象作为最后一个参数。当客户端通过接口方法调用服务器时,方法会立即返回,方法的返回值会作为对象传递给回调。
AsyncCallback
接口的定义如下:
public interface AsyncCallback {
void onFailure(Throwable caught);
void onSuccess(Object result);
}
MessengerServiceAsync
接口的定义如下:
public interface MessengerServiceAsync {
void signIn( String name, AsyncCallback callback );
void signOut( AsyncCallback callback );
void sendMessage(Contact to, Message message, AsyncCallback callback );
}
客户端代码要与服务器通信,首先需要创建一个代理对象,代码如下:
MessengerServiceAsync messengerService =
(MessengerServiceAsync) GWT.create( MessengerService.class );
通过
GWT.create
方法使用延迟绑定机制实例化类。GWT会根据
MessengerService
类实现了
RemoteService
接口,创建一个实现
MessengerServiceAsync
接口的代理实例。此时代理未连接,需要指定服务器URL:
ServiceDefTarget endpoint = (ServiceDefTarget) messengerService;
endpoint.setServiceEntryPoint( GWT.getModuleBaseURL() + "messenger" );
以下是从客户端调用服务器
signIn
方法的示例:
messengerService.signIn( name, new AsyncCallback(){
public void onFailure(Throwable throwable){
GWT.log("error sign in",throwable);
}
public void onSuccess(Object obj){
/* TODO: change view to acknowledge success */
}
});
代理会处理该方法,将参数序列化并发送到服务器。服务器完成方法调用后,会发送响应。如果远程调用成功,代理会调用回调实例的
onSuccess
方法,并将服务器的返回值作为
Object
参数传递;如果调用失败,则调用
onFailure
方法。
7. 连接到服务器
MessengerServiceClientImpl
类处理控制器的大部分工作,它实现了
MessengerViewListener
接口,用于与视图交互,并通过GWT - RPC调用服务器方法。该类的声明和相关方法如下:
public class MessengerServiceClientImpl implements MessengerViewListener{
private MessengerServiceAsync messengerService;
private ContactList contactList;
private MessengerView view = new MessengerView( this );
public MessengerServiceClientImpl( String url ){
messengerService = (MessengerServiceAsync) GWT.create(
MessengerService.class );
ServiceDefTarget endpoint = (ServiceDefTarget) messengerService;
endpoint.setServiceEntryPoint( url );
}
public MessengerView getView(){
return view;
}
public void onSignIn( String name ){
contactList = new ContactList( name );
messengerService.signIn( name, new SignInCallback() );
}
public void onSignOut(){
messengerService.signOut( new EmptyCallback() );
}
public void onSendMessage( Contact toContact, Message message ){
messengerService.sendMessage( toContact, message, new EmptyCallback());
}
}
onSignIn
方法根据视图提供的名称创建一个新的
ContactList
对象,并调用RPC方法
signIn
通知服务器用户已登录。应用使用
SignInCallback
内部类处理响应:
private class SignInCallback implements AsyncCallback{
public void onFailure(Throwable throwable){
GWT.log("error sign in",throwable);
}
public void onSuccess(Object obj){
view.setContactList( contactList );
}
}
当服务器上的
signIn
调用成功时,回调的
onSuccess
方法会被调用,将联系人列表设置到视图中,视图会从登录视图切换到联系人列表视图。
当浏览器窗口关闭或用户导航到其他URL时,视图会调用
onSignOut
方法,该方法简单地调用服务器的
signOut
方法,应用使用
EmptyCallback
内部类处理响应:
private class EmptyCallback implements AsyncCallback{
public void onFailure(Throwable throwable){
GWT.log("error",throwable);
}
}
综上所述,通过以上步骤和代码实现,我们可以构建一个完整的即时通讯应用,利用GWT - RPC实现客户端与服务器之间的高效通信,同时通过视图和控制器的协同工作,为用户提供良好的交互体验。
即时通讯应用控制器与GWT - RPC实现解析
8. 控制器工作流程总结
为了更清晰地理解控制器的工作流程,我们可以通过以下流程图展示:
graph LR
classDef startend fill:#F5EBFF,stroke:#BE8FED,stroke-width:2px
classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px
classDef decision fill:#FFF6CC,stroke:#FFBC52,stroke-width:2px
A([启动应用]):::startend --> B(设置主视图,显示SignInView):::process
B --> C{用户是否登录?}:::decision
C -- 是 --> D(创建ContactList对象,发送登录名到服务器):::process
D --> E(服务器广播用户上线):::process
E --> F(显示联系人列表):::process
F --> G{是否有联系人状态变化?}:::decision
G -- 是 --> H(更新视图显示):::process
G -- 否 --> I{用户是否发送消息?}:::decision
I -- 是 --> J(控制器转发消息到服务器):::process
J --> K(服务器发送消息到收件人):::process
I -- 否 --> L{是否收到其他客户端消息?}:::decision
L -- 是 --> M(控制器传递消息到视图渲染):::process
C -- 否 --> B
这个流程图展示了控制器在应用生命周期中的主要工作流程,从应用启动到用户登录、联系人状态管理以及消息收发等环节。
9. 关键类和接口总结
以下是对即时通讯应用中关键类和接口的总结表格:
| 类/接口名称 | 所在端 | 作用 | 相关方法 |
| ---- | ---- | ---- | ---- |
|
MessengerService
| 通用定义 | 定义与服务器进行远程通信的方法 |
signIn
、
signOut
、
sendMessage
|
|
MessengerServiceAsync
| 客户端 |
MessengerService
的异步版本,用于客户端调用 |
signIn
、
signOut
、
sendMessage
|
|
MessengerServiceImpl
| 服务器端 | 实现
MessengerService
接口,处理服务器端逻辑 |
signIn
、
signOut
、
sendMessage
|
|
MessengerServiceClientImpl
| 客户端 | 处理控制器的大部分工作,与视图和服务器交互 |
onSignIn
、
onSignOut
、
onSendMessage
|
|
AsyncCallback
| 客户端 | 用于处理服务器响应的回调接口 |
onFailure
、
onSuccess
|
|
Contact
| 通用 | 表示联系人的类,需实现序列化接口 | 无(示例中未给出具体方法) |
|
Message
| 通用 | 表示消息的类,需实现序列化接口 | 无(示例中未给出具体方法) |
10. 技术优势分析
使用GWT - RPC实现即时通讯应用具有以下几个显著优势:
-
简化开发
:GWT - RPC隐藏了网络协议的细节,开发者可以像调用本地方法一样调用远程方法,减少了学习和使用新网络协议的成本。例如,在客户端代码中,通过代理对象调用服务器方法,代码简洁易懂。
-
支持Java对象传输
:可以直接使用Java对象作为方法参数和返回值,只需实现相应的序列化接口。这使得开发者可以使用熟悉的Java编程模型,无需手动进行数据格式转换。
-
异步调用
:异步接口的使用避免了阻塞调用,不会影响浏览器的单线程JavaScript执行,保证了应用的流畅性。客户端在调用服务器方法时,方法会立即返回,通过回调函数处理服务器响应。
-
自动代码生成
:GWT编译器会根据接口定义自动生成代理对象和序列化代码,减少了开发者手动编写代码的工作量。
11. 潜在问题与解决方案
在使用GWT - RPC实现即时通讯应用时,可能会遇到以下潜在问题及相应的解决方案:
| 潜在问题 | 解决方案 |
| ---- | ---- |
| 网络延迟或中断 | 可以在回调函数中处理失败情况,例如在
onFailure
方法中进行重试或提示用户检查网络连接。同时,可以设置合理的超时时间,避免长时间等待。 |
| 序列化问题 | 确保要传输的类实现了
IsSerializable
或
Serializable
接口,并提供无参构造函数。如果遇到复杂对象的序列化问题,可以检查对象的属性是否也满足序列化要求。 |
| 服务器负载过高 | 可以对服务器进行性能优化,例如使用缓存、分布式系统等。同时,合理设计服务器端的处理逻辑,避免不必要的计算和资源消耗。 |
12. 未来扩展方向
基于现有的即时通讯应用实现,我们可以考虑以下几个未来扩展方向:
-
添加更多功能
:如群聊功能、消息撤回、消息提醒等。可以通过在
MessengerService
接口中添加新的方法,并在服务器端和客户端进行相应的实现。
-
优化用户界面
:可以使用更先进的前端框架和技术,如React或Vue.js,对应用的视图进行优化,提升用户体验。
-
增强安全性
:可以添加身份验证、加密传输等安全机制,保障用户信息和消息的安全。例如,在登录过程中使用更严格的身份验证方式,对消息内容进行加密处理。
通过以上对即时通讯应用控制器和GWT - RPC实现的详细分析,我们深入了解了如何构建一个完整的应用,从视图定位到控制器逻辑,再到客户端与服务器的通信。同时,我们也探讨了技术优势、潜在问题和未来扩展方向,为进一步优化和扩展应用提供了思路。在实际开发中,可以根据具体需求和场景,灵活运用这些技术和方法,打造出更加高效、稳定和安全的即时通讯应用。
超级会员免费看
48

被折叠的 条评论
为什么被折叠?



