软件体系结构设计Word文档下载推荐.docx
《软件体系结构设计Word文档下载推荐.docx》由会员分享,可在线阅读,更多相关《软件体系结构设计Word文档下载推荐.docx(13页珍藏版)》请在冰豆网上搜索。
电话用户姓名
表CallHistory
number文本类型;
电话号码(索引,允许重复)
startTime日期/时间类型;
起始通话时间
endTime日期/时间类型;
终止通话时间
其中,表TelephoneDirectory记录了所有用户的电话号码,以number为主关键码;
表CallHistory记录了所有电话的通话历史,其中number是外部关键码,建立该属性的允许重复的索引。
表CallHistory通过外部关键码number与表TelephoneDirectory相关联。
由于同一名字的用户可能登记多个电话号码(例如一个用户同时拥有两部住宅电话和一部移动电话),因而电话计费查询程序除了提供根据电话号码查询话费清单外,还可能提供以用户名字进行查询。
本系统设计后者的用法。
程序中利用JDBC/ODBC访问数据库,因而支持使用多种不同的数据库管理系统,只要这些数据库管理系统提供了ODBC接口,如MicrosoftAccess、MicrosoftSQLServer、Sybase、Oracle等。
使用ODBC访问数据库之前,必须将数据库配置为ODBC的一个数据源。
为进一步提高程序的数据独立性,还可以利用许多关系型数据库管理系统支持的“存储过程”来完成数据查询与更新操作。
数据独立性使得在数据库中表的属性或表之间的关联发生某些变化时,程序员无需对应用程序作任何修改。
使用存储过程访问数据库还可带来其他好处,例如提高了SQL语句查询与更新数据库的效率,在网络环境下加强了数据库的安全性等等。
在不同的数据库管理系统中,存储过程可能有不同的名字,如保留过程、触发器、查询等。
下面展示了在MicrosoftAccess中编写的一个存储过程(即一个查询),它根据指定电话用户名字的参数(TelephoneSubscriber)查询该用户登记的所有电话的通话记录。
本系统利用该存储过程实现对数据的查询。
查询QueryCallHistoryWithSubscriber
SELECTTelephoneDirectory.number,
CallHistory.startTime,
CallHistory.endTime
FROM
TelephoneDirectory,
CallHistory
WHERE(TelephoneDirectory.number
=
CallHistory.number)
AND
(TelephoneDirectory.subscriber
[Telephone
Subscriber])
ORDERBY
startTime;
3客户程序与服务程序之间的通信
客户程序与服务程序之间需要通信以交换信息。
在本系统中,服务程序负责接收客户程序发来的查询请求,完成访问数据库的查询操作,并将查询结果返回给客户程序。
客户程序利用图形用户界面与终端用户进行交互,从终端用户获取电话用户的名字并作为参数向服务程序发出查询请求,然后将服务程序返回的查询结果显示给终端用户浏览。
客户程序与服务程序可利用socket进行通信,socket在TCP/IP网络中是一种很原始、高效率的通信方式,但要求客户程序与服务程序自己处理交换消息的编码与解码工作,即程序员必须自定义客户程序与服务程序之间的应用层通信协议,这不仅需要花费大量的编程时间(例如必须考虑连接控制、错误恢复、如何通过防火墙等问题),同时也比较容易产生错误。
一种更好的通信方式是使用远程过程调用(RPC),它将客户程序与服务程序之间的通信接口抽象为过程调用层次,程序员可像调用本地过程一样去调用远程过程,由RPC系统完成参数与返回值的打包、解包与传输等底层任务。
但使用RPC不能平滑地与面向对象技术结合在一起。
本系统采用了抽象层次更高的远程方法调用(RMI)。
RMI是支持多层分布式对象计算的一系列Java类,使得Java应用程序员无需额外的程序设计工作即可实现客户程序与服务程序之间的连接与通信,在客户程序中可以像使用本地对象一样调用服务程序中远程对象的方法。
4远程方法调用
RMI可看作一种简化的、专用于Java平台的CORBA模型,由JDK1.1开始支持。
RMI的服务程序通常是一个Java应用程序,而客户程序既可以是一个Java应用程序,也可以是一个JavaApplet。
RMI注册表rmiregistry是运行在服务器上的一个后台进程,必须在服务程序启动之前就已启动完毕,它相当于客户程序与服务程序之间的通信网关。
服务程序将远程对象的名字注册到RMI注册表,客户程序通过RMI注册表将远程对象名字解析为远程对象引用,通过该对象引用调用远程对象上的方法。
RMI体系结构采用典型的层次设计风格,从上至下分别由桩/框架层、远程引用层和传输层组成,各层之间明确定义了接口与协议。
RMI系统采用类似CORBA的请求代理机制,桩(stub)是远程对象在客户端的代理,客户程序中的远程对象引用实际上是对本地桩的引用。
桩负责将远程调用请求通过远程引用层转发给服务端的框架(skeleton),再由服务端的框架分派给真正的远程对象实现。
创建应用程序时,客户程序与服务程序都需要桩,而框架仅服务程序需要。
远程引用层完成调用的语义,例如决定服务程序的对象是单个对象,还是需要与多个位置进行通信的复制对象,这些语义由远程对象的实现提供。
远程引用层还为上一层屏蔽了服务程序的激活方式,即桩/框架层不必关心提供远程对象的服务程序是一直在同一机器上运行,还是仅在有方法调用时才被激活(JDK1.2开始支持不同的激活策略)传输层负责建立与管理连接,跟踪远程对象,以及将调用请求分派给合适对象。
在服务端,传输层将调用请求向上转发给远程引用层,远程引用层作相应处理后转发给框架,由框架向上调用远程对象的实现,由远程对象的实现完成真正的方法调用。
远程调用的返回值送回客户端的路线是:
首先经过服务端的框架、远程引用层和传输层,再向上经过客户端的传输层、远程引用层和桩。
RMI在桩/框架层利用了两种关键技术。
首先,Java语言专用的对象串行化技术可将对象透明地传送到不同地址空间,桩与框架利用对象串行化技术打包(marshal)与解包(unmarshal)远程调用的参数与返回值。
其次,动态类装载技术用于支持将客户端的桩作为远程对象本身,桩实现了相同的远程接口集合,从而支持Java语言的类型检查与类型转换机制。
5接口定义
一个基于RMI的多层模型分布式应用程序通常包括以下几部分:
①远程接口,规定了客户程序与服务程序进行交互的界面;
②远程对象实现,为远程接口规定的每一个方法提供真正的实现;
③服务程序,远程对象并不是服务程序本身,它需要由服务程序创建并注册,服务程序中的这些真正提供服务的对象实例又称伺服对象(servant);
④客户程序,利用服务程序中伺服对象提供的服务完成某一功能。
其中,远程接口是编写服务程序与客户程序之前需要首先考虑的问题。
Java提供了接口与类两种机制:
接口不含数据表示方法与操作的具体实现,因而适用于定义对象的规格说明(specification),一个接口可以同时继承多个接口;
类给出了数据表示方法与操作实现,因而适用于定义对象的实现(implementation),仅支持对类的单继承。
所有远程对象的接口都使用接口来定义,并且必须继承java.rmi.Remote接口,还要求其中的每一个方法必须声明抛出java.rmi.RemoteException异常,因为网络通信或服务程序等原因均可能导致远程调用失败。
下面程序1-1给出了系统程序中通话记录管理器的远程接口定义
程序1-1CallManagerInterface.java
//通话记录管理器CallManager的远程接口
packageTelephone;
publicinterfaceCallManagerInterface
extendsjava.rmi.Remote
{
//根据电话用户名字查询通话记录。
//参数:
subscriber-电话用户的名字
publicDatabase.DatabaseTableModelgetCallHistory(Stringsubscriber)
throwsjava.rmi.RemoteException;
}
6服务端程序
程序1-2定义的类CallManager为远程接口CallManagerInterface中的每一个远程方法提供了具体实现。
为防止多个客户程序并发地调用数据库查询操作,方法getCallHistory()被定义同步方法。
程序1-3所示ServerApplication是服务程序的主程序,它必须首先装入安全管理器,用于保证动态装载的类不会执行某些敏感操作,如果未指定安全管理器则不允许装入任何RMI类。
程序1-2CallManager.java
//通话记录管理器(即远程接口CallManagerInterface的实现)
publicclassCallManager
extendsjava.rmi.server.UnicastRemoteObject
implementsCallManagerInterface
//属性定义
protectedDatabase.DatabaseAccessdatabase;
//缺省构造方法,必须抛出RemoteException异常。
publicCallManager()
throwsjava.rmi.RemoteException
database=newDatabase.DatabaseAccess();
}
//根据电话用户名字subscriber查询通话记录,实现远程接口指定的方法。
publicsynchronizedDatabase.DatabaseTableModelgetCallHistory(Stringsubscriber)
{
Stringsql="
"
;
//SQL查询语句
Database.DatabaseTableModeltable=null;
//返回的二维表模型
System.out.println("
Respondtoclientrequest:
"
+subscriber);
try{
sql="
QueryCallHistoryWithSubscriber('
+subscriber+"
'
)"
java.sql.ResultSetrs=database.callQuery(sql);
table=newDatabase.DatabaseTableModel(rs);
rs.close();
}catch(java.sql.SQLExceptionexc){
System.out.println(exc.getMessage());
System.exit
(1);
returntable;
程序1-3ServerApplication.java
//服务程序的主程序
publicclassServerApplication
finalstaticStringJDBC_DRIVER="
sun.jdbc.odbc.JdbcOdbcDriver"
publicstaticvoidmain(Stringargs[])
//为RMI设置安全管理器
System.setSecurityManager(newjava.rmi.RMISecurityManager());
//加载JDBC驱动程序
Class.forName(JDBC_DRIVER);
}catch(ClassNotFoundExceptionexc){
//创建并注册伺服对象
try{//创建伺服对象
Telephone.CallManagercallManager=newTelephone.CallManager();
//用名字"
CallManagerServant001"
注册伺服对象
java.rmi.Naming.rebind("
callManager);
}catch(java.rmi.RemoteExceptionexc){
}catch(.MalformedURLExceptionexc){
//提示服务程序就绪
Callmanagerintheserverisready..."
);
程序1-4定义的DatabaseAccess类与程序1-5定义的DatabaseTableModel类均用于抽象JDBC访问数据库的行为,在实际应用中我们通常会设计更完善、更个性化的数据库访问程序包来包装JDBC的API。
DatabaseAccess主要用于管理服务程序与数据库的连接,并完成数据库的查询与更新操作。
DatabaseTableModel负责将数据库查询结果转换为二维表数据模型的形式,方便利用Swing的二维表控件显示查询结果。
在基于关系数据库的应用中,二维表控件是最常用的数据表达方式,对于某些具有层次结构的数据则以树控件表达会更加自然。
程序1-4DatabaseAccess.java
//实现JDBC与数据库的连接packageDatabase;
importjava.sql.*;
publicclassDatabaseAccess{
//常量定义
protectedfinalStringDATABASE_NAME="
jdbc:
odbc:
Telephone"
protectedConnectionconnection;
//为数据库建立的连接protectedStatementstatement;
//将执行的SQL语句protectedCallableStatementcallable;
//将调用的SQL存储过程语句
//行为定义
//构造方法,建立与数据库的连接
publicDatabaseAccess(){
//建立与指定数据库的连接
connection=DriverManager.getConnection(DATABASE_NAME);
//如果连接成功则检测是否有警告信息
SQLWarningwarn=connection.getWarnings();
while(warn!
=null){
System.out.println(warn.getMessage());
warn=warn.getNextWarning();
//创建一个用于执行SQL的语句
statement=connection.createStatement();
callable=null;
}catch(SQLExceptionexc){
System.exit
(1);
}}
//析构方法,撤销与数据库的连接。
publicsynchronizedvoidfinalize(){
connection.close();
//利用存储过程执行数据库查询操作。
procedure-存储过程名字
//返回:
查询结果集
publicsynchronizedResultSetcallQuery(Stringprocedure)
throwsSQLException{
callable=connection.prepareCall("
{call"
+procedure+"
}"
ResultSetrs=callable.executeQuery();
returnrs;
}
程序1-5DatabaseTableModel.java
//根据数据库查询结果构造供JTable控件使用的二维表数据模型
packageDatabase;
importcom.sun.java.swing.*;
importjava.sql.*;
importjava.util.Vector;
publicclassDatabaseTableModel
extendscom.sun.java.swing.table.AbstractTableModel{
protectedString[]titles;
//列标题
protectedint[]types;
//各列的数据类型
protectedVectordata;
//二维表的数据
//构造方法,根据SQL查询结果集rs构造二维表。
publicDatabaseTableModel(ResultSetrs){
//取得结果集的元数据
ResultSetMetaDatameta=rs.getMetaData();
intcolumnCount=meta.getColumnCount();
//取所有列标题与类型名字(注意JDBC元数据的下标从1开始计数)titles=newString[columnCount];
types=newint[columnCount];
for(intindex=1;
index<
=columnCount;
index++){
titles[index-1]=meta.getColumnName(index);
types[index-1]=meta.getColumnType(index);
//逐行取结果集中的数据
data=newVector(1000,100);
while(rs.next()){
Vectorrow=newVector(30);
index<
index++)row.addElement(rs.getObject(index));
row.trimToSize();
data.addElement(row);
data.trimToSize();
//实现AbstractTableModel遗留的抽象方法。
publicintgetRowCount(){returndata.size();
publicintgetColumnCount(){returntitles.length;
publicStringgetColumnName(intcol){returntitles[col];
publicObjectgetValueAt(introw,intcol){
return((Vector)data.elementAt(row)).elementAt(col);
}}
7客户端程序
客户程序必须获取远程对象引用才可调用远程对象的方法。
一般情况下,远程对象引用可通过其他远程方法调用的参数或返回值获取,但最早的远程对象引用则必须借助基于URL的注册表,将远程对象标识解析为远程对象引用。
程序1-6给出的查询程序QueryPanel.java中,用于解析远程对象的对象标识为“CallManagerServant001”,它必须与服务程序注册远程对象时采用的名字完全相同。
由于构造远程对象的URL时必须包含主机名,所以QueryPanel的构造方法将当前运行的Applet作为参数。
注意程序中远程对象callManager必须声明为远程对象接口CallManagerInterface的实例,而不是远程对象实现类CallManager的实例。
程序1-6QueryPanel.java
//查询时的图形用户界面packageTelephone;
importjava.awt.*;
publicclassQueryPanelextendsJPanel
implementsjava.awt.event.ActionListener{
//属性
protectedJAppletapplet;
//当前正在运行的AppletprotectedJTextFieldsubscriberTextField;
//输入用户名字的字段protectedJPaneltablePanel;
//显示二维表的面板
//构造方法,显示图形用户界面。
//参数:
applet-启动查询的AppletpublicQueryPanel(JAppletapplet){
this.applet=applet;
setLayout(newBorderLay