从Spring实例入手谈SCA Component的创建和调用
2009-10-21 00:00:00 来源:WEB开发网面向服务组件的架构(Service Component Architecture,SCA),是目前业界最前沿的技术概念之一。但是对于很多开发人员来说,如何在SCA的架构上进行设计和开发还是显得相对抽象的。WID(WebSphere Integration Developer)和WPS(WebSphere Process Server)的推出,使得客户能够更加简单地向面向组件编程模型转变。本文将介绍在SCA编程模型中创建和调用SCA Component的基本概念和方法,并以一系列简单的实例来说明在不同场景中如何使用WID进行Component的创建和调用。SCA支持运用多种技术实现组件,Spring framework凭借依赖注入思想,成为这些技术中的天然一员。
引言
面向服务组件的架构(Service Component Architecture,SCA),是目前业界最前沿的技术概念之一。但是对于很多开发人员来说,如何在SCA的架构上进行设计和开发还是显得相对抽象的。WID(WebSphere Integration Developer)&WPS(WebSphere Process Server)的推出,使得客户能够更加简单地向面向组件编程模型转变。本文将介绍在SCA编程模型中创建和调用SCA Component的基本概念和方法,并以一系列简单的实例来说明在不同场景中如何使用WID进行Component的创建和调用。SCA支持运用多种技术实现组件,Spring framework凭借依赖注入思想成为这些技术中的天然一员,文中的实例实现了基于Spring framework的SCA组件,期待能够抛砖引玉,并为读者以后深入了解SCA打下基础。
读者定位为具有WID开发经验的开发人员,对SOA和SCA,Spring有所了解。
1.SCA简介
SCA是为了构建SOA系统而设计的一种规范,目前的版本是0.9。SCA的核心概念是服务及其相关实现。服务由接口定义,而接口包含一组操作。服务实现可以引用其他服务,称为引用。服务可以有一个或多个属性,这些属性是可以在外部配置的数据值。SCA使开发者把更多的精力集中在业务逻辑上。组件(component)的提出,在底层应用实现和service之间增加了一层,图1SOA架构图可以直观的找到组件的位置。这样不必把每个单独的功能都定义为service,更加优化了服务的颗粒度。
图1 SOA架构图
图片看不清楚?请点击这里查看原图(大图)。
2.实例介绍
实例主要实现查询账户余额信息的业务功能,主要体现在多种不同场景中如何构建基于SCA的应用。实例从构建一个基于Sprig framework的组件开始,层层深入,逐步展现对组件、组件之间、模块之间的方法的调用。图2展示了实例最终整体框架图,通过此图可以对这个实例有一个整体的认识。
图2 实例整体框架
图片看不清楚?请点击这里查看原图(大图)。
3.创建一个基于Spring实现的component
Web service并不是实现SOA的唯一技术,SCA支持多种技术实现。目前服务接口支持WSDL和Java两种,以后将会扩展到其他接口语言。构建SCA 的服务组件可以采用EJB,Spring bean和CORBA 等多种技术,可以应用Java、PHP、C++等多种语言。
Spring作为一种非侵略性的,轻量级的framework能很好地与SCA相融合,下面这段文字出自SCA白皮书的原文。
SCA views Spring as a natural partner which can be used as a component implementation technology. The Spring framework can be used to create components and wire them within a module using its dependency injection capabilities. SCA may be used to extend the capabilities of Spring components by publishing Spring beans as entry points to be accessed as services by other modules as well as by providing Spring beans with service references wired to services of other modules.
以此可以看出,SCA和Spring有着密切的关系,可以相互补充。相信未来SCA和Spring会有更紧密的融合,以更大更好地发挥各自的优点。
3.1 SCA和Spring的共同思想:依赖注入
从GoF设计模式中,我们已经习惯一种思维编程方式:Interface Driven Design 接口驱动,接口驱动有很多好处,可以提供不同的灵活的子类实现,增加代码稳定和健壮性等等。依赖注入(Dependency Injection)又称为控制反转(Inversion of Control)是解决调用者和被调用者之间关系的一种模式,它基于接口编程,在调用时进行对象的实例化,降低了耦合度,提高了重用性。在表1中给出了依赖注入的三种模式。
表1依赖注入有三种模式
类型 | 方式 | 应用框架 |
第一种类型 | 从JNDI或ServiceManager等获得被调用者,这里类似ServiceLocator模式。 | 1.SCA 2. EJB/J2EE |
第二种类型 | 使用JavaBeans的setter方法 | 1. Spring Framework 2. WebWork/XWork |
第三种类型 | 在构造方法中实现依赖 | 1. PicoContainer |
SCA组件主要通过接口进行交互。组件提供的服务是一个由接口定义的操作的集合,组件引用其他组件提供的服务也是通过该服务定义的接口来进行。SCA主要是应用表1中第一种依赖注入类型。
Spring Framework是构建Java应用的一个流行平台,您可以通过developerWorks的文章"Spring 系列: Spring 框架简介" 来了解Spring框架。Spring提供了IoC容器,一般采用表1中第二种通过JavaBeans的setter方式实现依赖注入。通过在 Spring配置文件ApplicationContext.xml中描述bean的信息,Spring IoC将根据这个文件在容器中实例化bean,再通过BeanFactory(在org.springframework.beans.facotry包中)找到实例化的bean。
因此在本质上SCA和Spring实例化对象的思想是一致的,都在不同程度上应用了依赖注入模式,和基于接口的实现。SCA将Spring视为实现 component众多技术中的天然一员,能够通过Spring framework创建component,也可以在同一Module中连接这些component。下面将介绍如何基于Spring来创建SCA component。
3.2如何基于Spring来创建SCA component
现在我们开始创建实例,在这一步中将应用Spring框架构建一个component,这个component的功能是根据输入的customerID,返回该customerID的余额balance和币种currency。
首先创建名为SimpleBankModule的Module,如下图所示。然后创建基于Spring framework的实现,在这点无需考虑SCA,只是创建Spring应用实现。
在实际应用中,一般都是从数据库获取customer的账户信息,Spring framework提供了强大的数据库操作的支持,Spring DAO封装了数据库操作,操作更简单。实例将从数据库中取得账户信息,但为了让读者更快的将实例运行起来也提供了dummy数据。您只需进行描述配置就可以选择不同的数据来源,这也体现了Spring framework的灵活性。如清单1所示Spring Bean的描述文件ApplicationContext.xml
清单1. ApplicationContext.xml.<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
|(1) <bean id="JDBCConn"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName">
<value>COM.ibm.db2.jdbc.app.DB2Driver</value>
</property>
<property name="url">
<value>jdbc:db2:sample</value>
</property>
<property name="username">
<value>username</value>
</property>
<property name="password">
<value>password</value>
</property>
</bean>
|(2) <bean id="DBAccountInfo"
class="com.ibm.simplebank.impl.QueryBalanceDBImpl">
<property name="dataSource">
<ref local="JDBCConn" />
</property>
</bean>
|(3) <bean id="DummyAccountInfo"
class="com.ibm.simplebank.impl.QueryBalanceDummyImpl">
<property name="balance">
<value>100.00</value>
</property>
<property name="currency">
<value>USD</value>
</property>
</bean>
|(4) <bean id="QueryBalance"
class="com.ibm.simplebank.impl.QueryBalanceImpl">
<property name="accountInfo">
<!-- 从数据库中取得数据ref到"DBAccountInfo",使用dummy数据ref到"DummyAccountInfo" -->
|-------10--------20--------30--------40--------50--------60--------70--------80--------9|
|-------- XML error: The previous line is longer than the max of 90 characters ---------|
<ref local="DummyAccountInfo" />
</property>
</bean>
</beans>
其中(1)这个bean用来建立数据库连接,可以选择不同的数据库及数据库连接方式;(2)和(3)的实现类都实现了同一个接口 SimpleBankDAO,这个接口的方法就是getAccountInfo(String customerID),(2)的实现类QueryBalanceDBImpl是查询数据库取得数据的实现,(3)的实现类 QueryBalanceDummyImpl只是将其属性实例化。(4)才是真正对外的实现类,更改它的引用实现从数据库取得数据还是用dummy数据。
现在我们创建这个component对外的接口,为了与Spring相结合,所以在定义接口时要定义Java接口,在其中声明要提供的业务方法。在 Business Integration视图中新建Interface默认的是WSDL 接口,所以要在新建向导中新建Java接口,如下图所示:
图片看不清楚?请点击这里查看原图(大图)。
清单2是QueryBalance接口,通过这个接口实现这个component与外交互。
清单2. QueryBalance.java.public interface QueryBalance {
public AccountInfo getBalance(String customerID);
}
然后在Business Integration视图中,点开SimpleBankModule的Assembly Diagram,添加component,并命名为QueryBalanceComponent,然后为这个component添加刚才创建的Java接口QueryBalance,如下图所示:
生成QueryBalanceComponent的Java实现,双击QueryBalanceComponent并选择Java实现,进入实现类。在实现类中加载Spring的ApplicationContext.xml,并实现getBalance方法。如清单3所示。
清单3 QueryBalance.java.private ClassPathXmlApplicationContext appContext = new ClassPathXmlApplicationContext(
"ApplicationContext.xml");
private BeanFactory factory = (BeanFactory) appContext;
public AccountInfo getBalance(String customerID) {
QueryBalanceImpl queryBalance = (QueryBalanceImpl) factory.getBean("QueryBalance");
|-------10--------20--------30--------40--------50--------60--------70--------80--------9|
|-------- XML error: The previous line is longer than the max of 90 characters ---------|
SimpleBankDAO dao = queryBalance.getAccountInfo();
AccountInfo accountInfo = dao.getAccountInfo(customerID);
return accountInfo;
}
QueryBalanceComponent这个组件就创建完了,单元测试其是否工作正常。
4.在SCA Module内部调用component
上面创建的QueryBalanceComponent实现了通过customerID查询账户信息的功能,但在真实应用中在查询账户信息前需要得到授权,因此要加上权限认证。考虑到权限认证是一个相对独立的功能模块,并且在整个系统中都需要使用,所以考虑在一个单独的Module中实现权限认证。为了保证应用的可扩展性,在SimpleBankModule中增加SimpleBankFacade组件,来调度对整个Module的请求。
在同一SCA Module内部,component之间通过reference进行交互。通过接口实现跨组件的操作。
下面将实现SimpleBankFacade组件,如图3所示,这个component封装了对QueryBalanceComponent的引用,负责调度组件的请求,方便以后系统的扩展。
1) 创建数据类型FacadeOutput
2) 创建WSDL类型接口SimpleBankFacade
图片看不清楚?请点击这里查看原图(大图)。
3) 创建组件SimpleBankFacade
创建一个组件并命名为SimpleBankFacade,然后为这个component添加刚才创建的WSDL接口SimpleBankFacade
4) 添加SimpleBankFacade对QueryBalanceComponent的引用
在两个组件间Add wire,添加引用。
5) 实现组件SimpleBankFacade
双击SimpleBankFacade并选择Java实现,WID会自动生成一些实现代码,其中已经实现了对 QueryBalanceComponent的引用。如清单4中(1)就是引用的实现,通过调用这个方法就能得到 QueryBalanceComponent的引用。我们做的就是在(2)下面加入业务实现。(3)创建输出对象,其中 http://SimpleBankModule为数据对象FacadeOutput的Namespace。(4)通过(1)取得 QueryBalanceComponent的引用。(5)调用QueryBalanceComponent的接口声明的方法。
清单4 SimpleBankFacadeImpl.java.public QueryBalance locateService_QueryBalancePartner() {
return (QueryBalance) ServiceManager.INSTANCE
.locateService("QueryBalancePartner"); |(1)
}
public DataObject QueryBalance(String customerID, String password) {
//TODO Needs to be implemented. |(2)
ServiceManager serviceManager = new ServiceManager();
BOFactory bof = (BOFactory) serviceManager
.locateService("com/ibm/websphere/bo/BOFactory");
DataObject facadeOutput = bof.create("http://SimpleBankModule",
"FacadeOutput"); |(3)
QueryBalance queryBalance = locateService_QueryBalancePartner(); |(4)
AccountInfo accountInfo = (AccountInfoImpl) queryBalance
.getBalance(customerID); |(5)
facadeOutput.setBoolean("isAuthed", true);
facadeOutput.setString("currency", accountInfo.getCurrency());
facadeOutput.setFloat("balance", accountInfo.getBalance());
return facadeOutput;
}
6) 单元测试组件SimpleBankFacade,看这个两个组件是否工作正常。
5.在SCA Module之间调用component
按照上一步的分析,需要单独创建一个Module来实现权限认证,在这部分将实现这个组件并将在SimpleBankModule中调用。这一步完成后实例的结构如下图所示。
在两个Module中的Component进行通信时需要将一个component导出,然后另一个component再导入。在导出导入时可以选择SCA binding也可以选择Web service binding,具体选择哪种方式要根据具体环境而定。如果两个模块在一个subsystem中(或者说在同一个应用服务器上)可以直接选择SCA binding;相反则要选择web service binding,通过WSDL进行调用交互。通过WSDL调用的方式与引用外部service一样,不再详细介绍,下面主要介绍如何通过SCA binding的方式进行跨module的component之间的调用。
1) 创建SimpleBankModule,在其中创建并实现AuthenticationComponent AuthenticationComponent的接口为Authentication,如下图所示,其实现只判断customerID是否为空,如果不为空则认证成功,否则失败。
图片看不清楚?请点击这里查看原图(大图)。
2)
3) 将接口Authentication复制到SimpleBankModule中为了在SimpleBankModule中引用AuthenticationComponent,要将接口Authentication复制到 SimpleBankModule中。也可以创建一个library然后将接口Authentication定义在这个library里面,其它 module添加对这个library的依赖从而共同使用这个接口。
4) 在SimpleBankModule中添加对AuthenticationComponent的引用首先添加import,并将其改名为ImportAuthentication。然后为ImportAuthentication添加接口Authentication 再为ImportAuthentication生成SCA binding
为ImportAuthentication选择要import的服务AuthenticationComponentExport
5) 添加SimpleBankFacade对ImportAuthentication的引用
6) 重新生成SimpleBankFacade的实现
重新生成后WID自动实现了对AuthenticationComponent的引用,如清单5中的(1),这样在调用时与Module内部 component之间的调用一样。(2)通过(1)取得对AuthenticationComponent的引用,(3)调用方法取得验证结果。
清单5 SimpleBankFacadeImpl.java.public Authentication locateService_AuthenticationPartner() {
return (Authentication) ServiceManager.INSTANCE
.locateService("AuthenticationPartner"); |(1)
}
public DataObject QueryBalance(String customerID, String password) {
... ...
Authentication authentication = locateService_AuthenticationPartner();
|(2)
boolean isAuthed = authentication.GetAuth(customerID, password)
.booleanValue(); |(3)
facadeOutput.setBoolean("isAuthed", isAuthed);
if (isAuthed) {
QueryBalance queryBalance = locateService_QueryBalancePartner();
AccountInfo accountInfo = (AccountInfoImpl) queryBalance
.getBalance(customerID);
facadeOutput.setBoolean("isAuthed", true);
facadeOutput.setString("currency", accountInfo.getCurrency());
facadeOutput.setFloat("balance", accountInfo.getBalance());
}
return facadeOutput;
}
7) 单元测试
要启动WPS,然后将这两个Module添加到server上,然后在测试时选择在WPS上测试。
这样将process的流程写在了SimpleBankFacade这个component的实现中,这个component也可以用process component代替,这样就可以通过Java或者BPEL语言来修改整个应用的process。
6.客户端与SCA Module交互的方式
通过上面的步骤已经实现了查询账户信息的业务功能,但是一直都是在Test component模式下调用组件。在这一步将创建客户端,从客户端调用刚才创建的服务。这一步完成后实例的结构如下图所示。
客户端调用component服务有两种实现方式,一种是在Module内部用Standalone的方式,一种是将component服务发布为 web service然后通过WSDL进行调用。第一种方式的使用范围比较有限,只限于在Module内部,但是在安全、性能等方面要比通过web service的方式好。
6.1 SCA module内部直接引用 (Stand alone)
当客户端只是在Module内部时,无需将服务发布为web service,即可直接通过standalone的方式调用。通过standalone的方式调用component与component之间调用的方法类似。
首先在SimpleBankModule内添加standalone引用,如下图所示。
然后将其引用到SimpleBankFacade,引用的默认名字为SimpleBankFacadePartner。最后实现服务的调用,下面是调用SimpleBankFacade的核心代码。如清单6所示,(1)通过standalone引用的名字引用SimpleBankFacade的服务。
清单6 SimpleBankFacadeImpl.java.String customerID = request.getParameter("customerID");
String password = request.getParameter("password");
ServiceManager serviceManager = new ServiceManager();
SimpleBankFacade service = (SimpleBankFacade) serviceManager
.locateService("SimpleBankFacadePartner"); |(1)
DataObject facadeOutput = service.QueryBalance(customerID, password);
boolean isAuthed = facadeOutput.getBoolean("isAuthed");
float balance = facadeOutput.getFloat("balance");
String currency = facadeOutput.getString("currency");
6.2调用SCA Module发布出来的web service
但是很多时候客户端并不在Module内部,有时甚至不在一个Application server上,这时就需要将服务发布为web service。
当SCA Module发布出来web service以后,对于客户端来说web service的实现框架、实现技术、数据来源等都是透明的。客户端只关心web service的WSDL。因此这一部分与SCA无关,但作为整个实例的一部分,将结合实际开发经验谈谈客户端调用web service。
首先要将组件SimpleBankFacade Export为web service binding,然后在WPS的administrative console,找到应用SimpleBankModuleApp,点击"Publish WSDL files",如下图所示,下载WSDL的zip文件。
然后将这个zip文件import 到工作区的一个临时项目中,在wsdl文件上点击右键,选择Web Services'Test with Web Services Explorer来测试服务是否工作正常。
测试成功后选择Web Services'Generate Client生成客户端。在生成客户端时可以选择客户端类型,如下图所示。
图片看不清楚?请点击这里查看原图(大图)。
6.2.1 用Java client模式调用
选择Java类型客户端,WID会自动生成的一些Java文件来调用Web service。其中在 SimpleBankFacadeExport_SimpleBankFacadeHttpServiceLocator.java中已经将web service的信息写死了,在Java application中通它来调用web service。清单7是通过自动生成的代理类调用web service的核心代码。
清单7 QueryBalanceJavaApplication.java in SimpleBankJavaClient project.SimpleBankFacadeExport_SimpleBankFacadeHttpServiceLocator serviceLocator =
new SimpleBankFacadeExport_SimpleBankFacadeHttpServiceLocator();
SimpleBankFacade simpleBankFacade = serviceLocator
.getSimpleBankFacadeExport_SimpleBankFacadeHttpPort();
|-------10--------20--------30--------40--------50--------60--------70--------80--------9|
|-------- XML error: The previous line is longer than the max of 90 characters ---------|
FacadeOutput output = simpleBankFacade.queryBalance(customerID,
password);
6.2.2 用web client模式调用
选择Web类型客户端,在WID自动生成的文件中除了有上面选择Java类型客户端生成的Java文件外,还添加和修改一些Web应用的配置文件。如在web.xml文件中加入了引用web service的JNDI描述,这样在web客户端就可以通过JNDI名字调用web service,清单8是通过JNDI名字调用web service的核心代码。
清单8 QueryBalance.java in SimpleBankWebClient project.InitialContext ic = new InitialContext();
Service service = (Service) ic.lookup("java:comp/env/service/SimpleBankFacadeExport_
|-------10--------20--------30--------40--------50--------60--------70--------80--------9|
|-------- XML error: The previous line is longer than the max of 90 characters ---------|
SimpleBankFacadeHttpService");
SimpleBankFacade simpleBankFacade =
(SimpleBankFacade)service.getPort(SimpleBankFacade.class);
FacadeOutput output = simpleBankFacade.queryBalance(customerID,
password);
7.通过ESB调用SCA Module
通过上面这些步骤实例已经完成了,但是再从整个实例架构的角度考虑,会发现web service的调用者和提供者仍是紧耦合的,调用者依赖于提供者发布的WSDL文件。服务提供者对服务接口的改变会使调用者无法正常使用。通过使用 ESB降低服务的调用者和提供者的耦合度,可以很好的解决这个问题。
ESB是调用服务的客户机和这些服务的提供者之间的中间件,负责处理它们之间的连接任务,从而简化了客户机和提供者,整个调用过程对服务两端是透明的,如下图所示。
图片看不清楚?请点击这里查看原图(大图)。
Service Integration Bus在WebSphere Application Server中,是早期IBM实现ESB产品,现在虽然可以作为一种开发策略,但已不推荐使用,推荐使用WESB提供的ESB服务。
WESB(WebSphere Enterprise Service Bus)为构建在开放标准和SOA的IT环境提供了一个ESB,相对Sibus来说它使构建中间层更简单,更容易管理,可以在前期开发中介功能,提供更广泛的连接。WID为WESB提供了一个很好的开发工具,通过构建Mediation module来提供ESB服务,如下图WESB的中介模型图。
Mediation module是一个特殊的SCA module,该模块在SCA的模块之间,以及SCA和非SCA之间建立了交流的桥梁。通过Mediation module可以截取、修改服务请求者(SCA Export)和服务提供者(SCA Import)之间传递的消息,从而减少依赖、降低耦合度。您可参照developerWorks的文章WebSphere Integration Developer中的中介模块来更多了解Mediation module,进而构建一个Mediation module来进一步完善实例。
8.结束语
本文从创建一个基于Spring framework的component开始,进而实现多个component的协同工作,再到Module之间component的交互,最后实现客户端调用component的service的几种方式方法,以一个开发者的角度介绍了SCA Component的创建和调用。对照图2实例整体框架和图1SOA结构图,实例在一定程度上基于SCA实现了SOA。
SCA支持多种技术和语言创建component,本文将Spring与SCA相融合旨在抛砖引玉,期待SCA与Spring等其他技术有更紧密的融合,以更加方便系统的迁移和整合,以及系统的维护和扩展。
同时也发现,依赖注入模式也为开发者带来了一些问题,比如实例化对象时更依赖于名字而不是创建类,这样使一些潜在的错误在编译时无法触发,只有在运行时才能捕获。同样的问题在类似Request.getAttrubute()的方法中以及引用JNDI名字时也会出现。
更多精彩
赞助商链接