内容


探索 WebSphere Application Server Feature Pack for SCA,第 4 部分

SCA Java 注释和组件实现

Comments

系列内容:

此内容是该系列 # 部分中的第 # 部分: 探索 WebSphere Application Server Feature Pack for SCA,第 4 部分

敬请期待该系列的后续内容。

此内容是该系列的一部分:探索 WebSphere Application Server Feature Pack for SCA,第 4 部分

敬请期待该系列的后续内容。

引言

服务组件体系结构(Service Component Architecture,SCA)组装模型提供了使用常用编程语言组装和连接服务组件的能力。此规范允许各种服务组件进行交互和协作,因此您可以集中精力解决具体问题。通过与语言无关的设计,SCA 概念可以扩展到愿意采用此规范的应用程序环境中,而不用考虑这些环境可能以哪个具体的技术为重点。SCA 编程模型提供可让服务在支持 SOA 的环境中操作的元数据和 API。

Java 编程语言是实现服务的一个热门选择。Open SCA 社区已经定义了 SCA 编程模型的 Java 变体,此变体受到 IBM WebSphere Application Server V7 Feature Pack for SCA 的支持。此编程模型针对现有 Java 注释和 API 提供,因此程序员可以让服务无缝地处理 SCA 业务逻辑。在可能的情况下,将使用注入之类的 Java 技术替代 API 来简化编程。SCA 编程模型的另一个在 Java 中实现的基本原则是分离关注的概念,即服务实现的开发人员不需要关注其所引用的服务实现的细节,开发人员也不需要关注使用任何所提供服务的使用者的实现细节。

本文将对 Java 的 SCA 编程模型进行概略介绍。此编程模型在两个规范中描述:

还有一些紧密相关的规范描述其他基于 Java 的框架和应用程序环境如何在 SCA 上下文中使用,例如 Spring 和 EJB 组件,它们也使用公共注释和 API。这些规范和行为未在 Feature Pack for SCA 的首个版本中考虑。

图 1. SCA 组件
图 1. SCA 组件
图 1. SCA 组件

如图 1 中所示,组件是 SCA 应用程序中的基本业务功能元素。从实现的角度而言,这意味着组件基本上就是单个实现的配置实例。组件的可配置方面为:

  • 服务使用关于服务质量的接口、绑定、意图和策略集定义。好的服务接口以提供业务价值为重点。它应该与分布式技术松散耦合,与实现技术松散耦合。通过遵循这些最佳实践,将能够实现更具弹性和适应能力的企业
  • 引用是服务依赖关系的描述。它也使用关于服务质量的接口、绑定、意图和策略集定义。通过引用,业务服务可以组合和聚合其他服务,以提供新业务服务。
  • 属性也是实现的一个可配置方面,但是与引用有区别,因为属性可以存储复杂或层次化的数据值。属性类型由实现声明,但其值由组件声明。通常,属性应该具有足够普遍的业务意义,以便在多个应用程序中使用组件实现。属性并非用于基础结构目的。

每个 SCA 应用程序都以一个或多个组件为基础构建。组件组组装在组合中,可以彼此互连形成公开一个或多个业务服务的程序集。

在图 1 中,接口显示为指向服务和引用中的直线。接口是服务提供的契约或引用要求提供的契约。图 1 中的组件是属于 SCA 域。这个域代表管理范围,可能分布在一系列相互连接的运行时节点上。组件位于组合内,而组合位于域内。对于启用 SCA 的 WebSphere Application Server 配置,SCA 域的范围与 Network Deployment 计算单元相当。

绑定描述服务和引用与端点或使用者(位于 SCA 域内或外)交互时的传输和协议配置。示例包括:Web 服务、EJB RMI/IIOP 等等。在图 1 的左下侧和右下侧将绑定描述为指向服务和引用中的直线。绑定是服务提供的访问机制的描述,同时也是引用所需的访问机制的描述。

SCA 还提供缺省绑定,可供位于相同 SCA 域中的远程部署组件使用。这样有一个优势,允许组装人员使用逻辑名引用 SCA 域中的服务,不需要特定的绑定配置,因此使用更为方便。请注意,绑定配置通常并不会出现在组件实现中,这是刻意实现的,也是我们所需要的方式。通过将绑定细节抽象到元数据中,可以重用业务逻辑,甚至可以随着 SOA 的特定基础结构和连接方式的成熟和发展进行重构。

这种关注分离实际上将 SCA 应用程序的开发分担到两个角色:

  • 业务逻辑开发人员
  • 组装人员

对于简单部署场景,这通常是同一个人;不过,在企业部署中,业务逻辑可以采用不同的方式进行重用,甚至使用特定于企业的约定进行组装,因此两个角色由不同的个人承担。

Java 组件实现

Java 类提供 SCA 组件的实现,包括它的各个特性(服务、引用和属性)。基于 Java 的实现提供的服务采用以下方法之一定义接口:

  • Java 接口
  • Java 类
  • 从 Web 服务描述语言(Web Services Description Language,WSDL)端口类型生成的 Java 接口

Java for SCA 支持创建本地或远程服务。本地服务只能由位于相同 JVM 中的客户端使用。本地服务是细粒度紧密耦合服务,允许您方便地将业务功能划分为离散的组件。远程服务应是粗粒度业务服务,各自提供业务可标识的功能。通常通过将多个本地服务组装为 SCA 组合来实现远程服务。

Java 实现类必须实现服务接口定义的所有操作。如果服务接口由 Java 接口定义,则基于 Java 的组件可以实现此 Java 接口或实现此接口的所有操作。

接口由 Java 类(相对于 Java 接口)定义的服务不能作为远程服务,而从 WSDL 端口类型生成的 Java 接口可用于远程服务。Java 实现可能指定其通过使用 @Service 显式提供的服务。在某些情况下,并不需要使用 @Service,采用 Java 实现提供的服务可能从实现类本身进行推断。@Service 可以用于指定提供多个服务的实现。

图 2 描述了一个 Java 服务接口和使用此接口提供服务的 Java 实现。这不是业务组件中的基础结构逻辑。通过 Java 5.0 注释,可以方便地进行服务声明和实现。

图 2. Java 实现示例:采用 Java 的服务
图 2. Java 实现示例采用 Java 的服务
图 2. Java 实现示例采用 Java 的服务

接口或实现类定义的 Java 服务契约可能使用 @Remotable 声明服务遵循 SCA Assembly 规范定义的远程服务的语义。如果未使用 @Remotable,则将服务视为本地服务。如果实现类实现了未使用 @Remotable 注释声明的接口,则此类将被视为实现了单个本地服务,而此本地服务的接口由类中定义的公共方法定义。

对本地服务调用的数据交换语义按引用调用。这意味着必须在了解客户端或对调用服务可见的服务提供者进行参数更改(而不是简单类型)的情况下编写代码。

Java 公共注释

与 Javadoc 标记不同,Java 注释表现为嵌入在编译程序生成的类文件中,可以由 JVM 保留供运行时检索。

注释提供了用于指定 SCA 组装模型的元素的关键机制。例如,注释可以指定服务范围的属性、实现的本质以及迭代特性、约束和需求。Java 注释的使用是一种被广泛接受的指定元数据的方法,在多个 Java EE 规范中对这方面进行了强调,让注释扮演了非常重要的角色。SCA 组装模型可以利用 Java 实现的自检来确定 SCA 服务的特性和本质。注释提供了将此元数据表示为实现的一部分的方法。

清单 1 给出了 PaymentService 服务的服务接口示例,此服务只限于在相同 JVM 中操作的客户端调用,能够直接接收和传递对象引用:

清单 1
package session.pay;
  import org.osoa.sca.annotations.AllowsPassByReference;
  @AllowsPassByReference
  public interface PaymentService {
      public PaymentOutput MakePayment(PaymentInput pI);
  }
图 3. SCA 范围注释
图 3. SCA 范围注释
图 3. SCA 范围注释

通过 Java 编程模型,Java 实现可以指示 SCA 运行时必须如何管理其状态。这个工作由 SCA 实现范围概念负责。范围控制实现的可见性和生命周期。实现范围通过 @Scope、@Init 和 @Destroy Java 注释指定。

对组件提供的服务的调用将由 SCA 运行时根据其实现范围的语义发布到实现实例。范围使用 @Scope 注释在实现类上指定。@Scope 注释用于在服务的接口定义上,或服务实现类本身上。

可以向接口或实现类定义添加范围注释。Feature Pack for SCA 支持无状态范围和组合范围:

  • 当服务请求没有与之前或之后的请求的隐含相关关系时,就使用无状态范围。每个请求操作彼此独立,SCA 运行时不保证保留该服务的状态信息。
  • 当实现在组合范围内时,在包含组合的生命周期中,所有服务请求都路由到相同的实现实例(采用组合范围部署在集群中的服务会导致每个 JVM 一个独立体)。Feature Pack for SCA 不支持请求或对话范围。

实现可以声明生命周期方法,以供初始化组件实例或范围过期时调用。@Init 指示在范围的生命周期中必须在首次使用实例时调用此方法。@Destroy 指定范围结束时调用的方法。只有不带参数的公共方法能注释为生命周期方法。

以下是使用范围和生命周期注释的服务实现的示例:

清单 2
package session.shop;
@Scope("stateless")
public class ShoppingCartServiceImpl implements ShoppingCartService {
    public void addToCart(int quantity) {
        //Code to populate shopping cart resources.
        ...
        return;
    }
    @Init
    public void beginCart() {
        //code to initialize shopping cart resources.
    }
    @Destroy
    public void endCart() {
        //code to free shopping cart resources.
    }
}

SCA 组装模型提供了一种通过组件类型或组合文件的特性指定方法,用于指定 Java 注释中的典型特性,而无需实现语言支持注释。side 文件提供了在注释样式与安装需求不匹配时的替代方法。

配置属性

属性通过允许 SCA 组件定义指定实现的行为条件对实现进行定制。@Property 注释用于对存储注入属性或委托运行时使用的 setter 方法来设置 SCA 属性值的 Java 类字段进行注释。

注入属性的类型可以为简单 Java 类型或复杂 Java 类型。Java 类字段或 setter 方法输入参数的类型都可以由注入属性的类型确定。甚至在字段上没有 @Property 注释时,也可以使用公共 Setter 方法来注入属性值。在这种情况下,字段或 setter 方法的名称用于确定属性的名称。非公共字段必须使用要注入的 @Property 注释。setter 方法用于 Java 实现中有 setter 方法和属性字段时。

@Property 注释具有以下特性:

  • name(可选):属性的名称,缺省为 Java 类字段的名称。
  • required(可选):指定是否需要注入,缺省为 false。

下面是两个使用 @Property 注释语句定义字符串的属性定义的示例。第一个(清单 3)是通过使用属性字段定义对字段进行注释完成的,而第二个(清单 4)是通过使用属性 setter 对 setter 方法进行注释实现的。使用私有字段并注释 setter 方法被视为最佳做法。

清单 3
@Property(name="currency", required=”true”)
	protected String currency;
清单 4
@Property(name="currency", required=”true”)
public void setCurrency( String theCurrency ){
	currency =thecurrency;
}

属性用于在运行时更改组件行为,不用更改代码。假定在使用 SCA 的某家银行中有一个 BankUpdate 组件。通过使用上面的示例,“货币”余额可以根据银行的策略设置为不同的值。

清单 5 显示了取自 BankUpdate 组件的示例,使用了 @Property 注释标识名为 maxCurrency 的属性。假定属性的缺省值为 1,000 美元。

清单 5
private int  maxCurrency  = 1000;
// Property declaration using a setter method
@Property(name = "maxCurrency", override = "may")
public void setMaxCurrency(int maxCurrency) {
    this.maxCurrency = maxCurrency;
}

清单 6 显示了组件的自定义,在组合 SCDL 文件中将 maxCurrency 属性设置为 1500,以覆盖组件类型中定义的缺省值 (1000)。

清单 6
<component name="BankUpdateComponent">
    <implementation.java class="balance.BankUpdateImpl" />
    <property name="maxCurrency">1500</property>
    ...
</component>

访问服务

Java SCA 实现通过服务引用与服务交互。服务引用有时候称为代理,这是一个在分布式编程中广泛使用的行业术语。

SCA 组件可以通过注入获得服务引用,或通过组件上下文 API 以编程方式获得。对于 SCA Java 实现,建议使用引用注入获得服务引用,因为这样可以得到最大限度减少中间件 API 使用的代码。ComponentContext API 应该用于不可能进行引用注入的位置,如 EJB 或 Servlet 中。此 API 会将引用返回到 SCA 域中公开的服务。

图 4. 引用注入
图 4. 引用注入
图 4. 引用注入

@Reference 注释用于对 Java 类字段或 setter 方法进行注释,以用于注入供调用服务之用的服务引用。注入的服务的接口由 Java 类字段定义,或由 setter 方法输入参数类型定义。

清单 7
private HelloService helloService;
@Reference (name="helloService", required=true)
 public setHelloService(HelloService service){
 helloService = service;
}
public void clientMethod() {
String result = helloService.hello("Hello World!");
}

与 Property 注释类似,@Reference 注释具有以下特性:

  • name(可选):引用的名称,缺省为 Java 类字段的名称。
  • required(可选):是否需要注入。缺省为 true。

CompositeContext API

SCA V1.0 Java 规范并未指定 API,API 支持 Java 使用者获得对服务的引用,除非它通过 SCA 组装机制静态连接到服务。Feature Pack for SCA 提供了实现此功能的 WebSphere Application Server 扩展,名为 CompositeContext API(图 5)。

图 5. CompositeContext API
图 5. CompositeContext API
图 5. CompositeContext API

使用 CompositeContext API 的 Java 程序必须部署在与目标服务相同的拓扑(基本服务器或 Network Deployment 计算单元)中,服务必须部署在 SCA 域中。在 Feature Pack for SCA 的这个初始版中,服务仅仅通过缺省绑定提供。

异步编程

SCA 的 Java 编程模型提供非阻塞回调异步模式,如图 6 中所示。服务的非阻塞单向调用允许客户端在无需等待服务运行的情况下进行执行。所调用的服务立即执行或稍后执行。必须通过独立的机制将服务的响应(如果有)反馈回客户端,因为调用时没有响应可用。

图 6. 异步模式
图 6. 异步模式
图 6. 异步模式

异步模型

@OneWay 注释指示非阻塞交互样式。单向调用是最简单的异步编程形式,其中的服务客户端调用服务,并立即继续处理,而不等待服务执行。

任何返回“void”且未声明异常的方法均可以使用 @OneWay 注释进行标记。

SCA 并不定义对返回值或声明要引发异常的方法的非阻塞调用的机制。可以适当地对异步响应进行建模,以让使用者和提供者创建一对单向请求,将从使用者到提供者的单向请求用于调用服务,从提供者到使用者的单向请求用于让使用者提供响应。

策略注释

SCA 提供了用于将策略相关的元素附加到 SCA 程序集的工具。实现策略会影响实现、服务和引用在运行时中的行为方式,而交互策略会影响服务被调用时应有的行为方式。SCA Policy Framework 包括两个主要概念:

  • 意图表达抽象的高级策略需求。
  • 策略集表达底层详细的具体策略。

SCA Java 定义了 @Requires 注释,用于支持将意图附加到 Java 类或接口,或附加到类和接口内的元素,如方法和字段。

这里是附加两个意图的 @Requires 注释,即 confidentiality.message 和 integrity.message(来自 Security 域):

@Requires({CONFIDENTIALITY, INTEGRITY})

Java for SCA 还提供了与 SCA Policy Framework 中定义的特定策略意图对应的注释。通常,Java 注释与 SCA 定义的意图具有相同的名称。如果意图是限定意图,要以字符串或字符串组的形式将限定符作为注释的特性提供。

例如,SCA 机密性意图可以指定为:

@Requires(CONFIDENTIALITY)
或者
@Confidentiality

特定限度意图的 Java 注释示例:

@Authentication ( {“message”, “transport”} )

抽象意图然后由 SCA 通过绑定上的交互策略的具体策略表达式实现,或者可能会导致 SCA 容器在服务获得实现策略的控制权前调整环境。通过抽象意图,可以将所需行为的通用表达式映射到具体的绑定,但是保留了业务逻辑提供者的重点,以及影响服务的往返访问路径的基础结构决策的实现细节间的关注分离。

示例:绑定到元数据的业务逻辑

以下 SCDL 示例显示了 SCA 服务可以如何在不用更改业务逻辑的情况下作为 Web 服务和 EJB 服务公开。

以存储在线支付系统的支付审核跟踪的 HistoryService 为例。业务逻辑可能包括存储在数据表中的记录,使用 Java 编程语言和 HistoryServiceImpl.class 类中的 JDBC 实现,而其中的类属于 history.sca.jdbc 包。此服务将审核跟踪作为更大的全局事务的一部分执行。服务可以根据应用程序的需要在不同的绑定上提供。如果场景需要使用缺省绑定,以便为 SCA 运行时选择连接机制提供最大的灵活性,则在采用 SCDL 时有三个对应场景(图 7 到图 9):

图 7. 采用 SCA 绑定的服务
图 7. 采用 SCA 绑定的服务
图 7. 采用 SCA 绑定的服务
图 8. 需要相同的服务像 Web 服务一样访问的场景
图 8. 需要相同的服务像 Web 服务一样访问的场景
图 8. 需要相同的服务像 Web 服务一样访问的场景
图 9. 需要与 EJB 服务交互,并需要 EJB 容器的功能的场景
图 9. 需要与 EJB 服务交互,并需要 EJB 容器的功能的场景
图 9. 需要与 EJB 服务交互,并需要 EJB 容器的功能的场景

上面所有情况的实现保持不变。绑定的选择决定此服务如何由客户端进行调用:作为 Web 服务或 EJB 服务。服务使用开发人员熟悉的技术开发,开发时重点考虑业务逻辑:在本例中,即如何创建支付的审核跟踪。连接方面由组装人员通过选择适合于部署的绑定进行控制,因此开发人员无需添加特定于连接方式的代码,而同时还提供了选择最适合每个场景的绑定的灵活性。

总结

SCA 程序员的 Java 体验应该是能够充分利用 Java 5 非常易用的特征,通过使用注释和依赖关系注入来实现真正切实的关注分离,让业务逻辑与特定于配置和协议的 API 分离,免去今天很多应用程序所面临的负担。这个 Java 体验配合可通过用于编辑 SCA 程序集的工具实现的可视组合模拟,能将应用程序开发的重点直接与 SOA 原则保持一致,使用各种实现和实现技术组合得到松散耦合、可重用的粗粒度服务,并将重点放在业务服务本身,而不受任何特定实现的困扰。


相关主题


评论

添加或订阅评论,请先登录注册

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=WebSphere
ArticleID=418614
ArticleTitle=探索 WebSphere Application Server Feature Pack for SCA,第 4 部分: SCA Java 注释和组件实现
publish-date=08102009