问题描述
Tuscany中的@Service注解中定义的value属性是个Class数组,注解的源码如下:
@Target(TYPE)
@Retention(RUNTIME)
public @interface Service {
/**
* The value is an array of interface or class objects that should be
* exposed as services by this component.
*
* @return the services of this component
*/
Class<?>[] value();
/**
* The value is an array of strings which are used as the service names
* for each of the interfaces declared in the value array.
*
* @return the service names
*/
String[] names() default {};
}
从源码中可以看出,Service注解是支持多接口的。但是当实现了多个接口的bean被定义,使用<implementation.spring/>时,运行时却会报出服务命名冲突的错误:
@Service(value = {Helloworld.class, I18NHelloworld.class}, names = {"Helloworld", "I18NHelloworld"})
public class HelloworldImpl implements Helloworld, I18NHelloworld {
@Override
public String sayHello(String name) {
return "Hello " + name;
}
@Override
public String sayI18nHello(String name) {
return "International Hello " + name;
}
@Init
public void init() {
System.out.println("service inited");
}
@Destroy
public void destroy() {
System.out.println("service stoped");
}
}
严重: [ASM40003,ASM60003,JCA90045] Duplicate implementation service name: Component = HelloworldComponent Service = helloworldImpl
解决方法
经过仔细排查,在Tuscany的tuscany-base-runtime-2.0-sources.jar!/org/apache/tuscany/sca/implementation/spring/introspect/SpringXMLComponentTypeLoader.java:467发现了下面这段话:
// Set the service name as bean name
for (Service componentService : beanComponentType.getServices()) {
componentService.setName(beanElement.getId());
}
不知道开发人员是出于什么目的非要把服务名称修改为springBean的ID,尝试修改这段代码为以下内容后,一切工作正常
List<Service> serviceList = beanComponentType.getServices();
if (serviceList.size() == 1) {
// Set the service name as bean name
serviceList.get(0).setName(beanElement.getId());
}
如果@Service注解中提供了names属性,则使用用户提供的names属性(如上面的代码),如果没有提供names属性,则使用接口类的短名。
附测试代码:
@Test
public void testSayHello() throws NoSuchServiceException {
// Run the SCA composite in a Tuscany runtime
Node node = TuscanyRuntime.runComposite("helloworld.composite", "target/classes");
try {
// Get the Helloworld service proxy
Helloworld helloworld = node.getService(Helloworld.class, "HelloworldComponent/Helloworld");
// test that it works as expected
Assert.assertEquals("Hello Amelia", helloworld.sayHello("Amelia"));
I18NHelloworld i18NHelloworld = node.getService(I18NHelloworld.class, "HelloworldComponent/I18NHelloworld");
Assert.assertEquals("International Hello Amelia", i18NHelloworld.sayI18nHello("Amelia"));
} finally {
// Stop the Tuscany runtime Node
node.stop();
}
}
后记
在Tuscany官网的说明文档中(链接页面尾部的Non-Supported Features in Tuscany中提到的场景3),明确指出不支持实现了多重接口的Bean的服务暴露,但是JIRA的链接已经失效了,不知道是已经解决了还是什么原因。
链接地址:http://tuscany.apache.org/documentation-2x/sca-java-implementationspring.html