之前写过类似的文章,但真正使用起来的时候,发现客户端的调用更倾向于使用动态代理的方式来完成。所以服务端的配置就要进行的修改。本文正是在此背景下产生的。
1. Maven
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxws</artifactId>
<version>3.1.1</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http</artifactId>
<version>3.1.1</version>
</dependency>
2. 客户端
// ---------------------- 工具类
public final class CxfUtil {
/**
*
* @param url
* @param serviceName 方法名
* @param parms
* @return
* @throws Exception
*/
@SuppressWarnings("unchecked")
public static <T> T getServiceResult(String url, String serviceName, Object... parms) {
JaxWsDynamicClientFactory dcf = JaxWsDynamicClientFactory.newInstance();
org.apache.cxf.endpoint.Client client = dcf.createClient(url);
try {
Object[] objects = client.invoke(serviceName, parms);
return (T) objects[0];
} catch (Exception e) {
throw ExceptionUtil.wrapRuntime(e);
}
}
}
// ---------------------- 测试
public class Tester {
final String BASE_URL_PATH = "http://localhost:9090/zzz/webservice/";
@Before
public void setUp() throws Exception {
}
@After
public void tearDown() throws Exception {
}
@Test
public void zipFile() {
final String url = BASE_URL_PATH + "mxyzService?wsdl";
final String serviceName = "zipFile";
Object serviceResult = CxfUtil.getServiceResult(url, serviceName, "123");
System.out.println(serviceResult);
}
}
3. 服务端
3.1 接口
package com.xx.yy.cxf.service;
@WebService((targetNamespace = "http://service.cxf.yy.xx.com")// 命名空间,一般是接口的包名倒序)
interface IHelloWorld {
String sayHello(@WebParam(name = "username") String username);
}
3.2 接口实现
这里有个注意的位置就是 这里targetNamespace
的值必须和上面接口IHelloWorld
中相应的targetNamespace
的值完全一致。
@WebService(serviceName = "helloService"//服务名, 可以省略, cxf会自动生成 本类类名+"Service" 这样格式的名称
, targetNamespace = "http://service.cxf.yy.xx.com"//包名倒序,并且和接口定义保持一致(一模一样!!!比如这里, com后面是没有 / 的),
, endpointInterface = "com.xx.yy.cxf.service.IHelloWorld") //接口的完整路径
public class HelloWorldImpl implements IHelloWorld {
@Override
public String sayHello(String username) {
return "Hello " + username;
}
3.3 其他配置
参见 cxf-spring-pratice-service 中的第3,4小节。
4. 总结
写下本文的主要动机是上面第3.2小节中的问题,同事在写WebService服务端接口时就出现了类似的问题。
5. 错误:No operation was found with the name {xxx}
解决方案有两种:1. 修改服务端。2. 修改客户端。
修改服务端的方案参见上面的第三小节。出现这种错误肯定是哪一步马虎了。
而修改客户端可以按照下面这样进行配置
JaxWsDynamicClientFactory factory = JaxWsDynamicClientFactory.newInstance();
Client client = factory.createClient(ASMX_WSDL_URL_PATH); // 记得要加入"?llwsdl"
// 重点就是下面这句了; 至于第一个参数的由来, 参加下图
QName opName = new QName("http://tempuri.org/", "ExportXZQDataToService"); // 指定到接口类所在包
Object[] res = client.invoke(opName, "123456");
System.out.println("Say: " + res[0]);