俗话说“好记性不如烂笔头”,编程的海洋如此的浩大,养成做笔记的习惯是成功的一步!
此笔记主要是CXF2.4.2版本的笔记,并且笔记都是博主自己一字一字编写和记录,有错误的地方欢迎大家指正。
1、说明:
<1.1>CXF的功能是提供了用于方便地构建和开发 Web 服务的可靠基础架构,使Web Service开发的简单框架。
<1.2>所需要导入的jar包:CXF的核心jar包是cxf-2.4.2.jar,但所依赖的jar包非常多,需要把lib下的所有58个jar包全部导入。
<1.3>当CXF与Spring框架整合时,还需要额外的导入Spring的jar包。
<1.4>如果不是绑定tomcat服务器来运行,只是使用普通的方法运行时,CXF会调用自带的jetty服务器进行启用来提供服务,
反之,会直接使用tomcat来提供web Service服务。
<1.5>使用CXF也还需要用到注解@WebService,表示此类事SebService服务类,@WebMethod(exclude=true)表示某个方法不给远程共享。但本地可以调用。
<1.6>所谓本地,指在统一进程中运行,如同一个项目中调用。
而远程,是指在不同进程间,如Tomcat6.0访问Tomcat7.0的资源,因为Tom6与Tom7不是两个Tom各自运行,故是属于远程调用。
<1.7>对外提供的方法必须是public的,且不能使static 和 final类型的。
<1.8>使用WebService时,如果是不同的语言间相互调用,参数可以是java的集合(List/Set/Map),但是返回参数不能是java的集合(List/Set/Map),否则客户端无法使用,可以手动转换成json格式的字符串。
<1.9>使用jdk6.0的wsimport的命令解析WebSerice的WSDL后,如果参数是个对象,那么对象的属性不能直接使用基本类型,而是使用JAXBElement对象进行包装:
JAXBElement<String> str=new JAXBElement<String>(new QName("http://model.ihkmsyf.hfhh.cn","houseName"),String.class,"abc");
http://model.ihkmsyf.hfhh.cn是通过解析后的WSDL的封装对象里查看到的命名空间,houseName是名称,这两个参数必须一致才能进行传递到服务端。
2、在普通的java Project中发布web Service的方式(必须要在WebService服务类上加注解@WebService):
//创建jaxws对象。jaxws:Jaca And Xml Web Service
JaxWsServerFactoryBean factory=new JaxWsServerFactoryBean();
//设置地址。访问时必须是http://localhost:8888/say?wsdl,否则会报错
String address="http://localhost:8888/say";
factory.setAddress(address);
//设置服务类
factory.setServiceBean(new CxfWS());
//设置访问日志,默认是输出到控制台。
factory.getInInterceptors().add(new LoggingInInterceptor());
//设置返回给客户端的信息记录日志,默认是输出到控制台。
factory.getOutInterceptors().add(new LoggingOutInterceptor());
//启动CXF内置的jetty服务器。此时将不再使用JDK自带的mini服务器
factory.create();
<2.1> 注意,访问时因为http://localhost:8888/say并没有任何数据,因此CXF会报错,只能直接访问WSDL文档:http://localhost:8888/say?wsdl
3、CXF与Spring框架的整合。
<3.1>配置spring.xml:
增加头文件内容:
xmlns:jaxws="http://cxf.apache.org/jaxws"
"http://cxf.apache.org/jaxws
http://cxf.apache.org/schemas/jaxws.xsd"
完整的spring.xml配置文件:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:jaxws="http://cxf.apache.org/jaxws"
xsi:schemaLocation="
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
http://cxf.apache.org/jaxws
http://cxf.apache.org/schemas/jaxws.xsd">
<!-- 将webservice服务类,注册到springIOC容器中 -->
<bean id="CxfWSID" class="cn.itcast.cxf.CxfWS"/>
<!--
将webservice服务类,暴露给远程客户端,以便调用
与传统的main()方法类似
目的是:发布webservice服务到指定的URL上
address:表示发布的URL地址,但这里不需要http://,只要服务名即可
bean:表示真正提供服务的类
-->
<jaxws:server address="/cat">
<jaxws:serviceBean>
<ref bean="CxfWSID"/>
</jaxws:serviceBean>
</jaxws:server>
<3.2>如果是使用Servlet,因为Servlet并不是有Spring来维护,则无法通过IOC进行注入服务类,则
需要在Servlet中获取ApplicationContext,直接通过getBeans()来获取WebService的服务类。
获取方法:
ApplicationContext ac=WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());
<3.3>如果是使用Struts2,则可以直接使用IOC来注入WebService的服务类。
<3.4>配置web.xml文件
<!-- 配置org.apache.cxf.transport.servlet.CXFServlet类
作用在于:将服务类,转成WSDL描述文件
注意路径:/myservice/*,值必须访问项目名/myservice,才会得到一个链接WSDL的页面
-->
<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>/myservice</url-pattern>
</servlet-mapping>
<3.5注意事项>
(1)CXFServlet的<url-pattern>/myservice/*</url-pattern>,是指必须要访问到http:localhost:8080/项目名/myservice,
才会显示一个链接WSDL的页面。
(2)address="/cat",表示当通过http:localhost:8080/项目名/myservice显示的链接WSDL的页面地址是
http:localhost:8080/cat?WSDL。
(3)如果Web项目也整合了Struts2框架,则必须修改Struts2拦截器的拦截地址,因为struts2的拦截地址是/*,默认拦截所有请求,导致
原本访问CXFServlet的/myservice也被拦截了,拦截器会找这个路径下的Action,导致最后找不到Action而引起报错。
解决方式有两种:
方式1(不建议使用).修改Struts2的拦截地址:
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/struts2/*</url-pattern>
</filter-mapping>
方式2.自定义一个过滤器Filter,且必须在struts2前声明,判断请求地址如果是/myservice,则转发到CXFServlet。
4、cxf与Spring的客户端整合方式(在Spring的配置文件xml中增加配置):
<!-- 配置ws的客户端 -->
<jaxws:client id="cardImgreconClient"
<!--提供服务的服务端地址-->
address="http://10.71.9.10:8080/cxfServerX/ImgReconCard"
<!--提供服务类的接口,必须与服务端的接口一致-->
serviceClass="com.wintone.webservice.CardImgrecon"/>
5、在使用cxf与Spring的客户端整合方式时,要求接口类的包路径必须一样,否则会抛出异常:
org.apache.cxf.interceptor.Fault: Unexpected wrapper element {****}
同时,最好保持类名的一致性,还有方法的一致性,以便出现其他错误。
6、凡是提供webservice服务的接口与实现类,都必须在类名或接口名上增加@WebService 的注解,表示是提供ws服务的。
注意,无论是客户端的接口类还是服务端的接口类,都必须添加该注解。
7、cxf的服务端与spring整合时,如果想让cxf的部分模块对象也交给spring管理,可以引入相应的cxf的xml配置文件:
(1)让spring管理 cxf的核心组件、 SOAP binding、servlet transport,则配置:
<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" />
(2)让spring管理 cxf的所有模块对象,则配置(不建议用此方式,因为如果没有使用cxf的全部功能,却生成全部模块的对象,浪费资源):
<import resource="classpath:META-INF/cxf/cxf.xml" />
<import resource="classpath*:META-INF/cxf/cxf-extension-*.xml" />
或者配置为:
<import resource="classpath:META-INF/cxf/cxf.xml" />
说明:META-INF/cxf/cxf.xml等这些配置文件在cxf.jar包中,只需要导入cxf.jar到项目中,不需要再拷贝对应的文件到自己的项目META-INF下。
上面的import并不是必须的,如果不引入相应的配置文件,则cxf自己管理模块对象,依旧可以使用cxf提供的ws服务。
如果项目中有使用spring,建议cxf的核心模块组件对象交由spring统一管理。