WebService的学习
1、WebService基本学习
WebService 即 Web服务,它是一种支持跨编程语言和跨操作系统的远程调用技术。比如,WebService服务端采用PHP语言编写,而客户端(Client)可以使用PHP,也可以使用JAVA语言来访问WebService服务端。
实现WebService的方式有不少。比如,使用JDK自带的JAX-WS,或者使用Apache CXF框架。这里,我们使用Apache CXF框架进行WebService的服务发布(服务端搭建)和 客户端远程访问(客户端搭建)。
Apache CXF官方文档:http://cxf.apache.org/docs/index.html
2、WebService规范
JAVA中有三种WebService规范:JAX-WS、JAX-RS、JAXM&SAAJ,常用的两种WebService规范是 JAX-WS 和 JAX-RS
JAX-WS,全称 Java Api For Xml-WebService,采用SOAP协议。
JAX-RS 采用HTTP协议,它发行比较晚,使用的是Restful风格定义的一套WebService规范。
SOAP协议,即简单对象访问协议(Simple Object Access Protocol),WebService使用的一种通信协议,用于客户端和服务端之间的通信。
3、wsdl说明书
WebService描述语言:WSDL(WebService Definition Language),实际就是一个XML文件,文件里描述了对外发布的服务名,接口的方法名,接口参数,返回值类型等等。
4、WebService服务端搭建
Maven依赖
<dependencies><dependency><groupId>org.apache.cxf</groupId><artifactId>cxf-rt-frontend-jaxws</artifactId><version>3.0.1</version></dependency><!--内置jetty web服务器,如果整合了SpringBoot,就不需要这个依赖,SpringBoot自带了tomcat服务器--><dependency><groupId>org.apache.cxf</groupId><artifactId>cxf-rt-transports-http-jetty</artifactId><version>3.0.1</version></dependency></dependencies>
接口请求参数
public class User {private String username;private Integer age;private String password;public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}
}
定义对外发布的服务和接口信息
/*** 对外发布的服务名*/
public interface UserService {/*** 对外发布的接口** @param username* @return*/String queryUserInfo(String username, String password);/*** 对外发布的接口** @param* @return*/void saveUser(User user);
}
服务端接口的实现类
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;/*** @WebService不传属性值,默认类名作为服务名*/
@WebService(name = "UserWebServiceDemo", serviceName = "UMR")
public class UserServiceImpl implements UserService {@WebMethod@WebResult(name = "QueryResult")public String queryUserInfo(@WebParam(name = "username") String username,@WebParam(name = "password") String password) {return username + "查询信息成功";}@WebMethod(operationName = "saveFunction")@WebResultpublic void saveUser(@WebParam(name = "accountInfo") User user) {System.out.println("保存用户信息");}
}
发布服务
import org.apache.cxf.jaxws.JaxWsServerFactoryBean;public class ServiceProvider {public static void main(String[] args) {//发布服务的工厂JaxWsServerFactoryBean factory = new JaxWsServerFactoryBean();//设置服务地址factory.setAddress("http://localhost:9180/ws");//服务类factory.setServiceBean(new UserServiceImpl());//发布服务factory.create();System.out.println("服务发布成功");}
}
5、wsdl说明书的使用
wsdl说明书的访问地址:服务地址?wsdl
wsdl说明书的阅读顺序:从下往上阅读
根据wsdl文件最下面的service节点找到服务名
然后,根据服务名绑定的 "UMRSoapBinding" 进行搜索
找到bingding节点的operation子节点,operation的name属性就是接口的方法名
input节点的name属性 就是 输入参数名,output节点的是 返回参数名
根据 输入参数名 和 返回参数名,找到真正的参数信息
6、WebService客户端搭建
① Maven依赖 和 接口请求参数 ,跟服务端搭建时保持一致
② 定义客户端接口(不需要写他的实现类)
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;@WebService(name = "UserWebServiceDemo", serviceName = "UMR", targetNamespace = "http://impl.service.com/")
public interface UserClient {@WebMethod@WebResult(name = "QueryResult")String queryUserInfo(@WebParam(name = "username") String username,@WebParam(name = "password") String password);@WebMethod(operationName = "saveFunction")@WebResultvoid saveUser(@WebParam(name = "accountInfo") User user);
}
③ 访问远程服务端
import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;public class UserConsumer {public static void main(String[] args) {//创建cxf代理工厂JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();//设置服务端的地址factory.setAddress("http://localhost:9180/ws");//设置接口类型factory.setServiceClass(UserClient.class);//生成代理对象UserClient userClient = factory.create(UserClient.class);String result = userClient.queryUserInfo("hello", "123");System.out.println(result);}
}
收到了服务端的返回
7、常见异常
Exception in thread "main" javax.xml.ws.soap.SOAPFaultException:
Unexpected wrapper element {http://client.com/} queryUserInfo found.
Expected {http://impl.service.com/} queryUserInfo.
遇到 Unexpected wrapper element 错误,通常是客户端发的消息结构和服务端WSDL中定义的不匹配。
针对上面那个异常,是命名空间不一致导致的。服务端生成WSDL的时候,命名空间默认是包名倒序。而客户端跟服务端包路径不一致,就导致出现命名空间的错误。
所以,我才在客户端的@WebService注解中,用targetNamespace属性指定了命名空间,保证和服务端的一致。