CXF-09: CXF 与 Spring 的整合 之 暴露 Web Service 接口
2016-10-27 22:53
881 查看
CXF 与 Spring 的整合:可以在传统的 Java EE 应用的基础上添加一层 Web Service 层,我们的 Java EE 应用就可以对外暴露成 Web Service,这样就允许任何平台、任何语言编写的程序来调用这个 Java EE 应用;
* 在传统Spring项目基础上增加 Web Service 的步骤(SSH 已经配置过,SSM 未配过):
1 . 复制 CXF 的 jar 包(最核心的6个), jar 包地址 http://pan.baidu.com/s/1jHJ56BW
注:请选择性复制 (Apache官网上也可以下载);
asm-3.3.jar commons-logging-1.1.1.jar cxf-2.4.1.jar neethi-3.0.0.jar wsdl4j-1.6.2.jar xmlschema-core-2.0.jar
2 . 在 web.xml 配置CXF的核心控制器:CXFServlet;
3 . 在 Spring 配置文件中导入 CXF 提供 Schema、XML(cxf.jar 包里提供)
* Schema 文件
<beans xmlns:jaxws="http://cxf.apache.org/jaxws"
xsi:schemaLocation="
http://cxf.apache.org/jaxws //命名空间
http://cxf.apache.org/schemas/jaxws.xsd">//物理路径
* XML 的配置文件
[b] <import resource="classpath:META-INF/cxf/cxf.xml"/>
[b] <import resource="classpath:META-INF/cxf/cxf-extension-soap.xml"/>
<import resource="classpath:META-INF/cxf/cxf-servlet.xml"/>[/b]
4 . 在 Spring 配置文件中使用 jaxws:endpoint 元素类暴露 Web Service;
[/b]
5 . 添加拦截器,在 jaxws:endpoint 元素里添加 inInterceptors、outInterceptors 子元素;
* 代码有时比语言更有说服力,新建一个 Web Project 项目代码如下:
(1)、服务器端:
①Food.java
②User.java
③UserService.java
[b]④UserServiceImpl.java[/b]
⑤HelloWorld.java
[b]⑥AuthInterceptor.java[/b]
[b]⑦HelloWorldWs.java[/b]
[b]⑧HelloXmlAdepter.java[/b]
[b]⑨StringFood.java[/b]
[b]⑩applicationContext.xml[/b]
我编写的服务器端地址为:http://192.168.0.159:8080/CXF_Spring/cxf-Spring/service/getAllFoods?wsdl;
[b][b] 按照文章 CXF-02 中生成客户端的操作,用命令 wsdl2java http://192.168.0.159:8080/CXF_Spring/cxf-Spring/service/getAllFoods?wsdl 生成客户端;
①ClientMain.java
[/b][/b]
代码编写完毕,运行结果如下,
[b]服务端正常启动运行客户端调用运行结果:
[/b]
[b]客户端运行结果:[/b]
[b]做一个权限控制,有用户名和密码的时候才允许调用 Web Service 该功能完成!
[/b]
希望对你有帮助,祝你有一个好心情,加油!
若有错误、不全、可优化的点,欢迎纠正与补充;转载请注明出处!
若有疑问请下方留言。
* 在传统Spring项目基础上增加 Web Service 的步骤(SSH 已经配置过,SSM 未配过):
1 . 复制 CXF 的 jar 包(最核心的6个), jar 包地址 http://pan.baidu.com/s/1jHJ56BW
注:请选择性复制 (Apache官网上也可以下载);
asm-3.3.jar commons-logging-1.1.1.jar cxf-2.4.1.jar neethi-3.0.0.jar wsdl4j-1.6.2.jar xmlschema-core-2.0.jar
2 . 在 web.xml 配置CXF的核心控制器:CXFServlet;
3 . 在 Spring 配置文件中导入 CXF 提供 Schema、XML(cxf.jar 包里提供)
* Schema 文件
<beans xmlns:jaxws="http://cxf.apache.org/jaxws"
xsi:schemaLocation="
http://cxf.apache.org/jaxws //命名空间
http://cxf.apache.org/schemas/jaxws.xsd">//物理路径
* XML 的配置文件
[b] <import resource="classpath:META-INF/cxf/cxf.xml"/>
[b] <import resource="classpath:META-INF/cxf/cxf-extension-soap.xml"/>
<import resource="classpath:META-INF/cxf/cxf-servlet.xml"/>[/b]
4 . 在 Spring 配置文件中使用 jaxws:endpoint 元素类暴露 Web Service;
[/b]
5 . 添加拦截器,在 jaxws:endpoint 元素里添加 inInterceptors、outInterceptors 子元素;
* 代码有时比语言更有说服力,新建一个 Web Project 项目代码如下:
(1)、服务器端:
①Food.java
public class Food { private Integer id; private String name; private String describe; public Food() { super(); } public Food(Integer id, String name, String describe) { super(); this.id = id; this.name = name; this.describe = describe; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDescribe() { return describe; } public void setDescribe(String describe) { this.describe = describe; } }
②User.java
public class User { private Integer id; private String name; private String tel; private String describe; public Integer getId() { return id; } public User() { super(); } public User(Integer id, String name, String tel, String describe) { super(); this.id = id; this.name = name; this.tel = tel; this.describe = describe; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getTel() { return tel; } public void setTel(String tel) { this.tel = tel; } public String getDescribe() { return describe; } public void setDescribe(String describe) { this.describe = describe; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((describe == null) ? 0 : describe.hashCode()); result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; User other = (User) obj; if (describe == null) { if (other.describe != null) return false; } else if (!describe.equals(other.describe)) return false; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; return true; } }
③UserService.java
import java.util.List; import java.util.Map; import org.fjava.cxf.model.Food; import org.fjava.cxf.model.User; public interface UserService { List<Food> getFoodsByUser(User user); Map<String, Food> getAllFoods(); }
[b]④UserServiceImpl.java[/b]
import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.fjava.cxf.model.Food; import org.fjava.cxf.model.User; public class UserServiceImpl implements UserService { //为简单,不写Dao层,用一个HashMap来模拟内存中的数据库 static Map<User, List<Food>> foodDb = new HashMap<>(); static{ List<Food> catList1 = new ArrayList<Food>(); catList1.add(new Food(1 , "一个汉堡" , "是三层的,有夹层哦!")); catList1.add(new Food(2 , "火腿肠" , "这是章鱼哥从岸上偷运来的,据说很美味!")); foodDb.put(new User(1 , "海绵宝宝" , "123123123" , "开心的海绵宝宝!") , catList1); List<Food> catList2 = new ArrayList<Food>(); catList2.add(new Food(3 , "蟹王汉堡" , "橙色,亮金色,我的宝贝,我的爱!")); catList2.add(new Food(4 , "海绵金币" , "吃着金币样的甜甜饼,想着海绵宝宝赚的钱被扣了,哈哈哈,爽气!")); foodDb.put(new User(2 , "蟹老板" , "321321321" , "哇!好有钱的蟹老板") , catList2); } @Override public List<Food> getFoodsByUser(User user) { return foodDb.get(user); } @Override public Map<String, Food> getAllFoods() { Map<String, Food> map = new HashMap<String, Food>(); for (List<Food> foods : foodDb.values()) { for (Food food : foods) { map.put(food.getName(), food); } } return map; } }
⑤HelloWorld.java
import java.util.List; import java.util.Map; import javax.jws.WebService; import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; import org.fjava.cxf.model.Food; import org.fjava.cxf.model.User; import org.fjava.cxf.ws.util.HelloXmlAdepter; @WebService//J2EE文档里查看WebService public interface HelloWorld { //还有一些像 Map、非JavaBean式的复合类,CXF不可以处理; //CXF不能处理Map<String, Food>类型,我们采用HelloXmlAdepter进行处理 @XmlJavaTypeAdapter(value = HelloXmlAdepter.class) Map<String, Food> getAllFoods(); }
[b]⑥AuthInterceptor.java[/b]
import java.util.ArrayList; import java.util.List; import org.apache.cxf.binding.soap.SoapMessage; import org.apache.cxf.headers.Header; import org.apache.cxf.interceptor.Fault; import org.apache.cxf.phase.AbstractPhaseInterceptor; import org.apache.cxf.phase.Phase; import org.w3c.dom.Element; import org.w3c.dom.NodeList; //通过PhaseInterceptor,可以指定拦截器在哪个阶段起作用。 public class AuthInterceptor extends AbstractPhaseInterceptor<SoapMessage> { public AuthInterceptor(){ //super表示显示调用父类有参数的构造器。 //显示调用父类构造器,程序将不会隐式调用父类无参的构造器。 //---父类构造器里方法AbstractPhaseInterceptor(String phase) //---phase是指一个拦截阶段 //---CXF文档里有Phase类,这个类里有各个阶段 super(Phase.PRE_INVOKE);//该拦截器将会在"调用之前"拦截Soap消息。 } //实现自己的拦截器时,需要实现handleMessage方法。 //handleMessage方法中的形参就是被拦截到的Soap消息。 //一旦程序获得了Soap消息,剩下的事情就可以解析Soap消息,或修改Soap消息。 @Override public void handleMessage(SoapMessage msg) throws Fault{ //下面代码显示"调用之前"成功拦截了信息 System.out.println("----------------"+ msg); //得到Soap消息的所有Header List<Header> headers = new ArrayList<Header>(); try { headers = msg.getHeaders(); } catch (Exception e) { throw new Fault(new IllegalArgumentException("没有Header,禁止调用!")); } //如果根本没有Header if(headers == null || headers.size() < 1 ){ throw new Fault(new IllegalArgumentException("没有Header,禁止调用!")); } System.out.println("headers" + headers); //假如要求第一个Header携带了用户名、密码信息 Header firstHeader = headers.get(0); Element ele = (Element)firstHeader.getObject(); NodeList userIds = ele.getElementsByTagName("userId"); NodeList passwords = ele.getElementsByTagName("password"); if(userIds == null || userIds.getLength() != 1){ throw new Fault(new IllegalArgumentException("用户名格式不正确!")); } if(passwords == null || passwords.getLength() != 1){ throw new Fault(new IllegalArgumentException("密码格式不正确!")); } System.out.println("userIds" + userIds); System.out.println("passwords" + passwords); //得到userId元素里的文本内容,以该内容作为用户名 String userId = userIds.item(0).getTextContent(); String password = passwords.item(0).getTextContent(); //实际项目中,是去查询数据库,该用户名、密码是否被授权访问该Web Service。 if(!"admin".equals(userId) || !"admin".equals(password)){ throw new Fault(new IllegalArgumentException("用户名、密码不正确!")); } } }
[b]⑦HelloWorldWs.java[/b]
import java.util.Date; import java.util.List; import java.util.Map; import javax.jws.WebService; import org.fjava.cxf.model.Food; import org.fjava.cxf.model.User; import org.fjava.cxf.ws.HelloWorld; import org.springframework.stereotype.Service; import org.fjava.cxf.service.UserService; import org.fjava.cxf.service.UserServiceImpl; @Service @WebService(endpointInterface="org.fjava.cxf.ws.HelloWorld", serviceName="HelloWorldWs") //endpointInterface=""表示实现的接口 serviceName是wsdl2java或其他语言后的文件名,可以不和HelloWorldWs相同 public class HelloWorldWs implements HelloWorld { private UserService userService; public void setUserService(UserService userService) { this.userService = userService; } @Override public Map<String, Food> getAllFoods() { return userService.getAllFoods(); } }
[b]⑧HelloXmlAdepter.java[/b]
import java.util.HashMap; import java.util.Map; import javax.xml.bind.annotation.adapters.XmlAdapter; import org.fjava.cxf.model.Food; import org.fjava.cxf.ws.util.StringFood.Entry; //该转换器负责完成StringFood与Map<String, Food>的相互转换 public class HelloXmlAdepter extends XmlAdapter<StringFood, Map<String, Food>> { @Override public Map<String, Food> unmarshal(StringFood v) throws Exception { Map<String, Food> resMap = new HashMap<>(); for (Entry entry : v.getEntries()) { resMap.put(entry.getKey(), entry.getValue()); } return resMap; } @Override public StringFood marshal(Map<String, Food> v) throws Exception { StringFood sf = new StringFood(); for (String key : v.keySet()) { sf.getEntries().add(new Entry(key, v.get(key))); } return sf; } }
[b]⑨StringFood.java[/b]
import java.util.ArrayList; import java.util.List; import org.fjava.cxf.model.Food; public class StringFood { public static class Entry{ private String key; private Food value; public Entry() { super(); } public Entry(String key, Food value) { super(); this.key = key; this.value = value; } public String getKey() { return key; } public void setKey(String key) { this.key = key; } public Food getValue() { return value; } public void setValue(Food value) { this.value = value; } } private List<Entry> entries = new ArrayList<>(); public List<Entry> getEntries() { return entries; } public void setEntries(List<Entry> entries) { this.entries = entries; } }
[b]⑩applicationContext.xml[/b]
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:p="http://www.springframework.org/schema/p" xmlns:jaxws="http://cxf.apache.org/jaxws" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- Web应用的类加载路径有两类 1.WEB-INF/classes目录 2.WEB-INF/lib目录下 --> <import resource="classpath:META-INF/cxf/cxf.xml"/> <import resource="classpath:META-INF/cxf/cxf-extension-soap.xml"/> <import resource="classpath:META-INF/cxf/cxf-servlet.xml"/> <!-- 注册Service --> <bean id="userService" class="org.fjava.cxf.service.UserServiceImpl"/> <bean id="helloWorldWs" class="org.fjava.cxf.ws.impl.HelloWorldWs" p:userService-ref="userService"/> <!-- implementor指定WebService的服务提供者 1.直接给定服务器提供者类名 2.设置为容器中的Bean,要在Bean的id前加上#号 --> <jaxws:endpoint implementor="#helloWorldWs" address="/getAllFoods"> <!-- 添加了两个In拦截器 --> <jaxws:inInterceptors> <bean class="org.apache.cxf.interceptor.LoggingInInterceptor"/><!-- 嵌套Bean,创建一个Bean --> <bean class="org.fjava.cxf.ws.auth.AuthInterceptor"/> <!-- <ref bean="anotherInterceptor">引用一个已有的Bean --> </jaxws:inInterceptors> <!-- 需要配置Out拦截器,使用<jaxws:outInterceptors> --> </jaxws:endpoint> <!-- <jaxws:endpoint implementor="org.fjava.cxf.ws.impl.HelloWorldWs" address="/getAllFoods"> </jaxws:endpoint> --> </beans>⑪web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5"> <display-name></display-name> <context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/applicationContext.xml</param-value> </context-param> <!-- 该Listener会保证在Web应用启动时加载Spring容器 --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- 配置CXF的核心控制器,所有来自/cxf-Spring/service/*的请求都交给CXF处理 --> <servlet> <servlet-name>cxf</servlet-name> <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>cxf</servlet-name> <url-pattern>/cxf-Spring/service/*</url-pattern> </servlet-mapping> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> </web-app>(2)、客户端:
我编写的服务器端地址为:http://192.168.0.159:8080/CXF_Spring/cxf-Spring/service/getAllFoods?wsdl;
[b][b] 按照文章 CXF-02 中生成客户端的操作,用命令 wsdl2java http://192.168.0.159:8080/CXF_Spring/cxf-Spring/service/getAllFoods?wsdl 生成客户端;
①ClientMain.java
[/b][/b]
import java.util.List; import org.apache.cxf.endpoint.Client; import org.apache.cxf.frontend.ClientProxy; import org.apache.cxf.interceptor.LoggingInInterceptor; import org.apache.cxf.interceptor.LoggingOutInterceptor; import org.apache.cxf.jaxws.endpoint.dynamic.JaxWsDynamicClientFactory; import org.fjava.cxf.ws.Entry; import org.fjava.cxf.ws.Food; import org.fjava.cxf.ws.HelloWorld; import org.fjava.cxf.ws.StringFood; import org.fjava.cxf.ws.auth.AddHeaderInterceptor; import org.fjava.cxf.ws.impl.HelloWorldWs; public class ClientMain { public static void main(String[] args) { //这是命令生成的类,该类的实例可当成工厂来使用 HelloWorldWs factory = new HelloWorldWs(); //无参的方法,返回的是远程Web Service服务端的代理,服务端不能关闭。 HelloWorld helloWorld = factory.getHelloWorldWsPort(); Client client = ClientProxy.getClient(helloWorld); client.getOutInterceptors().add(new AddHeaderInterceptor("admin","admin")); client.getOutInterceptors().add(new LoggingOutInterceptor()); StringFood allFoods = helloWorld.getAllFoods(); List<Entry> entries = allFoods.getEntries(); for (Entry entry : entries) { System.out.println(entry.getKey() + " " + entry.getValue().getDescribe()); } } }②AddHeaderInterceptor.java——服务端限制了用户名与密码,在 Soap 中增加 header 元素
import java.util.List; import javax.xml.namespace.QName; import org.apache.cxf.binding.soap.SoapMessage; import org.apache.cxf.headers.Header; import org.apache.cxf.helpers.DOMUtils; import org.apache.cxf.interceptor.Fault; import org.apache.cxf.phase.AbstractPhaseInterceptor; import org.apache.cxf.phase.Phase; import org.w3c.dom.Document; import org.w3c.dom.Element; public class AddHeaderInterceptor extends AbstractPhaseInterceptor<SoapMessage> { private String userId; private String password; public AddHeaderInterceptor(String userId, String password) { super(Phase.PREPARE_SEND);//在准备发送Soap消息时启用该拦截器 this.userId = userId; this.password = password; } @Override public void handleMessage(SoapMessage msg) throws Fault { List<Header> headers = msg.getHeaders(); //创建Document对象 Document document = DOMUtils.createDocument(); Element eleAuthHeader = document.createElement("authHeader"); //此处创建的元素应该与服务器的拦截元素相同 Element eleUserId = document.createElement("userId"); eleUserId.setTextContent(userId); Element elePassword = document.createElement("password"); elePassword.setTextContent(password); eleAuthHeader.appendChild(eleUserId); eleAuthHeader.appendChild(elePassword); /* * 上面代码生成了一个如下XML文档片段: * <authHeader> * <userId></userId> * <password></password> *</authHeader> */ //把ele元素包装成Header,并添加到SOAP消息的Header列表中。 headers.add(new Header(new QName("fjava"),eleAuthHeader)); } }
代码编写完毕,运行结果如下,
[b]服务端正常启动运行客户端调用运行结果:
[/b]
十月 27, 2016 10:43:30 下午 org.apache.cxf.transport.servlet.CXFServlet updateContext 信息: Load the bus with application context 十月 27, 2016 10:43:30 下午 org.apache.cxf.bus.spring.BusApplicationContext getConfigResources 信息: No cxf.xml configuration file detected, relying on defaults. 十月 27, 2016 10:43:30 下午 org.apache.cxf.transport.servlet.AbstractCXFServlet replaceDestinationFactory 信息: Servlet transport factory already registered 十月 27, 2016 10:43:31 下午 org.apache.cxf.interceptor.LoggingInInterceptor logging 信息: Inbound Message ---------------------------- Encoding: UTF-8 Headers: {cache-control=[no-cache], content-type=[text/xml; charset=UTF-8], connection=[keep-alive], host=[192.168.0.159:8080], content-length=[264], SOAPAction=[""], user-agent=[Apache CXF 2.4.1], Accept=[*/*], pragma=[no-cache]} Messages: Message: Payload: <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Header><authHeader><userId>admin</userId><password>admin</password></authHeader></soap:Header><soap:Body><ns2:getAllFoods xmlns:ns2="http://ws.cxf.fjava.org/"/></soap:Body></soap:Envelope> -------------------------------------- ----------------org.apache.cxf.binding.soap.SoapMessage@de912969 headers[org.apache.cxf.binding.soap.SoapHeader@608d8ce4] userIdscom.sun.org.apache.xerces.internal.dom.DeepNodeListImpl@6d1cfe41 passwordscom.sun.org.apache.xerces.internal.dom.DeepNodeListImpl@a78955e
[b]客户端运行结果:[/b]
十月 27, 2016 10:43:31 下午 org.apache.cxf.service.factory.ReflectionServiceFactoryBean buildServiceFromWSDL 信息: Creating Service {http://impl.ws.cxf.fjava.org/}HelloWorldWs from WSDL: http://192.168.0.159:8080/CXF_Spring/cxf-Spring/service/getAllFoods?wsdl 十月 27, 2016 10:43:31 下午 org.apache.cxf.interceptor.AbstractLoggingInterceptor log 信息: Outbound Message --------------------------- ID: 1 Address: http://192.168.0.159:8080/CXF_Spring/cxf-Spring/service/getAllFoods Encoding: UTF-8 Content-Type: text/xml Headers: {Accept=[*/*], SOAPAction=[""]} Payload: <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Header><authHeader><userId>admin</userId><password>admin</password></authHeader></soap:Header><soap:Body><ns2:getAllFoods xmlns:ns2="http://ws.cxf.fjava.org/"/></soap:Body></soap:Envelope> -------------------------------------- 蟹王汉堡 橙色,亮金色,我的宝贝,我的爱! 海绵金币 吃着金币样的甜甜饼,想着海绵宝宝赚的钱被扣了,哈哈哈,爽气! 一个汉堡 是三层的,有夹层哦! 火腿肠 这是章鱼哥从岸上偷运来的,据说很美味!
[b]做一个权限控制,有用户名和密码的时候才允许调用 Web Service 该功能完成!
[/b]
希望对你有帮助,祝你有一个好心情,加油!
若有错误、不全、可优化的点,欢迎纠正与补充;转载请注明出处!
若有疑问请下方留言。
相关文章推荐
- CXF-10: CXF 与 Spring 的整合 之 使用别人暴露的 Web Service 接口
- CXF学习-与Spring整合(暴露web Service接口给其他程序使用)
- 简单WEB Service接口实现(CXF与spring整合)
- CXF学习-与Spring结合:使用其他程序提供的web service 接口
- spring整合cxf方式发布的rest webservice,调用接口
- Spring boot 整合CXF开发web service示例
- Spring整合CXF 发布webservice接口服务器(普通及REST)和客户端,WSDL简单解析
- Web Service (四) 手动发布Web Service接口-CXF与Spring集成
- 基于 Spring 整合 CXF 的 Web Service 超时时间配置
- 10.Web Service Spring整合CXF之添加拦截器 完结
- Spring boot 整合CXF开发web service
- Web Service学习-CXF与Spring整合为JavaEE应用发布WebService(三)
- spring与cxf整合开发webservice服务接口
- Web Service的CXF实现(Spring整合方式)
- Web Service学习-CXF与Spring整合为JavaEE应用发布WebService(三)
- Spring boot 整合CXF开发web service
- CXF [web service]整合spring 总结
- Spring boot 整合CXF开发web service
- springboot的junit测试和多个模块cxf暴露接口问题
- 使用cxf发布rest服务接口,和spring的整合