CXF相关的文章看了很多,但是这篇是讲解的最清楚的!
原文链接:http://my.oschina.net/huangyong/blog/286439#OSC_h1_3
以下是文章的部分内容,仅供备忘!
在 Web 容器中使用 Spring + CXF 发布 WS
Tomcat + Spring + CXF,这个场景应该更加接近我们的实际工作情况,开发过程也是非常自然。
第一步:配置 Maven 依赖
<project xmlns&#61;"http://maven.apache.org/POM/4.0.0" xmlns:xsi&#61;"http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation&#61;"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0modelVersion> <groupId>demo.wsgroupId> <artifactId>soap_spring_cxfartifactId> <version>1.0-SNAPSHOTversion> <packaging>warpackaging> <properties> <project.build.sourceEncoding>UTF-8project.build.sourceEncoding> <spring.version>4.0.5.RELEASEspring.version> <cxf.version>3.0.0cxf.version> properties> <dependencies> <dependency> <groupId>org.springframeworkgroupId> <artifactId>spring-contextartifactId> <version>${spring.version}version> dependency> <dependency> <groupId>org.springframeworkgroupId> <artifactId>spring-webartifactId> <version>${spring.version}version> dependency> <dependency> <groupId>org.apache.cxfgroupId> <artifactId>cxf-rt-frontend-jaxwsartifactId> <version>${cxf.version}version> dependency> <dependency> <groupId>org.apache.cxfgroupId> <artifactId>cxf-rt-transports-httpartifactId> <version>${cxf.version}version> dependency> dependencies> project>
第二步&#xff1a;写一个 WS 接口及其实现
接口部分&#xff1a;
package demo.ws.soap_spring_cxf;import javax.jws.WebService;&#64;WebService
public interface HelloService { String say(String name); }
实现部分&#xff1a;
package demo.ws.soap_spring_cxf;import javax.jws.WebService;
import org.springframework.stereotype.Component;&#64;WebService
&#64;Component public class HelloServiceImpl implements HelloService { public String say(String name) { return "hello " &#43; name; } }
需要在实现类上添加 Spring 的 org.springframework.stereotype.Component
注解&#xff0c;这样才能被 Spring IOC 容器扫描到&#xff0c;认为它是一个 Spring Bean&#xff0c;可以根据 Bean ID&#xff08;这里是 helloServiceImpl&#xff09;来获取 Bean 实例。
第三步&#xff1a;配置 web.xml
<web-app xmlns&#61;"http://java.sun.com/xml/ns/javaee" xmlns:xsi&#61;"http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation&#61;"http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version&#61;"3.0"> <context-param> <param-name>contextConfigLocationparam-name> <param-value>classpath:spring.xmlparam-value> context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListenerlistener-class> listener> <servlet> <servlet-name>cxfservlet-name> <servlet-class>org.apache.cxf.transport.servlet.CXFServletservlet-class> servlet> <servlet-mapping> <servlet-name>cxfservlet-name> <url-pattern>/ws/*url-pattern> servlet-mapping> web-app>
所有带有 /ws
前缀的请求&#xff0c;将会交给被 CXFServlet
进行处理&#xff0c;也就是处理 WS 请求了。目前主要使用了 Spring IOC 的特性&#xff0c;利用了 ContextLoaderListener
加载 Spring 配置文件&#xff0c;即这里定义的 spring.xml 文件。
第四步&#xff1a;配置 Spring
配置 spring.xml&#xff1a;
<beans xmlns&#61;"http://www.springframework.org/schema/beans" xmlns:xsi&#61;"http://www.w3.org/2001/XMLSchema-instance" xmlns:context&#61;"http://www.springframework.org/schema/context" xsi:schemaLocation&#61;"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd"> <context:component-scan base-package&#61;"demo.ws"/> <import resource&#61;"spring-cxf.xml"/> beans>
以上配置做了两件事情&#xff1a;
- 定义 IOC 容器扫描路径&#xff0c;即这里定义的
demo.ws
&#xff0c;在这个包下面&#xff08;包括所有子包&#xff09;凡是带有Component
的类都会扫描到 Spring IOC 容器中。 - 引入
spring-cxf.xml
文件&#xff0c;用于编写 CXF 相关配置。将配置文件分离&#xff0c;是一种很好的开发方式。
第五步&#xff1a;配置 CXF
配置 spring-cxf.xml&#xff1a;
<beans xmlns&#61;"http://www.springframework.org/schema/beans" xmlns:xsi&#61;"http://www.w3.org/2001/XMLSchema-instance" xmlns:jaxws&#61;"http://cxf.apache.org/jaxws" xsi:schemaLocation&#61;"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd"> <jaxws:server id&#61;"helloService" address&#61;"/soap/hello"> <jaxws:serviceBean> <ref bean&#61;"helloServiceImpl"/> jaxws:serviceBean> jaxws:server> beans>
通过 CXF 提供的 Spring 命名空间&#xff0c;即 jaxws:server
&#xff0c;来发布 WS。其中&#xff0c;最重要的是 address
属性&#xff0c;以及通过 jaxws:serviceBean
配置的 Spring Bean。
可见&#xff0c;在 Spring 中集成 CXF 比想象的更加简单&#xff0c;此外&#xff0c;还有一种更简单的配置方法&#xff0c;那就是使用 CXF 提供的 endpoint
方式&#xff0c;配置如下&#xff1a;
<beans xmlns&#61;"http://www.springframework.org/schema/beans" xmlns:xsi&#61;"http://www.w3.org/2001/XMLSchema-instance" xmlns:jaxws&#61;"http://cxf.apache.org/jaxws" xsi:schemaLocation&#61;"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd"> <jaxws:endpoint id&#61;"helloService" implementor&#61;"#helloServiceImpl" address&#61;"/soap/hello"/> beans>
使用 jaxws:endpoint
可以简化 WS 发布的配置&#xff0c;与 jaxws:server
相比&#xff0c;确实是一种进步。
注意&#xff1a;这里的 implementor
属性值是 #helloServiceImpl
&#xff0c;这是 CXF 特有的简写方式&#xff0c;并非是 Spring 的规范&#xff0c;意思是通过 Spring 的 Bean ID 获取 Bean 实例。
同样&#xff0c;也可以在 Spring 中使用 simple 方式来发布 WS&#xff0c;配置如下&#xff1a;
<beans xmlns&#61;"http://www.springframework.org/schema/beans" xmlns:xsi&#61;"http://www.w3.org/2001/XMLSchema-instance" xmlns:simple&#61;"http://cxf.apache.org/simple" xsi:schemaLocation&#61;"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://cxf.apache.org/simple http://cxf.apache.org/schemas/simple.xsd"> <simple:server id&#61;"helloService" serviceClass&#61;"#helloService" address&#61;"/soap/hello"> <simple:serviceBean> <ref bean&#61;"#helloServiceImpl"/> simple:serviceBean> simple:server> beans>
可见&#xff0c;simple:server
与 jaxws:server
的配置方式类似&#xff0c;都需要配置一个 serviceBean
。
比较以上这三种方式&#xff0c;我个人更加喜欢第二种&#xff0c;也就是 endpoint 方式&#xff0c;因为它够简单&#xff01;
至于为什么 CXF 要提供如此之多的 WS 发布方式&#xff1f;我个人认为&#xff0c;CXF 为了满足广大开发者的喜好&#xff0c;也是为了向前兼容&#xff0c;所以这些方案全部保留下来了。
第六步&#xff1a;启动 Tomcat
将应用部署到 Tomcat 中&#xff0c;在浏览器中输入以下地址可进入 CXF 控制台&#xff1a;
http://localhost:8080/ws
通过以上过程&#xff0c;可以看出 CXF 完全具备 RI 的易用性&#xff0c;并且与 Spring 有很好的可集成性&#xff0c;而且配置也非常简单。
同样通过这个地址可以查看 WSDL&#xff1a;
http://localhost:8080/ws/soap/hello?wsdl
注意&#xff1a;紧接在 /ws
前缀后面的 /soap/hello
&#xff0c;其实是在 address&#61;"/soap/hello"
中配置的。
现在已经成功地通过 CXF 对外发布了 WS&#xff0c;下面要做的事情就是用 WS 客户端来调用这些 endpoint 了。
您可以不再使用 JDK 内置的 WS 客户端&#xff0c;也不必通过 WSDL 打客户端 jar 包&#xff0c;因为 CXF 已经为您提供了多种 WS 客户端解决方案&#xff0c;根据您的口味自行选择吧&#xff01;
4. 关于 CXF 提供的 WS 客户端方案一&#xff1a;静态代理客户端
package demo.ws.soap_cxf;import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;public class JaxWsClient {public static void main(String[] args) { JaxWsProxyFactoryBean factory &#61; new JaxWsProxyFactoryBean(); factory.setAddress("http://localhost:8080/ws/soap/hello"); factory.setServiceClass(HelloService.class); HelloService helloService &#61; factory.create(HelloService.class); String result &#61; helloService.say("world"); System.out.println(result); } }
这种方案需要自行通过 WSDL 打客户端 jar 包&#xff0c;通过静态代理的方式来调用 WS。这种做法最为原始&#xff0c;下面的方案更有特色。
方案二&#xff1a;动态代理客户端
package demo.ws.soap_cxf;import org.apache.cxf.endpoint.Client;
import org.apache.cxf.jaxws.endpoint.dynamic.JaxWsDynamicClientFactory;public class JaxWsDynamicClient { public static void main(String[] args) { JaxWsDynamicClientFactory factory &#61; JaxWsDynamicClientFactory.newInstance(); Client client &#61; factory.createClient("http://localhost:8080/ws/soap/hello?wsdl"); try { Object[] results &#61; client.invoke("say", "world"); System.out.println(results[0]); } catch (Exception e) { e.printStackTrace(); } } }
这种方案无需通过 WSDL 打客户端 jar 包&#xff0c;底层实际上通过 JDK 的动态代理特性完成的&#xff0c;CXF 实际上做了一个简单的封装。与 JDK 动态客户端不一样的是&#xff0c;此时无需使用 HelloService 接口&#xff0c;可以说是货真价实的 WS 动态客户端。
方案三&#xff1a;通用动态代理客户端
package demo.ws.soap_cxf;import org.apache.cxf.endpoint.Client;
import org.apache.cxf.endpoint.dynamic.DynamicClientFactory;public class DynamicClient { public static void main(String[] args) { DynamicClientFactory factory &#61; DynamicClientFactory.newInstance(); Client client &#61; factory.createClient("http://localhost:8080/ws/soap/hello?wsdl"); try { Object[] results &#61; client.invoke("say", "world"); System.out.println(results[0]); } catch (Exception e) { e.printStackTrace(); } } }
这种方案与“方案三”类似&#xff0c;但不同的是&#xff0c;它不仅用于调用 JAX-WS 方式发布的 WS&#xff0c;也用于使用 simple 方式发布的 WS&#xff0c;更加智能了。
方案四&#xff1a;基于 CXF simple 方式的客户端
package demo.ws.soap_cxf;import org.apache.cxf.frontend.ClientProxyFactoryBean;public class SimpleClient {public static void main(String[] args) { ClientProxyFactoryBean factory &#61; new ClientProxyFactoryBean(); factory.setAddress("http://localhost:8080/ws/soap/hello"); factory.setServiceClass(HelloService.class); HelloService helloService &#61; factory.create(HelloService.class); String result &#61; helloService.say("world"); System.out.println(result); } }
这种方式仅用于调用 simple 方式发布的 WS&#xff0c;不能调用 JAX-WS 方式发布的 WS&#xff0c;这是需要注意的。
方案五&#xff1a;基于 Spring 的客户端
方法一&#xff1a;使用 JaxWsProxyFactoryBean
<beans xmlns&#61;"http://www.springframework.org/schema/beans" xmlns:xsi&#61;"http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation&#61;"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd"> <bean id&#61;"factoryBean" class&#61;"org.apache.cxf.jaxws.JaxWsProxyFactoryBean"> <property name&#61;"serviceClass" value&#61;"demo.ws.soap_spring_cxf.HelloService"/> <property name&#61;"address" value&#61;"http://localhost:8080/ws/soap/hello"/> bean> <bean id&#61;"helloService" factory-bean&#61;"factoryBean" factory-method&#61;"create"/> beans>
方法二&#xff1a;使用 jaxws:client&#xff08;推荐&#xff09;
<beans xmlns&#61;"http://www.springframework.org/schema/beans" xmlns:xsi&#61;"http://www.w3.org/2001/XMLSchema-instance" xmlns:jaxws&#61;"http://cxf.apache.org/jaxws" xsi:schemaLocation&#61;"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd"> <jaxws:client id&#61;"helloService" serviceClass&#61;"demo.ws.soap_spring_cxf.HelloService" address&#61;"http://localhost:8080/ws/soap/hello"/> beans>
客户端代码&#xff1a;
package demo.ws.soap_spring_cxf;import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class Client { public static void main(String[] args) { ApplicationContext context &#61; new ClassPathXmlApplicationContext("spring-client.xml"); HelloService helloService &#61; context.getBean("helloService", HelloService.class); String result &#61; helloService.say("world"); System.out.println(result); } }
谈不上那种方案更加优秀&#xff0c;建议根据您的实际场景选择最为合适的方案。
Spring &#43; CXF 这对搭档&#xff0c;让发布 WS 更加简单&#xff0c;只需以下四个步骤&#xff1a;
- 配置 web.xml
- 编写 WS 接口及其实现
- 配置 CXF 的 endpoint
- 启动 Web 容器