基于Spring + CXF框架的Web Service
1、用CXF编写基于Spring的Web service,也是需要分为Server服务器端、Client客户端的。
1.1)、Server端,操作步骤,如下所示:
第一步,创建spring的配置文件beans.xml,在其中配置SEI。
第二步,在web.xml中,配置上CXF的一些核心组件。
1.2、Client端,操作步骤,如下所示:
第一步,生成客户端代码。
第二步,创建客户端的spring配置文件beans-client.xml,并配置。
第三步,编写测试类请求web service。
2、创建一个动态web工程,将apache-cxf-2.5.9\lib目录下面的包添加到此动态工程的lib目录下面,然后Build Path一下的哦。如果要看源代码,需要下载对应的src包的,不然无法进行查看源代码的。
创建web.xml配置文件,如下所示:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 3 xmlns="http://java.sun.com/xml/ns/javaee" 4 xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 5 http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" 6 id="WebApp_ID" version="2.5"> 7 <display-name>webServiceCxf_Spring</display-name> 8 <welcome-file-list> 9 <welcome-file>index.html</welcome-file> 10 <welcome-file>index.jsp</welcome-file> 11 </welcome-file-list> 12 13 <!-- 配置beans.xml --> 14 <context-param> 15 <param-name>contextConfigLocation</param-name> 16 <param-value>classpath:beans.xml</param-value> 17 </context-param> 18 <!-- 应用启动的一个监听器 --> 19 <listener> 20 <listener-class> 21 org.springframework.web.context.ContextLoaderListener 22 </listener-class> 23 </listener> 24 25 <!-- 所有请求都会先经过cxf框架 --> 26 <servlet> 27 <servlet-name>CXFServlet</servlet-name> 28 <servlet-class> 29 org.apache.cxf.transport.servlet.CXFServlet 30 </servlet-class> 31 <load-on-startup>1</load-on-startup> 32 </servlet> 33 <servlet-mapping> 34 <servlet-name>CXFServlet</servlet-name> 35 <url-pattern>/*</url-pattern> 36 </servlet-mapping> 37 38 </web-app>
创建webservice的接口服务,如下所示:
1 package com.bie.webservice.ws; 2 3 import javax.jws.WebMethod; 4 import javax.jws.WebService; 5 6 import com.bie.webservice.bean.Order; 7 8 /** 9 * 10 * @author 11 * 12 */ 13 @WebService 14 public interface OrderWS { 15 16 /** 17 * 根据订单号获取到订单信息 18 * 19 * @param id 20 * @return 21 */ 22 @WebMethod 23 public Order getOrderById(int id); 24 25 }
创建webservice的接口服务实现类,如下所示:
1 package com.bie.webservice.ws.impl; 2 3 import javax.jws.WebService; 4 5 import com.bie.webservice.bean.Order; 6 import com.bie.webservice.ws.OrderWS; 7 8 /** 9 * 10 * @author 项目部署的时候就创建好了webservice服务了。项目启动就自动部署了webservice了。 11 * 12 */ 13 @WebService 14 public class OrderWSImpl implements OrderWS { 15 16 /** 17 * 从对象什么时候创建呢,结论,项目部署的时候执行了 18 */ 19 public OrderWSImpl() { 20 System.out.println("无参构造器执行,OrderWSImpl......"); 21 } 22 23 @Override 24 public Order getOrderById(int id) { 25 System.out.println("Server getOrderById() : " + id); 26 return new Order(1, "大飞机", 999999999.00); 27 } 28 29 }
创建一个是实体类,如下所示:
1 package com.bie.webservice.bean; 2 3 /** 4 * 5 * @author 6 * 7 */ 8 public class Order { 9 10 private int id;// 订单编号 11 private String name;// 订单名称 12 private Double price;// 订单价格 13 14 public int getId() { 15 return id; 16 } 17 18 public void setId(int id) { 19 this.id = id; 20 } 21 22 public String getName() { 23 return name; 24 } 25 26 public void setName(String name) { 27 this.name = name; 28 } 29 30 public Double getPrice() { 31 return price; 32 } 33 34 public void setPrice(Double price) { 35 this.price = price; 36 } 37 38 @Override 39 public String toString() { 40 return "Order [id=" + id + ", name=" + name + ", price=" + price + "]"; 41 } 42 43 public Order(int id, String name, Double price) { 44 super(); 45 this.id = id; 46 this.name = name; 47 this.price = price; 48 } 49 50 }
创建beans.xml配置文件,如下所示:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xmlns:jaxws="http://cxf.apache.org/jaxws" 5 xsi:schemaLocation="http://www.springframework.org/schema/beans 6 http://www.springframework.org/schema/beans/spring-beans.xsd 7 http://cxf.apache.org/jaxws 8 http://cxf.apache.org/jaxws"> 9 10 <!-- 引cxf-2.5.9.jar此包下面的配置,这些配置不在此项目中,cxf的一些核心配置 --> 11 <import resource="classpath:META-INF/cxf/cxf.xml" /> 12 <import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" /> 13 <import resource="classpath:META-INF/cxf/cxf-servlet.xml" /> 14 15 <!-- 16 jaxws:endpoint标签配置终端。 17 implementor是实现类。 18 address是虚拟地址。 19 --> 20 <jaxws:endpoint id="orderWS" 21 implementor="com.bie.webservice.ws.impl.OrderWSImpl" 22 address="/orderws"> 23 <!-- 24 <jaxws:inInterceptors> 25 <bean class="com.bie.interceptor.CheckUserInterceptor"></bean> 26 </jaxws:inInterceptors> 27 --> 28 </jaxws:endpoint> 29 30 </beans>
项目结构,如下所示:
然后可以进行发布了,Run as -> Run on Server,启动项目,项目启动就自动部署了webservice了,可以进行访问,如下所示:
访问地址:http://localhost:8080/webServiceCxf_Spring/orderws?wsdl,其中项目名称后面的地址是beans.xml里面配置的address属性的值。
可以使用eclipse自带的web service浏览器进行查看,如下所示:
3、然后,创建一个客户端访问的动态web工程,将apache-cxf-2.5.9\lib目录下面的包添加到此动态工程的lib目录下面,然后Build Path一下的哦。如果要看源代码,需要下载对应的src包的,不然无法进行查看源代码的。此时,还是需要借助java自带的工具来生成客户端的代码,如下所示:
刷新项目,就可以看到生成的代码了,如下所示:
然后创建一个配置文件client-beans.xml,里面需要进行配置webservice的请求地址和所需要生成的动态代理对象。
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xmlns:jaxws="http://cxf.apache.org/jaxws" 5 xsi:schemaLocation="http://www.springframework.org/schema/beans 6 http://www.springframework.org/schema/beans/spring-beans.xsd 7 http://cxf.apache.org/jaxws http://cxf.apache.org/jaxws"> 8 9 <!-- 10 客户端配置: 11 serviceClass根据此类动态产生接口的代理对象。 12 address是指的是webservice请求的地址。 13 --> 14 <jaxws:client id="orderClient" 15 serviceClass="com.bie.webservice.ws.OrderWS" 16 address="http://localhost:8080/webServiceCxf_Spring/orderws"> 17 18 <!-- <jaxws:outInterceptors> 19 <bean class="org.apache.cxf.interceptor.LoggingOutInterceptor" /> 20 <bean 21 class="com.bie.webservice.ws.interceptor.AddUserInterceptor"> 22 <constructor-arg name="name" value="张姗姗" /> 23 <constructor-arg name="password" value="123456" /> 24 </bean> 25 </jaxws:outInterceptors> --> 26 </jaxws:client> 27 28 </beans>
最后搞一个客户端的访问类,就可以进行运行了,如下所示:
1 package com.bie.webservice.ws.client; 2 3 import org.springframework.context.support.ClassPathXmlApplicationContext; 4 5 import com.bie.webservice.ws.Order; 6 import com.bie.webservice.ws.OrderWS; 7 8 /** 9 * 10 * @author 客户端访问webservice的代码 11 * 12 */ 13 public class ClientCxfSpring { 14 15 public static void main(String[] args) { 16 ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext( 17 new String[] { "client-beans.xml" }); 18 OrderWS orderWS = (OrderWS) context.getBean("orderClient"); 19 Order order = orderWS.getOrderById(1); 20 System.out.println(order); 21 } 22 23 }
4、如何在此基础上添加自定义拦截器,步骤如下所示:
第一步,Server端,在beans.xml中,在endpoint中配置上入拦截器。
1 package com.bie.webservice.interceptor; 2 3 import javax.xml.namespace.QName; 4 5 import org.apache.cxf.binding.soap.SoapMessage; 6 import org.apache.cxf.headers.Header; 7 import org.apache.cxf.interceptor.Fault; 8 import org.apache.cxf.phase.AbstractPhaseInterceptor; 9 import org.apache.cxf.phase.Phase; 10 import org.w3c.dom.Element; 11 import org.w3c.dom.Node; 12 13 /** 14 * 查检用户的拦截器 15 * 16 * @author 17 * 18 */ 19 20 public class CheckUserInterceptor extends AbstractPhaseInterceptor<SoapMessage> { 21 22 private static final String name = "张姗姗"; 23 private static final String password = "123456"; 24 25 public CheckUserInterceptor() { 26 super(Phase.PRE_PROTOCOL); 27 System.out.println("CheckUserInterceptor()"); 28 } 29 30 /** 31 * 请求体,服务器端需要解析请求头信息 32 * 33 * <soap:Envelope> 34 * <head> 35 * <zhangsansan> 36 * <name>zhangsansan</name> 37 * <password>123456</password> 38 * </zhangsansan> 39 * </head> 40 * <Body> 41 * <sayHello> 42 * <arg0>张姗姗</arg0> 43 * </sayHello> 44 * </Body> 45 * </soap:Envelope> 46 */ 47 @Override 48 public void handleMessage(SoapMessage soapMessage) throws Fault { 49 // 获取到请求头的信息 50 QName qName = QName.valueOf("zhangsansan"); 51 // 获取到请求头 52 Header header = soapMessage.getHeader(qName); 53 // 判断是否为空 54 if(header != null) { 55 // 获取到对象,强转为w3c的元素标签 56 Element element = (Element) header.getObject(); 57 // 获取到name标签的值 58 Node nameNode = element.getElementsByTagName("name").item(0); 59 // 获取到name的值 60 String nameValue = nameNode.getTextContent(); 61 // 获取到pasword标签的值 62 Node passwordNode = element.getElementsByTagName("password").item(0); 63 // 获取到pasword的值 64 String paswordValue = passwordNode.getTextContent(); 65 // 开始进行判断 66 if(CheckUserInterceptor.name.equals(nameValue) && CheckUserInterceptor.password.equals(paswordValue)) { 67 System.out.println("Server 通过拦截器......"); 68 return; 69 } 70 } 71 // 如果不能通过 72 System.out.println("Sorry Server 不通过拦截器......"); 73 // 抛出异常信息 74 throw new Fault(new RuntimeException("账号密码错误......")); 75 } 76 77 }
然后在服务器端的beans.xml进行配置,如下所示:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xmlns:jaxws="http://cxf.apache.org/jaxws" 5 xsi:schemaLocation="http://www.springframework.org/schema/beans 6 http://www.springframework.org/schema/beans/spring-beans.xsd 7 http://cxf.apache.org/jaxws http://cxf.apache.org/jaxws"> 8 9 <!-- 引cxf-2.5.9.jar此包下面的配置,这些配置不在此项目中,cxf的一些核心配置 --> 10 <import resource="classpath:META-INF/cxf/cxf.xml" /> 11 <import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" /> 12 <import resource="classpath:META-INF/cxf/cxf-servlet.xml" /> 13 14 <!-- 15 jaxws:endpoint标签配置终端。 16 implementor是实现类。 17 address是虚拟地址。 18 --> 19 <jaxws:endpoint id="orderWS" 20 implementor="com.bie.webservice.ws.impl.OrderWSImpl" 21 address="/orderws"> 22 <!-- 服务器端配置入拦截器 --> 23 <jaxws:inInterceptors> 24 <!-- 配置自定义的拦截器,注入到ioc容器中 --> 25 <bean class="com.bie.webservice.interceptor.CheckUserInterceptor"></bean> 26 </jaxws:inInterceptors> 27 </jaxws:endpoint> 28 29 </beans>
如果开发完毕,重启项目,报下面的错误,我的操作是clean项目,清空tomcat下面的项目,清空tomcat,重启项目解决的。
1 cvc-complex-type.2.4.c: 通配符的匹配很全面, 但无法找到元素 'context:property-placeholder' 的声明
第二步,Client端,通过Client对象设置出拦截器。
1 package com.bie.webservice.ws.interceptor; 2 3 import java.util.List; 4 5 import javax.xml.namespace.QName; 6 import javax.xml.parsers.DocumentBuilder; 7 import javax.xml.parsers.DocumentBuilderFactory; 8 import javax.xml.parsers.ParserConfigurationException; 9 10 import org.apache.cxf.binding.soap.SoapMessage; 11 import org.apache.cxf.headers.Header; 12 import org.apache.cxf.interceptor.Fault; 13 import org.apache.cxf.phase.AbstractPhaseInterceptor; 14 import org.apache.cxf.phase.Phase; 15 import org.apache.xml.utils.DOMHelper; 16 import org.w3c.dom.Document; 17 import org.w3c.dom.Element; 18 19 /** 20 * 21 * @author 拦截的是某一个消息,所以泛型是SoapMessage 22 * 23 */ 24 public class ClientValidateUserInterceptor extends AbstractPhaseInterceptor<SoapMessage> { 25 26 private String name;// 账号 27 private String password;// 密码 28 29 /** 30 * 此构造器是关键点,决定了什么时候拦截器会拦截到消息 31 * 32 * @param phase 33 */ 34 public ClientValidateUserInterceptor(String name, String password) { 35 // 准备协议化的时候进行拦截调用 36 super(Phase.PRE_PROTOCOL); 37 this.name = name; 38 this.password = password; 39 System.out.println("Client 客户端,拦截器初始化......"); 40 } 41 42 /** 43 * 请求体 44 * 45 * <soap:Envelope> 46 * <head> 47 * <zhangsansan> 48 * <name>zhangsansan</name> 49 * <password>123456</password> 50 * </zhangsansan> 51 * </head> 52 * <Body> 53 * <sayHello> 54 * <arg0>张姗姗</arg0> 55 * </sayHello> 56 * </Body> 57 * </soap:Envelope> 58 */ 59 @Override 60 public void handleMessage(SoapMessage soapMessage) throws Fault { 61 // 获取到头信息,向头部信息设置值 62 List<Header> headers = soapMessage.getHeaders(); 63 // 此时需要构造这种结构的数据 64 // <zhangsansan> 65 // <name>zhangsansan</name> 66 // <password>123456</password> 67 // </zhangsansan> 68 69 // 第一步:初始化一个XML解析工厂 70 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 71 // 第二步:创建一个DocumentBuilder实例 72 DocumentBuilder builder = null; 73 try { 74 builder = factory.newDocumentBuilder(); 75 } catch (ParserConfigurationException e) { 76 // TODO Auto-generated catch block 77 e.printStackTrace(); 78 } 79 // 第三步:构建一个Document实例 80 Document doc = builder.newDocument(); 81 // standalone用来表示该文件是否呼叫其它外部的文件。若值是 ”yes” 表示没有呼叫外部文件 82 doc.setXmlStandalone(true); 83 // 第四步:创建一个根节点,名称为root,并设置一些基本属性,创建标签 84 Element rootElement = doc.createElement("zhangsansan"); 85 // 设置节点属性 86 // rootElement.setAttribute("attr", "root"); 87 // 设置标签之间的内容 88 // rootElement.setTextContent("root attr"); 89 90 // 开始设置<zhangsansan>下面的标签<name>zhangsansan</name> 91 Element nameElement = doc.createElement("name"); 92 nameElement.setTextContent(this.name); 93 // 第五步:把节点添加到Document中,再创建一些子节点加入,将子标签添加到父标签中 94 rootElement.appendChild(nameElement); 95 96 // 开始设置<zhangsansan>下面的标签<name>zhangsansan</name> 97 Element passwordElement = doc.createElement("password"); 98 passwordElement.setTextContent(this.password); 99 // 第五步:把节点添加到Document中,再创建一些子节点加入,将子标签添加到父标签中 100 rootElement.appendChild(passwordElement); 101 102 // 第六步:把构造的XML结构,写入到具体的文件中 103 // 参数一QName起一个唯一的名字,这个名称必须和rootElement标签的值必须一样 104 // 参数二就是rootElement根节点 105 Header header = new Header(new QName("zhangsansan"), rootElement); 106 // 将此请求体和构建的请求头发送给服务器端 107 headers.add(header); 108 109 System.out.println("Client handleMessage Interceptor......"); 110 111 // DOMHelper.createDocument()方法过期了 112 // Document createDocument = DOMHelper.createDocument(); 113 } 114 115 }
然后在客户端的beans.xml进行配置,如下所示:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xmlns:jaxws="http://cxf.apache.org/jaxws" 5 xsi:schemaLocation="http://www.springframework.org/schema/beans 6 http://www.springframework.org/schema/beans/spring-beans.xsd 7 http://cxf.apache.org/jaxws http://cxf.apache.org/jaxws"> 8 9 <!-- 10 客户端配置: 11 serviceClass根据此类动态产生接口的代理对象。 12 address是指的是webservice请求的地址。 13 --> 14 <jaxws:client id="orderClient" 15 serviceClass="com.bie.webservice.ws.OrderWS" 16 address="http://localhost:8080/webServiceCxf_Spring/orderws"> 17 18 <!-- 客户端配置出拦截器类 --> 19 <jaxws:outInterceptors> 20 <!-- <bean class="org.apache.cxf.interceptor.LoggingOutInterceptor" /> --> 21 <bean 22 class="com.bie.webservice.ws.interceptor.ClientValidateUserInterceptor"> 23 <!-- 通过构造参数传递给客户端拦截器类 --> 24 <constructor-arg name="name" value="张姗姗" /> 25 <constructor-arg name="password" value="123456" /> 26 </bean> 27 </jaxws:outInterceptors> 28 </jaxws:client> 29 30 </beans>
使用tomcat启动服务端,然后客户端访问服务端,效果如下所示: