您的位置:首页 > 编程语言 > Java开发

Web Service ---- CXF + SPRING 开发 SOAP服务

2016-09-21 17:34 609 查看

项目目录视图:



依赖架包:



源码:

WS 接口: HelloService.java

package service;

import javax.jws.WebService;

@WebService
public interface HelloService {

String say(String name);
}


WS 接口实现类:HelloServiceImpl.java

package service;

import javax.jws.WebService;

@WebService
public class HelloServiceImpl implements HelloService {

public String say(String name) {
return "hello " + name;
}
}


JaxWsServer 类来发布 WS: JaxWsServer.java

package service;

import org.apache.cxf.frontend.ServerFactoryBean;
import org.apache.cxf.jaxws.JaxWsServerFactoryBean;

public class JaxWsServer {

public static void main(String[] args) {
//ServerFactoryBean factory = new ServerFactoryBean();
JaxWsServerFactoryBean factory = new JaxWsServerFactoryBean();
factory.setAddress("http://localhost:8080/ws/soap/hello");
factory.setServiceClass(HelloService.class);
factory.setServiceBean(new HelloServiceImpl());
factory.create();
System.out.println("soap ws is published");
}
}


运行 JaxWsServer 类

浏览器访问:http://localhost:8080/ws/soap/hello?wsdl, 输出如下内容,则部署成功。



客户端访问WS:

方案一: CXF静态代理客户端

import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;

public class JaxWsClient {

public static void main(String[] args) {
JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
factory.setAddress("http://localhost:8080/ws/soap/hello");
factory.setServiceClass(HelloService.class);

HelloService helloService = factory.create(HelloService.class);
String result = helloService.say("world");
System.out.println(result);
}
}


方案二: 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 = JaxWsDynamicClientFactory.newInstance();
Client client = factory.createClient("http://localhost:8080/ws/soap/hello?wsdl");

try {
Object[] results = client.invoke("say", "world");
System.out.println(results[0]);
} catch (Exception e) {
e.printStackTrace();
}
}
}


方案三:通用动态代理客户端

import org.apache.cxf.endpoint.Client;
import org.apache.cxf.endpoint.dynamic.DynamicClientFactory;

public class DynamicClient {

public static void main(String[] args) {
DynamicClientFactory factory = DynamicClientFactory.newInstance();
Client client = factory.createClient("http://localhost:8080/ws/soap/hello?wsdl");

try {
Object[] results = client.invoke("say", "world");
System.out.println(results[0]);
} catch (Exception e) {
e.printStackTrace();
}
}
}


方案四:基于 CXF simple 方式的客户端

import org.apache.cxf.frontend.ClientProxyFactoryBean;

public class SimpleClient {

public static void main(String[] args) {
ClientProxyFactoryBean factory = new ClientProxyFactoryBean();
factory.setAddress("http://localhost:8080/ws/soap/hello");
factory.setServiceClass(HelloService.class);
HelloService helloService = factory.create(HelloService.class);
String result = helloService.say("world");
System.out.println(result);
}
}


方案五:基于 Spring 的客户端

方法一:使用 JaxWsProxyFactoryBean

<?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-4.0.xsd"> 
<bean id="factoryBean" class="org.apache.cxf.jaxws.JaxWsProxyFactoryBean">
<property name="serviceClass" value="demo.ws.soap_spring_cxf.HelloService"/>
<property name="address" value="http://localhost:8080/ws/soap/hello"/>
</bean>

<bean id="helloService" factory-bean="factoryBean" factory-method="create"/>

</beans>


方法二:使用 jaxws:client(推荐)

<?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-4.0.xsd http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd"> 
<jaxws:client id="helloService"
serviceClass="demo.ws.soap_spring_cxf.HelloService"
address="http://localhost:8080/ws/soap/hello"/>

</beans>


客户端代码

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Client {

public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-client.xml");

HelloService helloService = context.getBean("helloService", HelloService.class);
String result = helloService.say("world");
System.out.println(result);
}
}


WEB容器中部署 Spirng + CXF 开发的WS服务

配置 web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0">

<!-- Spring -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<!-- 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>
<!-- 所有带有 /ws 前缀的请求,将会交给被 CXFServlet 进行处理,也就是处理 WS 请求了。目前主要使用了 Spring IOC 的特性,利用了 ContextLoaderListener 加载 Spring 配置文件,即这里定义的 spring.xml 文件。 -->
<url-pattern>/ws/*</url-pattern>
</servlet-mapping>

</web-app>


配置spring.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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="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="demo.ws"/>

<import resource="spring-cxf.xml"/>

</beans>


以上配置做了两件事情:

  1、定义 IOC 容器扫描路径,即这里定义的 demo.ws,在这个包下面(包括所有子包)凡是带有 Component 的类都会扫描到 Spring IOC 容器中。

  2、 引入 spring-cxf.xml 文件,用于编写 CXF 相关配置。将配置文件分离,是一种很好的开发方式。

配置 spring-cxf.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-4.0.xsd http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd"> 
<jaxws:server id="helloService" address="/soap/hello">
<jaxws:serviceBean>
<ref bean="helloServiceImpl"/>
</jaxws:serviceBean>
</jaxws:server>

</beans>


通过 CXF 提供的 Spring 命名空间,即 jaxws:server,来发布 WS。其中,最重要的是 address 属性,以及通过 jaxws:serviceBean 配置的 Spring Bean。

可见,在 Spring 中集成 CXF 比想象的更加简单,此外,还有一种更简单的配置方法,那就是使用 CXF 提供的 endpoint 方式,配置如下:

<?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-4.0.xsd http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd"> 
<jaxws:endpoint id="helloService" implementor="#helloServiceImpl" address="/soap/hello"/>

</beans>


使用 jaxws:endpoint 可以简化 WS 发布的配置,与 jaxws:server 相比,确实是一种进步。

注意:这里的 implementor 属性值是 #helloServiceImpl,这是 CXF 特有的简写方式,并非是 Spring 的规范,意思是通过 Spring 的 Bean ID 获取 Bean 实例。

同样,也可以在 Spring 中使用 simple 方式来发布 WS,配置如下:

<?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:simple="http://cxf.apache.org/simple"
xsi:schemaLocation="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="helloService" serviceClass="#helloService" address="/soap/hello">
<simple:serviceBean>
<ref bean="#helloServiceImpl"/>
</simple:serviceBean>
</simple:server>

</beans>


可见,simple:server 与 jaxws:server 的配置方式类似,都需要配置一个 serviceBean。

比较以上这三种方式,我个人更加喜欢第二种,也就是 endpoint 方式,因为它够简单!

至于为什么 CXF 要提供如此之多的 WS 发布方式?我个人认为,CXF 为了满足广大开发者的喜好,也是为了向前兼容,所以这些方案全部保留下来了。

启动 Tomcat

将应用部署到 Tomcat 中,在浏览器中http://localhost:8080/ws



通过以上过程,可以看出 CXF 完全具备 RI 的易用性,并且与 Spring 有很好的可集成性,而且配置也非常简单。

同样通过这个地址可以查看 WSDL:http://localhost:8080/ws/soap/hello?wsdl

注意:紧接在 /ws 前缀后面的 /soap/hello,其实是在 address=”/soap/hello” 中配置的。

现在已经成功地通过 CXF 对外发布了 WS,下面要做的事情就是用 WS 客户端来调用这些 endpoint 了。

您可以不再使用 JDK 内置的 WS 客户端,也不必通过 WSDL 打客户端 jar 包,因为 CXF 已经为您提供了多种 WS 客户端解决方案,根据您的口味自行选择吧!

Cxf工具

java2ws

通过该工具我们可以根据SEI(Service Endpoint Implementation的缩写)类及其相关的类来生成WebService的服务端、客户端和wsdl文件等。SEI需要是使用了@WebService标注的Service接口或类。

在Cxf根目录下的bin目录下有一个java2ws工具,我们可以在命令行下使用该指令生成相应代码。Java2ws指令后面可以接很多参数,主要有:

  -?|-h|-help : 这三个参数都可输出java2ws指令的帮助信息

  -wsdl : 生成wsdl文件

  -o fileName : 指定生成的wsdl文件的名称

  -d resourceDir : 指定资源文件存放的目录,生成的wsdl文件也会放在该目录下

  -server : 指定生成服务端代码

  -client : 指定生成客户端代码

  -wrapperbean: 指定生成wrapper和fault bean

  -cp classpath : 指定java2ws搜寻SEI和相关类型类的路径

  -t targetNameSpace : 指定targetNameSpace

  -servicename serviceName : 指定serviceName

  -verbose :在生成代码的时候显示注释信息

  -quiet :在生成代码的时候不显示注释信息

  -s sourceDir :指定生成的java代码存放的目录

  -classdir classdir :指定生成的java代码编译后的class类存放的目录

  className :指定SEI类的名称

在使用java2ws指令时className是必须给定的,其他的参数都可选。java2ws将会到classpath路径下或使用-cp参数指定的classpath路径下寻找SEI类及其相关类型。

示例:

  java2ws –server com.tiantian.cxftest.sample.jaxws.HelloWorld(生成服务端代码)

  java2ws –wsdl –cp . com.tiantian.cxftest.sample.jaxws.HelloWorld(在当前目录下寻找HelloWorld类生成wsdl文件)

Maven使用java2ws

Cxf为java2ws工具提供了一个Maven的插件,叫cxf-java2ws-plugin。所以当我们的项目是使用Maven的时候我们可以在项目的pom.xml文件中加入Cxf提供的cxf-java2ws-plugin,这样当我们执行Maven的某个操作的时候就会触发java2ws指令生成对应的代码。如:

<plugin>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-java2ws-plugin</artifactId>
<version>${cxf.version}</version>
<executions>
<execution>
<id>process-classes</id>
<phase>process-classes</phase>
<configuration>
<className>com.tiantian.cxftest.sample.jaxws.HelloWorld</className>
<genWsdl>true</genWsdl>
<verbose>true</verbose>
</configuration>
<goals>
<goal>java2ws</goal>
</goals>
</execution>
</executions>
</plugin>


使用cxf-java2ws-plugin插件生成的Java代码默认会放在项目的根目录下。

对于java2ws指令的参数可以通过configuration元素下的元素来指定。可以配置的参数如下:

  className:指定SEI类的名称

  classpath:搜选SEI类等相关类的类路径,默认情况下会到当前项目的类路径下寻找

  outputFile:生成的wsdl文件存放的位置,默认是project.build.directory/generated/wsdl/{className}.wsdl

  genWsdl:默认为true。是否生成wsdl文件。

  genServer:默认false。是否生成Server端代码。

  genClient:默认为false。是否生成Client端代码

  genWrapperbean:默认为false。是否生成wrapper bean

  frontend:表明使用的frontend。支持jaxws和simple,默认是jaxws。

  databinding:指定数据绑定。默认frontend为jaxws时用jaxb,simple用aegis

  serviceName:指定serviceName

  soap12:指定生成的wsdl文件包含soap1.2绑定

  targetNameSpace:指定用来生成wsdl文件时的target namespace

  verbose:生成代码的过程中显示注释信息

  quiet:在生成代码的时候不显示注释信息

  attachWsdl:当为true时,在对当前项目进行Maven的install操作时会把生成的wsdl文件以当前项目的groupId、artifactId和version,以type为wsdl一起安装到Maven的repository中去。默认为true

  address:指定port的address

wsdl2java工具

wsdl2java是CXF自带的工具,在CXF根目录下的bin目录下。我们可以在命令行使用这一工具。就可以通过wsdl文件生成对应的Java代码,包括WebService的Service类定义、服务端代码、客户端代码。wsdl2java命令后可以接很多参数,主要有:

  -?|-h|-help:这三个参数都可以查看wsdl2java指令的帮助信息,可以查看wsdl2java可以带哪些参数

  -p packageName:指定生成的java类使用的包名称,如未指定将根据wsdl文件里面的targetNameSpace来决定。

  -d dir:生成的Java文件的存放在dir目录

  -compile:编译生成的Java文件

  -classdir dir:指定编译生成的文件存放在dir目录下

  -server:生成WebService的服务端发布代码

  -client:生成WebService的客户端访问代码

  -impl:生成Service类的实现类代码,即上面的HelloWorldImpl

  -all:生成所有可以生成的代码

  wsdlurl:Wsdl文件的访问路径,可以是本地file,也可以是web上的请求。

示例如:wsdl2java –all http://localhost:8080/test/services/HelloWorld?wsdl

wsdl2java在生成Service实现类的时候只会生成基本的代码结构,至于里面的操作的具体逻辑还需要我们自己来实现。

cxf-codegen-plugin

cxf-codegen-plugin是cxf针对于maven的一个插件。当我们的项目是基于Maven的项目时,我们可以在pom.xml文件中引入cxf-codegen-plugin,如:

<plugin>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-codegen-plugin</artifactId>
<version>2.7.6</version>
<executions>
<execution>
<id>generate-sources</id>
<phase>generate-sources</phase>
<goals>
<goal>wsdl2java</goal>
</goals>
<configuration>
<sourceRoot>${project.build.directory}/generated-sources/cxf</sourceRoot>
<wsdlOptions>
<wsdlOption>
<wsdl>${basedir}/src/main/resources/HelloWorld.wsdl</wsdl>
</wsdlOption>
</wsdlOptions>
</configuration>
</execution>
</executions>
</plugin>


在上面例子中,当我们执行mvn generate-sources时就会运行wsdl2java这个指令。其配置信息是通过configuration元素来指定的。通过sourceRoot可以指定生成的Java代码的存放位置,上面我们指定了生成代码的存放位置为target目录下的generated-sources/cxf目录,该目录也是在没有指定sourceRoot时,cxf-codegen-plugin存放生成的Java代码的默认位置。每个wsdlOption元素对应于一个用来生成代码的WSDL定义。wsdl元素用于指定wsdl文件的存放位置。wsdl2java指令的其他参数可以通过:

<plugin>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-codegen-plugin</artifactId>
<version>2.7.6</version>
<executions>
<execution>
<id>generate-sources</id>
<phase>generate-sources</phase>
<goals>
<goal>wsdl2java</goal>
</goals>
<configuration>
<wsdlOptions>
<wsdlOption>
<wsdl>${basedir}/src/main/resources/HelloWorld.wsdl</wsdl>
<extraargs>
<extraarg>-impl</extraarg>
<extraarg>-server</extraarg>
<extraarg>-p</extraarg><!-- 指定包名,后面紧跟着的extraarg为对应的值 -->
<extraarg>com.tiantian</extraarg>
</extraargs>
</wsdlOption>
</wsdlOptions>
</configuration>
</execution>
</executions>
</plugin>


每一个extraarg元素代表一个参数,如果使用extraarg元素指定wsdl2java参数后面还带有参数值时,参数值也用一个extraarg来表示

使用serviceName元素指定要生成Java代码的service

<configuration>
<wsdlOptions>
<wsdlOption>
<wsdl>${basedir}/src/main/resources/HelloWorld.wsdl</wsdl>
<!-- 指定要生成代码的service名称 -->
<serviceName>HelloWorldImplService</serviceName>
</wsdlOption>
</wsdlOptions>
</configuration>


使用wsdlRoot来指定wsdl文件存放的目录

configuration>
<wsdlRoot>${basedir}/src/main/resources/wsdl</wsdlRoot>
<!-- 包含 -->
<includes>
<include>*.wsdl</include>
</includes>
<!-- 不包含 -->
<excludes>
<exclude>null</exclude>
</excludes>
</configuration>


使用defaultOptions来指定公共的选项

当我们使用wsdlRoot元素来指定wsdl文件存放目录时,我们就不能再指定wsdl2java指令执行时的可选参数了。这个时候我们可以通过defaultOptions元素来指定。defaultOptions元素下指定的选项对所有的wsdlOption元素都会产生作用。如:

<configuration>
<defaultOptions>
<extraargs>
<extraarg>-all</extraarg>
</extraargs>
</defaultOptions>
<wsdlRoot>${basedir}/src/main/resources/wsdl</wsdlRoot>
<includes>
<include>helloWorld.wsdl</include>
</includes>
</configuration>
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  spring web service soap cxf