1 Java 实现类型
1.1 简介
该规范扩展自SCA装配模型规范,定义了java类如何提供SCA组件的实现,以及该类在SCA中是如何作为组件实现类型来使用的。
该规范需要用到所有《SCA的 Java注解和API规范v100》(本人正在翻译)中定义的所有的注解和API。该文档所引用的所有注解和API都是前者提到的规范所定义的,除非另行指定。SCA的 Java注解和API规范中定义的语义都是标准化的。
1.2 Java实现类型
这一节指出java类是如何提供SCA组件的实现的,包括各种属性,如服务,引用和属性。另外,它详细列出了在作为组件实现类型的Java类上下文中,SCA的 Java注解和API规范中的元数据和Java API的使用方法。
1.2.1 service
基于Java类的组件实现可以提供一个或多个服务。
由基于Java的实现所提供的服务可以拥有按如下方法之一定义的接口:
l Java接口
l Java类
l 产生自WSDL portType的Java接口
Java实现类必须实现service接口定义的所有操作。如果服务接口是由某个java接口定义的,那么基于java的组件或实现该java接口,或实现接口的所有操作。
与java接口相比,其接口是通过java类定义的服务不是远程的。产生自WSDL portType的Java接口是远程的,细节可以查看SCA java注释和API规范的WSDL 2 Java和Java 2 WSDL节。
Java实现类型可以指定通过@Service显式提供的服务。以上情形下,@Service的使用并不是必须的,Java实现类型提供的服务可以从实现类自身继承而来。
1.2.1.1 @Service的使用
服
务接口可以作为Java接口来指定。是组件实现的Java类,可以通过实现指定服务契约的java接口来提供服务。因为一个java类可以实现多个接口,
而某些并没有定义SCA 服务,所以@Service注解可以用于表示由实现提供的服务以及他们相应的Java接口定义。
如下是java服务接口和使用该接口提供服务的Java实现的例子:
Interface:
public interface HelloService {
String hello(String message);
}
Implementation class:
@Service(HelloService.class)
public class HelloServiceImpl implements HelloService {
public String hello(String message) {
...
}
}
该实现的组件类型的XML描述如下。没有必要指定component type,因为可以从Java类反射得到。
<?xml version="1.0" encoding="ASCII"?>
<componentType xmlns="http://www.osoa.org/xmlns/sca/0.9">
<service name="HelloService">
<interface.java inter>"services.hello.HelloService"/>
</service>
</componentType>
和接口相比,Java类实现本身也能定义由组件提供的服务。这种情况下,@Service可以被用于显式地声明定义了实现所提供的服务的实现类。同情形下,组件只能用@Service提供服务。如下所示:
@Service(HelloServiceImpl.class)
public class HelloServiceImpl implements AnotherInterface {
public String hello(String message) {
...
}
…
}
在上述例子中,HelloWorldServiceImpl提供了一个服务。该服务在实现类上定义为public方法。AnotherInterface接口没有指定组件所提供的服务。如下是内省的组件类型的XML描述:
<?xml version="1.0" encoding="ASCII"?>
<componentType xmlns="http://www.osoa.org/xmlns/sca/1.0">
<service name="HelloService">
<interface.java inter/>
</service>
</componentType>
@Service可以用于指定由实现提供的多个服务:
@Service(interfaces={HelloService.class, AnotherInterface.class})
public class HelloServiceImpl implements HelloService, AnotherInterface {
public String hello(String message) {
...
}
…
}
如下片段演示了该实现的内省的组件类型:
<?xml version="1.0" encoding="ASCII"?>
<componentType xmlns="http://www.osoa.org/xmlns/sca/1.0">
<service name="HelloService">
<interface.java inter>"services.hello.HelloService"/>
</service>
<service name="AnotherService">
<interface.java inter>"services.hello.AnotherService"/>
</service>
</componentType>
1.2.1.2 本地和远程服务
由接口定义的Java服务契约,可以使用@Remotable来声明服务按照SCA装配规范遵循远程服务的语义。如下演示了@Remotable使用:
package services.hello;
@Remotable
public interface HelloService {
String hello(String message);
}
如果没有声明@Remotable,由Java接口定义的服务就被认为是SCA装配模型规范里定义的本地服务。
如果实现类实现了没有用@Remotable注解修饰的接口,那么该类被认为实现了单个本地服务。该本地服务的类型是由该类定义的。(注:本地服务可以使用Java接口或类指定类型)
某个实现类为SCA运行时提供关于是否能通过使用@AllowsPassByReference来完成不经过copy而传值语义工作的信息。
1.2.1.3 内省Java实现提供的服务
上述情况下,由Java实现类提供的服务可以通过内省来决定,而可以忽略使用@Service指定的必要。如下的算法被用来决定服务是如何从实现类内省的。
如果SCA服务的接口在实现类上没有用@Service注解指定,那么就假设所有的用@Remotable注解了的接口都是由组件提供的服务接口。如果没有一个实现接口是远程的,那么默认实现提供了单个服务,该服务的类型就是实现的class。
1.2.1.4 非堵塞型服务操作
由java接口或实现类定义的服务操作可以使用@OneWay来声明SCA 运行时在客户程序调用服务操作的时候必须遵循非堵塞性语义。该非堵塞性语义由SCA装配规范(本人正在翻译)定义了。
1.2.1.5 非会话和会话服务
Java实现类型支持所有的会话服务注解,这些会话注解在SCA Java注解和API规范中定义:@Conversational,@EndsConversational和@ConversationAttributes。
以
下的语义控制由Java接口或实现类定义的服务契约。由Java接口或实现类定义的服务契约隐式地表示非会话除非使用了@Conversational注
解修饰。一旦使用了@Conversational注解修饰,@Conversational就用于声明提供服务的组件实现实现了会话语义。
1.2.1.6 回调服务
回调接口通过在某个Java类实现的服务接口上使用@Callback注解来声明。
1.2.2 引用
引用可以通过注入或SCA Java 注解和API规范中定义的ComponentContext API来维护。只要可能,推荐使用注入方式来访问引用。
1.2.2.1 引用注入
某个Java实现类型可以通过使用@Reference显式地指定其引用,如下所示:
public class ClientComponentImpl implements Client {
private HelloService service;
@Reference
public void setHelloService(HelloService service) {
this.service = service;
}
}
如 果@Reference标记了某个public或protected
的setter方法,那么就要求SCA运行时提供指定了方法参数类型的服务引用契约的相应实现。通过调用实现实例的setter方法来完成这个工作。注入
什么时候发生是由实现的作用域(scope)定义的。然而,它总是在第一个服务方法被调用之前发生注入。
如果@Reference标记了某个public或protected的属性域,那么就要求SCA运行时提供指定了属性域类型的服务引用契约的相应实现。通过设置实现实例的该属性域来完成。什么时候发生注入由实现的作用域定义。
如果@Reference标记构造函数的某个参数上,那么就要求SCA运行时提供在实现初始化时期指定了构造函数参数的服务引用契约的相应实现。
引用也可以根据XXX(这里原文有问题)节定义的规则通过内省实现类来指定。
引用可以是声明选项(声明选项在SCA Java注解和API规范中定义了)。
1.2.2.2 动态引用访问
引用可以通过ComponentContext.getService()和ComponentContext.getServiceReference(.)方法来动态访问。
1.2.3 属性(Property)
1.2.3.1 属性注入
属性可以通过注入或ComponentContext API来维护。只要可能,推荐使用注入方式来访问属性。
Java实现类型可以通过使用@Property注解显式指定它的属性,如下所示:
public class ClientComponentImpl implements Client {
private int maxRetries;
@Property
public void setRetries(int maxRetries) {
this. maxRetries = maxRetries;
}
}
如果@Property标记了某个public或protected属性域,那么就要求SCA运行时提供相应的属性值。什么时候发生注入由实现的作用域(scope)定义。
如果@Property标记在构造函数的某个参数上,那么就要求SCA运行时在实现实例初始化期间提供相应的属性值。
属性也可以根据XXX(这里原文有问题)节定义的规则通过内省实现类来指定。
1.2.3.2 动态属性访问
属性可以通过ComponentContext.getService()和ComponentContext.getServiceReference(.)方法来动态访问。这问mponentContext.getServiceReference()
1.2.4 实现实例的初始化
Java实 现类必须提供一个public或protected
构造函数,以便SCA运行时用于初始化实现的实例。构造函数可以有参数;当存在参数时,SCA容器会在调用构造函数的时候传递可用的属性或引用值。在任何
一个服务方法被调用之前,所有的属性或引用值,都将会设置给属性域或传递给与属性相关的setter方法。
构造函数的选用由容器选择,如下所示:
1. 用@Constructor注解标注的声明的构造函数
2. 无二义性的标识所有属性和引用值的声明的构造函数
3. 无参数的构造函数
@Constructor注解只能在一个构造函数上指定;如果多个构造函数标注了@Constructor,SCA容器将报错。
与构造函数的每个参数相关的属性或引用用下列方式标识:
l @Constructor注解的by name(如果存在)
l 通过在参数声明上使用的@Property或@Reference注解
l 通过唯一地与属性或引用的类型匹配参数类型
组件间的循环引用发生的话,容器会用以下办法之一进行处理:
l 如果循环中的任意引用是可选的(不是必须的),那么容器就会在构造期间注入null值。保证调用任何服务之前都已经注入了目标的引用。
l 容器也可以注入一个目标服务的代理;在代理上的方法调用可能会导致ServiceUnavailableException异常。
以下是合法的Java组件构造函数声明的例子:
/** Simple class taking a single property value */
public class Impl1 {
String someProperty;
public Impl1(String propval) {...}
}
/** Simple class taking a property and reference in the constructor;
* The values are not injected into the fields.
*//
public class Impl2 {
public String someProperty;
public SomeService someReference;
public Impl2(String a, SomeService b) {...}
}
/** Class declaring a named property and reference through the constructor */
public class Impl3 {
@Constructor({"someProperty", "someReference"})
public Impl3(String a, SomeService b) {...}
}
/** Class declaring a named property and reference through parameters */
public class Impl3b {
public Impl3b(
@Property("someProperty") String a,
@Reference("someReference) SomeService b) {...}
}
/** Additional property set through a method */
public class Impl4 {
public String someProperty;
public SomeService someReference;
public Impl2(String a, SomeService b) {...}
@Property public void setAnotherProperty(int x) {...}
}
1.2.5 实现作用域和生命周期回调
Java实现类型支持在SCA Java注解和API规范中定义的所有作用域:STATELESS,REQUEST,CONVERSATION和COMPOSITE。实现通过使用@Scope注解指定它们的作用域:
@Scope(”COMPOSITE”)
public class ClientComponentImpl implements Client {
// …
}
当没有在实现类上指定@Scope注解,默认为STATELESS。
Java组件的实现通过分别使用@Init和@Destroy来指定init和destroy回调。例如:
public class ClientComponentImpl implements Client {
@Init
public void init() {
//…
}
@Destroy
public void destroy() {
//…
}
}
1.2.5.1 作用域为CONVERSATION的Java实现类可能会使用@ConversationID注解来持有当前的会话ID。该会话ID通过public或protected属性域或setter方法注入。可选地,Conversation API(在SCA Java注解和API规范中定义)能用来获取当前的会话ID。
会话性的实现
作为会话服务的提供者,有必要在单个会话的连续的方法调用之间维持状态数据。对于Java实现类型,有两种可能的策略用于处理该状态数据:
1. 实现可以构建为无状态的代码片(本质上来说,代码认为对于每个函数调用都是新的实例)。代码必须负责访问会话的conversationID,该会话由SCA运行时维护。然后,在方法的处理过程期间和需要访问持久化的状态数据时,由实现来负责持久化任何必要的状态数据,而所有都是以conversationID作为key来使用。
2. 实现可以构建为有状态的代码片,也就意味着实现把所有的状态数据存储在java类实例的属性域中。实现必须用@Scope注解声明为会话作用域。这就告诉SCA运行时环境,实现是有状态的,并且SCA运行时必须在客户函数调用与服务实现的某个特定实例之间扮演中介角色。如果运行时因为某种原因需要将实例清除出内存,运行时环境还要负责持久化和存储实现的实例。(注:会话是非常长的生命期限,SCA运行时可以使用集群系统。在集群系统中给定的实例对象可以在集群的各个节点中移动,以实现负载均衡)
1.2.6访问回调服务
Java实现类用@Callback注解来要求回调服务。该@Callback注解引用了在某个public或protected属性域或setter方法上与当前调用注入相关的回调服务。
1.2.7未注解的实现的语义
这一节定义了并未使用@Reference或@Property显式声明的Java组件实现的属性和引用的判定规则。
在没有@Property和@Reference注解的时候,类的属性和引用按以下规则定义:
1. 如果Java类型是由@Remotable注解的接口,就是引用。
2. 否则,如果Java类型是一个数组,并该数组元素的类型是由@Remotable注解的接口,那么就是引用。
3. 否则,如果Java类型是java.util.Collection的子接口或子类,并且collection的参数化类型是由@Remotable注解的接口,那么就是引用。
4. 否则就是属性。
1.2.8在装配中指定java实现
如下定义了用于java实现类型的实现元素schema:
<implementation.java class="NCName" />
Implementation.java元素有如下属性:
class(必须) –实现的java类全名。
1.2.9指定component type
对于Java实现类,组件类型一般都是直接来自java类的内省。
在配置文件中component type的指定是可选的。组件类型配置文件由装载java类的同一个类装载器发现。配置文件必须在与实现的名称空间一致的目录下,并与java类拥有相同的名字,并用.componentType扩展名替代.class扩展名。
componentType side文件是如何加入到从组件的实现反射而来的组件类型信息中的规则在SCA装配模型规范中定义了。如果组件类型信息与实现冲突,将产生错误。
如果componentType side文件用WSDL接口指定了服务接口,那么java类应该实现由JAX-WS的WSDL到java接口的映射而产生的接口。请查看”WSDL 2 Java和Java 2 WSDL”节