CXF服务发布+HttpClient调用

使用CXF作为服务端发布一个服务,再通过HttpClient来建立一个客户端进行远程的服务调用。这里使用IDEA来进行开发,使用maven来进行依赖的管理。

服务端

CXF使用起来很简单,将相关的依赖包导入后编写服务接口和实现,可以使用两种方式来将服务发布出去。

依赖包:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-core</artifactId>
<version>3.3.6</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http</artifactId>
<version>3.3.6</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http-jetty</artifactId>
<version>3.3.6</version>
</dependency>

CXF自带了jetty来作为服务器,所以最后一个包少了就会报java.io.IOException: Cannot find any registered HttpDestinationFactory from the Bus.

服务接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* 这里的targetNamespace决定了soap发布的命名空间,即后面soap+xml中的调用方法的地址
*/
@WebService(targetNamespace = "http://service/")
public interface TestService {

/**
* 这是一个服务接口
* 接口中的参数若是没有加@WebParam注解
* 传输的数据里不可以用名称来传递参数
* 可以看做是起了别名,没有别名只能用arg0、arg1这样的形式来进行参数传递
*/
public String sayHello(@WebParam(name = "p") String param);
}

服务的实现

1
2
3
4
5
6
7
8
@WebService(targetNamespace = "http://service/")
public class SendMailServiceImpl implements SendMailService {

public String sayHello(@WebParam(name = "p") String param) {
System.out.println("sayHello调用了,参数是 " + param);
return "hello!";
}
}

服务发布

这里可以使用两种方式来发布服务,这两种方式的效果是一样的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public static void createByFactoryBean(String address) {
JaxWsServerFactoryBean jaxWsServerFactoryBean = new JaxWsServerFactoryBean();
jaxWsServerFactoryBean.setAddress(address);
jaxWsServerFactoryBean.setServiceClass(SendMailService.class);
jaxWsServerFactoryBean.setServiceBean(new SendMailServiceImpl());
jaxWsServerFactoryBean.create();
}

public static void createByEndpoint(String address) {
Endpoint.publish(address, new SendMailServiceImpl());
}

public static void main(String[] args) {
System.out.println("web service start");
String address = "http://localhost:8080/test";
// createByFactoryBean(address);
createByEndpoint(address);
System.out.println("web service started");
}

客户端

客户端在调用的时候就需要遵守以下soap协议了,soap是基于xml的,具体可以查看菜鸟教程的soap教程,里面对基础的soap协议的写法有作解释。需要调用api,肯定是要使用http来访问api接口的,首先需要用HttpClient来创建连接,并发送soapXml数据来告知需要执行的service的方法是哪个。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class HttpClientUtils {

/**
* 发送一个soapXml格式的数据到url来调用服务,并将服务的返回内容返回出去。
*/
public static String soapPost(String url, String soapXml) throws IOException {
CloseableHttpClient defaultHttpClient = HttpClients.createDefault();
HttpPost httpPost = new HttpPost(url);
try {
HttpEntity entity = new StringEntity(soapXml, "utf-8");
httpPost.setHeader("Content-Type", "application/soap+xml; charset=utf-8");
httpPost.setEntity(entity);
HttpResponse response = defaultHttpClient.execute(httpPost);
HttpEntity responseEntity = response.getEntity();
return EntityUtils.toString(responseEntity, "utf-8");
} catch (Exception e) {
e.printStackTrace();
return null;
} finally {
defaultHttpClient.close();
}
}

}

下面给出soapXml的写法,当时在写这个Xml时,不知道需要指定目标命名空间,卡了挺久的,也是因为XML的知识盲点,对XML不够了解。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<soap:Envelope
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
<!-- 这个需要指定服务的命名空间 -->
xmlns:ser="http://service/"
soap:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<soap:Header>
</soap:Header>
<soap:Body>
<!-- 这里调用指定的方法 -->
<ser:sayHello>
<!-- 如果服务上没有指定参数的名称,这里只能使用arg0、arg1这种形式来传递参数 -->
<p>123</p>
</ser:sayHello>
</soap:Body>
</soap:Envelope>