基于CXF Java 搭建Web Service

一,关于cxf 框架的运行时序的个人思考过程:

    1.Tomcat启动时,加载web.xml,根据web.xml的配置,把CXF 的Servlet 放到Tomcat的侦听端口的映射中,同时也把Spring的Bean加载器也加载到Tomcat的进程空间中,Spring根据初始化的配置文件(比如beans.xml),加载Bean对象。在加载过程中,会根据配置文件中的beans.xml中的xmlns进行分析,调用对应的加载器进行解释加载,这里调用cxf的加载器进行加载。cxf加载器会根据 beans.xml中对应的项加载最终实现的class文件,这些class在对应的java源文件编译过程中,根据java文件中的annomation标记符进行处理,使得这些class加载时形成正确的path 与对象的映射。

    2.客户端发出请求,通过XML/HTTP把请求及参数对象转为XML经过HTTP传到Servlet容器中。

    3.CXF会根据Path与对象的映射找到正确的对象,如果是Restful Web Service还会再根据映射找到Path中对应的执行方法。

二,创建一个基于CXF及Spring的SOAP Web Service:

    1.创建Web Service 相关的类:

       因为这种类型Web Service是SOA(面象服务架构)的,所以是提供一个远程的RPC接口。所以首先要有一个接口,当然,在服务端要提供真正的服务,所以要有一个这个接口在服务端的实现。下面分别实现:

IHelloWorld.java: 

package com.services.soap;

import javax.jws.WebParam;

import javax.jws.WebService;

@WebService

public interface IHelloWorld {

        public String speakoutUserInfo(@WebParam(name = "param") ParamDTO obj);

}

HelloWorld.java:

package com.services.soap;

import javax.jws.WebService;

/**  *   * 使用@WebService指向Interface定义类即可.  */

@WebService(endpointInterface = "com.services.soap.IHelloWorld")

public class HelloWorld implements IHelloWorld{

@Override

public String speakoutUserInfo(ParamDTO obj) {

  // TODO Auto-generated method stub

        return "hello"; 

}

}

上述的服务实现用到一个对象,这个对象可以做为参数远程进行传递,一般叫做DTO(数据传输对象)。当然你可以不用对象,用普通的数据类型这个实例一次性都表现一下。

ParamDTO.java:

package com.services.soap;

import javax.xml.bind.annotation.XmlAccessorType;

import javax.xml.bind.annotation.XmlAccessType;

import javax.xml.bind.annotation.XmlType;

/** 

* Web Service传输信息的DTO. 

* 分离entity类与web service接口间的耦合,隔绝entity类的修改对接口的影响. 使用JAXB 2.0的annotation标注JAVA-XML映射,尽量使用默认约定.  *  

*/

@XmlAccessorType(XmlAccessType.FIELD) @XmlType(name = "User")

public class ParamDTO  {

        protected Integer id;

        protected String name;

         public Integer getId()      {

              return id;

        }

        public void setId(Integer value)      {

              id = value;

       }

        public String getName()      {

              return name;

      }

        public void setName(String value)      {

             name = value;

      }

}

2.在配置文件中体映射这个Service:

我们定义这个Beans.xml

<?xml version="1.0" encoding="UTF-8"?> 
<beans xmlns="http://www.springframework.org/schema/beans" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:jaxws="http://cxf.apache.org/jaxws" 
    xsi:schemaLocation=" 
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd  
http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd">   
    <jaxws:endpoint id="webServiceHelloWorld" 
        address="/HelloWorld" implementor="com.services.soap.HelloWorld"/> 
</beans>

这个Beans.xml放到Spring的加载Beans的配置文件中被引用:

applicationContext.xml:

<?xml version="1.0" encoding="UTF-8"?> 
<beans 
    xmlns="http://www.springframework.org/schema/beans" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"> 
    <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" /> 
    <import resource="classpath:beans.xml" />  //注意这行代码的引用
   
</beans>

当然我们要在Web.xml中配置Spring:

<?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" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
  <display-name>webrest</display-name>
 
    <context-param> 
        <param-name>contextConfigLocation</param-name> 
        <param-value>WEB-INF/classes/applicationContext.xml</param-value> 
    </context-param> 
    <listener> 
        <listener-class> 
            org.springframework.web.context.ContextLoaderListener 
        </listener-class> 
    </listener> 
    <servlet> 
        <servlet-name>CXFServlet</servlet-name> 
        <servlet-class> 
            org.apache.cxf.transport.servlet.CXFServlet 
        </servlet-class> 
    </servlet> 
    <servlet-mapping> 
        <servlet-name>CXFServlet</servlet-name> 
        <url-pattern>/*</url-pattern> 
    </servlet-mapping> 
 
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
  </welcome-file-list>
</web-app>

三,创建一个基于CXF及Spring的Restful Web Service:

  这个就相对简单了。因为经不需要直接接供RPC接口给客户端,只是其于ROA的方式提供资源的操作,可以理解为基于一些xml,json的表达一些资源对象变态变化的传输同步给远程服务。

  所以通过xml映射对象,Annomation进行直接映射方法与path.所以直接写实现类就行了,当然cxf还有别的框架有其它的映射或配置方式。

a.代码实现:

  先上代码:

HelloWorld.java

package com.services.rest;

import javax.ws.rs.Consumes;

import javax.ws.rs.GET;

import javax.ws.rs.POST;

import javax.ws.rs.Path;

import javax.ws.rs.PathParam;

import javax.ws.rs.Produces;

import javax.ws.rs.core.Response;

import org.codehaus.jackson.jaxrs.JacksonJsonProvider; 

@Path("/hello")

public class HelloWorld {

     @GET

     @Path("/echo/{input}")

     @Produces("text/plain")

     public String ping(@PathParam("input") String input) {

         return input + ":in server!";

     }

     @POST

     @Produces("application/json")

     @Consumes("application/json")

     @Path("/jsonBean")

     public Response modifyJson(JsonBean input) {

            input.setCommand(222);

            input.getParam().put("content", "welcome to server!");

           return Response.ok().entity(input).build();

     }

}

其中用到JsonBean对象这个是可以远程传送参数对象,一般情况无需特别的定义。就可以直接用了。我这里定义如下:

JsonBean:

package com.services.rest;

import java.util.Map;

public class JsonBean {

private Integer command;

private Integer protocolVersion;

private String  platformType;

private Map<String, Object> param;

public Integer getCommand() {

   return command;

}

public void setCommand(Integer command) {

     this.command = command;

}

public Integer getProtocolVersion() {

     return protocolVersion;

}

public void setProtocolVersion(Integer protocolVersion) {

     this.protocolVersion = protocolVersion;

}

public String getPlatformType() {

     return platformType;

}

public void setPlatformType(String platformType) {

    this.platformType = platformType;

}

public Map<String, Object> getParam() {

     return param;

}

public void setParam(Map<String, Object> param) {

     this.param = param;

}

}

b.进行发布:

配置一个rest-services.xml进行映射:

<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans"   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   xmlns:jaxrs="http://cxf.apache.org/jaxrs"   xmlns:context="http://www.springframework.org/schema/context"   xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://cxf.apache.org/jaxrs http://cxf.apache.org/schemas/jaxrs.xsd">

  <import resource="classpath:META-INF/cxf/cxf.xml" />

  <context:property-placeholder/>  
  <context:annotation-config/>  
  <bean class="org.springframework.web.context.support.ServletContextPropertyPlaceholderConfigurer"/>  
  <bean class="org.springframework.beans.factory.config.PreferencesPlaceholderConfigurer"/>

   <jaxrs:server id="services" address="/">          
     <jaxrs:serviceBeans>    
        <bean class="com.services.rest.HelloWorld" />         
     </jaxrs:serviceBeans>    
     <jaxrs:providers>        
        <bean class="org.codehaus.jackson.jaxrs.JacksonJsonProvider"/>     
     </jaxrs:providers>    
  </jaxrs:server>

</beans>

在Spring的加载配置文件(applicationContext.xml)里引入:

<?xml version="1.0" encoding="UTF-8"?> 
<beans 
    xmlns="http://www.springframework.org/schema/beans" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"> 
    <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" /> 
    <import resource="classpath:rest-services.xml" />
</beans>

OK,大功告成。

到时此我们能把这两种模式的Web Service同时在一个框架里发布吗?当然可以:)要做的只有一步,就是在上面的applicationContext.xml里同时加载两个Service的映射文件就可以了。

<?xml version="1.0" encoding="UTF-8"?> 
<beans 
    xmlns="http://www.springframework.org/schema/beans" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"> 
    <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" /> 

    <import resource="classpath:beans.xml" />
    <import resource="classpath:rest-services.xml" />
</beans>

现在就你就可以编译完成,Publish到你的tomcat上进行测试了,不过一定要注意,在发布选项里一定要把你项目工程中引用的jar依赖库(比如,cxf相关,spring相关的,Json相关的)同时发布到你的Tomcat Server的运行环境里,这里只需要修改:项目(MyServices)右键=》Properties=>Deployment Assembly=>Add=>Java Build Path Entries 不过在引入的jar过多时可能会造成冲突,假如在测试时,说CXF 的一个Discoveryxxx对象..... Null Point之类的错误:

SEVERE: Context initialization failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'bookservice': Invocation of init method failed; nested exception is org.apache.cxf.service.factory.ServiceConstructionException
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1422)
        .......

        Caused by: org.apache.cxf.service.factory.ServiceConstructionException
        at org.apache.cxf.jaxrs.JAXRSServerFactoryBean.create(JAXRSServerFactoryBean.java:201)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
              ...

Caused by: java.lang.NullPointerException
        at org.apache.cxf.ws.discovery.listeners.WSDiscoveryServerListener.startServer(WSDiscoveryServerListener.java:64)
        at org.apache.cxf.bus.managers.ServerLifeCycleManagerImpl.startServer(ServerLifeCycleManagerImpl.java:61)
        at org.apache.cxf.endpoint.ServerImpl.start(ServerImpl.java:146)
        at org.apache.cxf.jaxrs.JAXRSServerFactoryBean.create(JAXRSServerFactoryBean.java:192)
          ........

就是最常见的cxf-services-ws-discovery-service-2.x.x.jar冲突,去掉这个.jar的依赖即可。如果你在项目的Java Build Path中去掉这个jar仍不行,就去你测试的Tocat Server上右键“clean" 然后再"Publish",如果这样还不行,说明是Eclipse 清除Tomcat的发布目录不彻底(Eclipse也有很多bug的),你就去Tomcat 的运行时临时Web根目录中去清除这个jar.这个目录是在Eclipse的Workspace目录下的”.metadata\.plugins\org.eclipse.wst.server.core\tmp0\wtpwebapps“子目录。现在估计你能理解为什么你在Eclipse 的runtime server中用Tomcat测试发布后的文件在Tomcat的安装目录看不到的原因了吧?呵呵,因为Eclipse整合tomcat测试运行时,根本上会使用自己的临时目录作为Tomcat的运行时Web根目录。

如果你遇到:

Caused by: java.lang.NullPointerException

Class Could not found: org.springframework.web.context.ContextLoaderListener 之类的错误。你需要在你的Web project的Deployment Assemblly 中 加入Java build path的库,即点击"Add"按钮,在弹出列表窗口中选择“Java Build Path Entries"然后选中你的工程发布所需要的库即可。

到这里应该完成了。



你可能感兴趣的:(web Service)