使用CXF集成Web Service
Web service是一个平台独立的,低耦合的,自包含的、基于可编程的web的应用程序,可使用开放的XML(标准通用标记语言下的一个子集)标准来描述、发布、发现、协调和配置这些应用程序, 用于开发分布式的互操作的应用程序。
添加依赖
<dependency>
<groupId>org.apache.cxfgroupId>
<artifactId>cxf-spring-boot-starter-jaxwsartifactId>
<version>3.2.4version>
dependency>
<dependency>
<groupId>org.apache.cxfgroupId>
<artifactId>cxf-spring-boot-starter-jaxwsartifactId>
<version>3.2.4version>
dependency>
创建服务端接口
package com.example.demo.webservice;
import com.example.demo.base.BaseResult;
import com.example.demo.dto.UserDTO;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebService;
/**
* ---------------------------
* User Web Service服务端接口 (UserWebService)
* ---------------------------
* @author:XiaoYang
* @date:2020-09-15 13:00:00
* ---------------------------
*/
@WebService
public interface UserWebService {
/**
* 测试传字符串
* @param name
* @return
*/
@WebMethod(operationName="sayHello")
String sayHello(@WebParam(name = "name", targetNamespace = "http://webservice.demo.example.com/") String name);
/**
* 测试传对象类型
* @param userDTO
* @return BaseResult是封装的返回结果对象
*/
@WebMethod(operationName="operateUser")
BaseResult operateUser(@WebParam(name = "userDTO", targetNamespace = "http://webservice.demo.example.com/") UserDTO userDTO);
}
package com.example.demo.webservice;
import com.example.demo.base.BaseResult;
import com.example.demo.dto.UserDTO;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebService;
/**
* ---------------------------
* User Web Service服务端接口 (UserWebService)
* ---------------------------
* @author:XiaoYang
* @date:2020-09-15 13:00:00
* ---------------------------
*/
@WebService
public interface UserWebService {
/**
* 测试传字符串
* @param name
* @return
*/
@WebMethod(operationName="sayHello")
String sayHello(@WebParam(name = "name", targetNamespace = "http://webservice.demo.example.com/") String name);
/**
* 测试传对象类型
* @param userDTO
* @return BaseResult是封装的返回结果对象
*/
@WebMethod(operationName="operateUser")
BaseResult operateUser(@WebParam(name = "userDTO", targetNamespace = "http://webservice.demo.example.com/") UserDTO userDTO);
}
创建服务端接口实现类
package com.example.demo.webservice.impl;
import com.example.demo.base.BaseResult;
import com.example.demo.dto.UserDTO;
import com.example.demo.webservice.UserWebService;
import org.springframework.context.annotation.Configuration;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebService;
/**
* ---------------------------
* User Web Service服务端接口实现类 (UserWebServiceImpl)
* ---------------------------
* @author:XiaoYang
* @date:2020-09-15 13:00:00
* ---------------------------
*/
@WebService (
serviceName = "webService", // 暴露的服务名称
targetNamespace = "http://webservice.demo.example.com/", // wsdl命名空间,一般是接口的包名倒序
endpointInterface = "com.example.demo.webservice.UserWebService" // 指定发布webservcie的接口类
)
@Configuration
public class UserWebServiceImpl implements UserWebService {
@Override
@WebMethod(operationName="sayHello")
public String sayHello(@WebParam(name="name", targetNamespace = "http://webservice.demo.example.com/") String name) {
System.out.println("WebService sayHello " + name);
return "sayHello " + name;
}
@Override
@WebMethod(operationName="operateUser")
public BaseResult operateUser(@WebParam(name="userDTO", targetNamespace = "http://webservice.demo.example.com/") UserDTO userDTO) {
if (null == userDTO) {
return BaseResult.error("参数为空!");
}
// TODO 逻辑代码
System.out.println(userDTO.getUserName());
return BaseResult.ok("操作成功!");
}
}
package com.example.demo.webservice.impl;
import com.example.demo.base.BaseResult;
import com.example.demo.dto.UserDTO;
import com.example.demo.webservice.UserWebService;
import org.springframework.context.annotation.Configuration;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebService;
/**
* ---------------------------
* User Web Service服务端接口实现类 (UserWebServiceImpl)
* ---------------------------
* @author:XiaoYang
* @date:2020-09-15 13:00:00
* ---------------------------
*/
@WebService (
serviceName = "webService", // 暴露的服务名称
targetNamespace = "http://webservice.demo.example.com/", // wsdl命名空间,一般是接口的包名倒序
endpointInterface = "com.example.demo.webservice.UserWebService" // 指定发布webservcie的接口类
)
@Configuration
public class UserWebServiceImpl implements UserWebService {
@Override
@WebMethod(operationName="sayHello")
public String sayHello(@WebParam(name="name", targetNamespace = "http://webservice.demo.example.com/") String name) {
System.out.println("WebService sayHello " + name);
return "sayHello " + name;
}
@Override
@WebMethod(operationName="operateUser")
public BaseResult operateUser(@WebParam(name="userDTO", targetNamespace = "http://webservice.demo.example.com/") UserDTO userDTO) {
if (null == userDTO) {
return BaseResult.error("参数为空!");
}
// TODO 逻辑代码
System.out.println(userDTO.getUserName());
return BaseResult.ok("操作成功!");
}
}
创建CXF配置类
package com.example.demo.config;
import com.example.demo.webservice.UserWebService;
import org.apache.cxf.Bus;
import org.apache.cxf.bus.spring.SpringBus;
import org.apache.cxf.jaxws.EndpointImpl;
import org.apache.cxf.transport.servlet.CXFServlet;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.xml.ws.Endpoint;
/**
* ---------------------------
* CXF配置类 (CxfConfig)
* ---------------------------
* @author:XiaoYang
* @date:2020-09-15 13:00:00
* ---------------------------
*/
@Configuration
public class CxfWebServiceConfig {
@Autowired
private UserWebService userWebService;
@Bean("cxfServletRegistration")
public ServletRegistrationBean dispatcherServlet() {
// 注册servlet 拦截/ws 开头的请求 如果不设置 默认为:/services/*
return new ServletRegistrationBean(new CXFServlet(), "/ws/*");
}
@Bean(name = Bus.DEFAULT_BUS_ID)
public SpringBus springBus() {
return new SpringBus();
}
/**
* 发布endpoint
* 地址:http://IP地址:端口号/ws/webService?wsdl
* @return
*/
@Bean
public Endpoint endpoint() {
EndpointImpl endpoint = new EndpointImpl(springBus(), userWebService);
// 发布的地址
endpoint.publish("/webService");
return endpoint;
}
}
package com.example.demo.config;
import com.example.demo.webservice.UserWebService;
import org.apache.cxf.Bus;
import org.apache.cxf.bus.spring.SpringBus;
import org.apache.cxf.jaxws.EndpointImpl;
import org.apache.cxf.transport.servlet.CXFServlet;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.xml.ws.Endpoint;
/**
* ---------------------------
* CXF配置类 (CxfConfig)
* ---------------------------
* @author:XiaoYang
* @date:2020-09-15 13:00:00
* ---------------------------
*/
@Configuration
public class CxfWebServiceConfig {
@Autowired
private UserWebService userWebService;
@Bean("cxfServletRegistration")
public ServletRegistrationBean dispatcherServlet() {
// 注册servlet 拦截/ws 开头的请求 如果不设置 默认为:/services/*
return new ServletRegistrationBean(new CXFServlet(), "/ws/*");
}
@Bean(name = Bus.DEFAULT_BUS_ID)
public SpringBus springBus() {
return new SpringBus();
}
/**
* 发布endpoint
* 地址:http://IP地址:端口号/ws/webService?wsdl
* @return
*/
@Bean
public Endpoint endpoint() {
EndpointImpl endpoint = new EndpointImpl(springBus(), userWebService);
// 发布的地址
endpoint.publish("/webService");
return endpoint;
}
}
启动SpringBoot服务
浏览器访问:http://127.0.0.1:8080/ws?wsdl
即可查看整体信息。
浏览器访问:http://127.0.0.1:8080/ws/webService?wsdl
即可查看某个Service详情信息。
客户端调用
Java中调用
package com.example.demo.webservice;
import com.example.demo.base.BaseResult;
import com.example.demo.dto.UserDTO;
import org.apache.cxf.endpoint.Client;
import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
import org.apache.cxf.jaxws.endpoint.dynamic.JaxWsDynamicClientFactory;
/**
* ---------------------------
* UserWebService测试类 (UserWebServiceTest)
* 该类提供两种不同的方式来调用webservice服务:
* 1:代理工厂方式
* 2:动态调用webservice
* ---------------------------
* @author XiaoYang
* @date 2020-09-15 13:30:00
* ---------------------------
*/
public class UserWebServiceTest {
/**
* 接口地址
*/
private static final String ADDRESS = "http://127.0.0.1:8080/ws/webService?wsdl";
public static void main(String[] args) {
testSayHello();
testOperateUser();
}
/**
* 1.静态调用,代理类工厂的方式,需要拿到对方的接口地址
* @return
*/
public static void testOperateUser() {
try {
// 创建Web Service客户端代理工厂
JaxWsProxyFactoryBean jaxWsProxyFactoryBean = new JaxWsProxyFactoryBean();
// 设置代理地址
jaxWsProxyFactoryBean.setAddress(ADDRESS);
// 设置接口类型
jaxWsProxyFactoryBean.setServiceClass(UserWebService.class);
// 创建一个代理接口实现
UserWebService us = (UserWebService) jaxWsProxyFactoryBean.create();
// 数据准备
UserDTO userDTO = new UserDTO();
userDTO.setUserName("张三");
// 调用代理接口的方法调用并返回结果
BaseResult result = us.operateUser(userDTO);
System.out.println("状态码:" + result.getCode());
System.out.println("提示信息:" + result.getMsg());
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 2:动态调用
* @return
*/
public static void testSayHello() {
// 创建动态客户端
JaxWsDynamicClientFactory dcf = JaxWsDynamicClientFactory.newInstance();
Client client = dcf.createClient(ADDRESS);
// 需要密码的情况需要加上用户名和密码
// client.getOutInterceptors().add(new ClientLoginInterceptor(USER_NAME, PASS_WORD));
Object[] objects = new Object[0];
try {
// invoke("方法名",参数1,参数2,参数3....);
objects = client.invoke("sayHello", "admin");
System.out.println("返回的数据:" + objects[0]);
} catch (Exception e) {
e.printStackTrace();
}
}
}
package com.example.demo.webservice;
import com.example.demo.base.BaseResult;
import com.example.demo.dto.UserDTO;
import org.apache.cxf.endpoint.Client;
import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
import org.apache.cxf.jaxws.endpoint.dynamic.JaxWsDynamicClientFactory;
/**
* ---------------------------
* UserWebService测试类 (UserWebServiceTest)
* 该类提供两种不同的方式来调用webservice服务:
* 1:代理工厂方式
* 2:动态调用webservice
* ---------------------------
* @author XiaoYang
* @date 2020-09-15 13:30:00
* ---------------------------
*/
public class UserWebServiceTest {
/**
* 接口地址
*/
private static final String ADDRESS = "http://127.0.0.1:8080/ws/webService?wsdl";
public static void main(String[] args) {
testSayHello();
testOperateUser();
}
/**
* 1.静态调用,代理类工厂的方式,需要拿到对方的接口地址
* @return
*/
public static void testOperateUser() {
try {
// 创建Web Service客户端代理工厂
JaxWsProxyFactoryBean jaxWsProxyFactoryBean = new JaxWsProxyFactoryBean();
// 设置代理地址
jaxWsProxyFactoryBean.setAddress(ADDRESS);
// 设置接口类型
jaxWsProxyFactoryBean.setServiceClass(UserWebService.class);
// 创建一个代理接口实现
UserWebService us = (UserWebService) jaxWsProxyFactoryBean.create();
// 数据准备
UserDTO userDTO = new UserDTO();
userDTO.setUserName("张三");
// 调用代理接口的方法调用并返回结果
BaseResult result = us.operateUser(userDTO);
System.out.println("状态码:" + result.getCode());
System.out.println("提示信息:" + result.getMsg());
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 2:动态调用
* @return
*/
public static void testSayHello() {
// 创建动态客户端
JaxWsDynamicClientFactory dcf = JaxWsDynamicClientFactory.newInstance();
Client client = dcf.createClient(ADDRESS);
// 需要密码的情况需要加上用户名和密码
// client.getOutInterceptors().add(new ClientLoginInterceptor(USER_NAME, PASS_WORD));
Object[] objects = new Object[0];
try {
// invoke("方法名",参数1,参数2,参数3....);
objects = client.invoke("sayHello", "admin");
System.out.println("返回的数据:" + objects[0]);
} catch (Exception e) {
e.printStackTrace();
}
}
}
运行后,控制台可以找到输出如下:
返回的数据:sayHello admin
状态码:00000
提示信息:操作成功!
返回的数据:sayHello admin
状态码:00000
提示信息:操作成功!
Postmen中调用
地址栏输入Web Service 接口地址,选择post方式,Headers中设置Content-Type为text/xml;charset=utf-8
。
在Body中发送数据进行测试,选择raw 和 XML,其中xmlns="http://webservice.demo.example.com/" 对应Service中targetNamespace的值。
「接口:sayHello」
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<sayHello xmlns="http://webservice.demo.example.com/">
<name>张三name>
sayHello>
soap:Body>
soap:Envelope>
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<sayHello xmlns="http://webservice.demo.example.com/">
<name>张三name>
sayHello>
soap:Body>
soap:Envelope>
返回结果:
「接口:operateUser」
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<operateUser xmlns="http://webservice.demo.example.com/">
<userDTO>
<id>123id>
<userName>张三userName>
userDTO>
operateUser>
soap:Body>
soap:Envelope>
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<operateUser xmlns="http://webservice.demo.example.com/">
<userDTO>
<id>123id>
<userName>张三userName>
userDTO>
operateUser>
soap:Body>
soap:Envelope>
返回结果:
遇到的问题
Java中调用报:
javax.xml.ws.soap.SOAPFaultException: Error reading XMLStreamReader: Unexpected character '{' (code 123) in prolog; expected '
at [row,col {unknown-source}]: [1,1]
at org.apache.cxf.jaxws.JaxWsClientProxy.invoke(JaxWsClientProxy.java:161)
at com.sun.proxy.$Proxy37.sendWorkingHoursOrder(Unknown Source)
javax.xml.ws.soap.SOAPFaultException: Error reading XMLStreamReader: Unexpected character '{' (code 123) in prolog; expected '
at [row,col {unknown-source}]: [1,1]
at org.apache.cxf.jaxws.JaxWsClientProxy.invoke(JaxWsClientProxy.java:161)
at com.sun.proxy.$Proxy37.sendWorkingHoursOrder(Unknown Source)
出现问题的原因:是因为Spring Boot整合了在Shiro,这个Web Service路径没有添加到可以过滤的路径上面去。
解决:在Shiro配置文件中加上这个过滤路径。
// 过滤掉WebService的接口
filterMap.put("/ws/**","anon");
// 过滤掉WebService的接口
filterMap.put("/ws/**","anon");