CXF Frontends的使用

概览


CXF frontends 是一组编程的API,被用来开发和发布webservice。CXF支持两种类型的frontend--JAX-WS和简单frontend。这章将对JAX-WS frontend提供更加详细的解释。我们同样也会展示如何使用简单frontend API来构建一个webservice。这个章节将会关注使用如下两种frontend来开发基于SOAP的web服务:

  • JAX-WS frontend
  • 简单 frontend

关于JAX-WS frontend,我们将说明如下内容:

  • 使用编码优先的开发方式来开发webservice
  • 使用契约优先的开发方式来开发webservice
  • 构建一个动态客户端或消费者
  • 基于Provider and Dispatch的实现
  • 理解webservice上下文

JAX-WS frontend


CXF支持由Java Community Process(JCP)提供的JAX-WS 2.0 API 规范。JAX-WS是一个由JCP制订的正式规范,它定义了API用来构建,开发和部署webservice。CXF提供了它自己的JAX-WS实现来附加在JAX-WS标准规范上。CXF JAX-WS frontend为构建不同种类的webservice提供了不公的API。抛开提供标准的基于WSDL的开发不讲,它还在Provider and Dispatch接口的形式中提供了用于构建基于XML服务的API。

有两种方式来开发基于SOAP的JAX-WS webservice-编码优先契约优先。正如名字显示的那样,在编码优先的开发模式中,你将从编码开始,然后将它转化成WSDL。编码优先的开发方式适用于:在实现方法的输入和输出的对象格式较简单时,并且你想要将它们快速地暴露为webservice的时候。编码优先的开发方式是更简单的,因为你将从Java对象开始,而不必考虑如何生成WSDL和XSD,而这个过程会在Java对象无法被按照你的意愿映射到XML元素的时候导致一些问题。注意,像CXF Java2WSDL 这样基于实现方法的输入输出格式的工具,将会生成契约,包括为你生成XSD格式。比如,你不能暴露一个Map或者Collection作为一个输出信息格式,因为没有标准的做法将它映射到XML schema,而这将会导致交互上的问题。

契约优先的开发方式中,开发者从一个已经存在的WSDL artifact来构建webservice。契约优先的开发方式适用于:当你已经有了一个XML schema为webservice的操作定义了输入输出消息的格式的时候,或者想要更好的控制XML如何被映射到Java对象的时候。契约优先的开发方式需要你很精通XSD和WSDL契约,因为你将使用这些契约开始创建你的model。如果你在创建基于工业标准的服务,你或许需要从契约优先的开发方式开始,因为工业消息的格式通常是XML schema的。

如果两种方式你都熟悉,并且知道对象是如何映射到XML的,你可以使用编码优先的开发方式。CXF不仅支持这些开发方式,而且为多种数据绑定机制提供了支持,这些机制将帮助你把Java对象映射到XML。

我们将从编码优先的开发方式开始。

编码优先的开发方式


在这个部分,我们将以开发一个Java类,并且通过注解它将它转化成一个service类开始。你将遵循如下的步骤完成webservice的开发:

  • 创建Service Endpoint Interface(SEI)
  • 添加Java注解
  • 发布服务
  • 开发一个消费者
  • 运行编码优先的例子

创建Service Endpoint Interface(SEI)

Service Endpoint Interface是一个Java接口,它定义了一个被用来暴露为服务的业务方法。这个服务方法被一个服务类实现。一个SEI可以通过两种不同的方式来构建:

  • 从零构建一个SEI组件
  • 将存在的业务功能转化成基于服务的组件

第一个方式--从零构建一个SEI组件的方式是指,开发一个全新的webservice而不依赖于任何已经存在的代码或WSDL契约。它推荐你从写服务接口开始,然后创建这个服务的实现类。写服务接口总是好的实践,因为它为你的服务方法提供了一个适当的客户端视图。之后,实现类可以实现定义在接口中的方法。

第二个方式是指,拿到已经存在的业务功能,然后把它转化成基于服务的组件。大多数时候,你已经开发过了业务逻辑,你只是想把它们作为服务方法暴露出去。你可以通过如下的方式来完成这个过程:开发一个SEI,并只定义那些你想要暴露成为一个服务方法的业务方法,然后使已经存在的Java代码实现那个SEI。另一个常用的方式是:创建包装后的SEI和一个实现类,这个实现类可以使用已经存在的实现类来完成自己的功能。

我们将从零创建一个SEI组件开始。我们开发一个OrderProcessSEI并且实现它。下面的代码显示了这个OrderProcessSEI:

package demo.order;
public interface OrderProcess {
    String processOrder(Order order);
}

你可以看到,上面的代码是一个简单的POJO接口。它定义了一个抽象方法processOrder,它接收一个Orderbean作为一个参数。OrderProcessImpl实现类实现了processOrder方法,并且这个方法将在之后作为一个webservice方法暴露出来。

我们接下来将实现这个接口的业务逻辑。你将要写这个OrderProcessImpl来实现OrderProcessSEI。下面的代码显示这个OrderProcessImpl类:

package demo.order;
public class OrderProcessImpl implements OrderProcess {
    public String processOrder(Order order) {
        System.out.println("Processing order...");
        String orderID = validate(order);
        return orderID;
    }
}

上面的代码是一个简单的POJO实现类,它实现类processOrder方法。这个方法简单地校验了订单,并返回一个唯一的订单ID。简单起见,我们返回了一个静态的订单ID作为我们实现的一部分。在下一个部分,我们将通过注解它们的方式把SEI和实现类转化成为webservice组件。

添加Java注解

Webservice注解被添加到一个Java类用来将其暴露成为一个服务组件。JAX-WS使用Java 5注解,它是Web Services Metadata为Java Platform规范(JSR-181)提供的,用来将一个组件转化成为一个webservice。这些注解简单地标记,被用来为一个特定的组件或者方法定义一个特定上下文。每一个注解被一个或多个上下文的属性所支持。在这个部分,我们将向我们的OrderProcessSEI和实现类添加注解,并将它们转化成一个服务组件。在这个部分,我们将覆盖如下的webservice注解:

  • javax.jws.WebService
  • javax.jws.soap.SOAPBinding
javax.jws.WebService

一个Java组件可以通过添加一个@WebService注解的方式被转化成为一个服务。这个注解必须被同时定义在SEI和实现类中。@WebService注解被定义在javax.jws.WebService接口。

@WebService注解支持如下的属性:

@WebService注解支持的属性

让我们注解我们的OrderProcessSEI和OrderProcessImpl实现类。下面的代码展示了@WebService注解的使用:

package demo.order;
import javax.jws.WebService;
@WebService
public interface OrderProcess {
    String processOrder(Order order);
}

@WebService被直接声明在接口或者类之上。它注解了这个类或者接口作为一个webservice的类或者接口。在上面的代码中,OrderProcess接口通过@WebService注解,来被定义成为一个webservice接口。

让我们看一下OrderProcessImpl实现类。下面的代码展示了被注解的OrderProcessImpl实现类:

package demo.order;
import javax.jws.WebService;
@WebService(serviceName="OrderProcessService", 
    portName="OrderProcessPort")
public class OrderProcessImpl implements OrderProcess {
    public String processOrder(Order order) {
        System.out.println("Processing order...");
        String orderID = validate(order);
        return orderID;
    }
}

和SEI类似,你在类之上声明@WebService。你将定义两个属性--serviceNameportNameserviceName属性被赋值为OrderProcessService。这个服务名称被消费者用来获取远程接口的句柄来调用服务方法。端口名称签名了endpoint名称。服务的endpoint也同样作为一个服务端口在服务发布的地方被调用。这里的名字是OrderProcessPort

有许多其它可选的注解被用来和@WebService搭配使用,用来更加完整地描述一个webservice。其它注解可以添加更精细的细节到一个服务上。总是推荐你使用这些注解来描述你的webservice,以便于生成的WSDL文档有更多的被这些注解指定的细节。如果你没有使用这些可选的注解,那么WSDL将被按照默认的规则生成,正如前面表格中讨论的一样。

javax.jws.soap.SOAPBinding

@SOAPBinding注解被定义在javax.jws.soap.SOAPBinding接口中。这个注解在你想要为你的服务制定SOAP绑定的时候使用。

这个注解支持如下的属性:

@SOAPBinding注解支持的属性

SOAP绑定在webservice交互中扮演着重要的角色。让我们更加详细地看看两种风格的SOAP绑定。

RPC 风格 v.s Document 风格

Webservice的SOAP交互风格在服务提供者和消费者之间交互SOAP XML消息中扮演着重要的角色。有两种SOAP消息风格--DocumentRPC。SOAP消息的风格被作为SOAP绑定定义在WSDL文档中。一个SOAP绑定能够拥有一个基于编码的使用(encoded use) 或者 基于字面的使用(encoded use or a literal use)编码,这个词暗示了消息将使用某种格式被编码,而字面的表示使用纯文本消息,而不做任何编码逻辑。

正如名字显示的那样,Document风格将XML文档作为有效的载荷来添加到定义良好的契约中,通常的做法是使用XML schema定义来创建。XML schema格式指定了被消费者调用的服务消息的契约。XML schema定义了服务提供者和服务消费者之间的request和response消息的格式。这些消息的格式可以被服务的提供者和消费者验证为合法。Document literal风格对webservice交互来完成协作来说是更受欢迎的方式。

另一方面,RPC(Remote Procedure Call)风格表明了SOAP主体包含一个XML的方法展示。为了序列化方法的参数到SOAP消息,以便于它能够被任何webservice实现反序列化。SOAP规范定义了一系列的编码规则。由于RPC通常被用在连接SOAP的编码规则,这个连接将被引用为RPC/encoded。你用养有一种RPC/literal交互风格模式,这种模式中你没有任何的编码格式,但是消息依然受限于RPC 基于方法的交互,这种方式下,消息不能被验证为合法的,因为他们与任何XML schema定义之间都没有关联。你或许应该避免开发RPC风格的webservice,因为它们有许多协作性上的问题。

下面的代码展示了@SOAPBinding注解的使用:

@WebService(name="OrderProcess")
@SOAPBinding(parameterStyle=ParameterStyle.BARE)
public interface OrderProcess {
    String processOrder(Order order);
}
javax.jws.WebMethod

@WebMethod注解被定义在javax.jws.WebMethod接口中。这个注解被用来自定义webservice的操作。@WebMethod注解提供了operation的nameaction属性,这两个属性分别被用来自定义在WSDL文档中的<wsdl:operation>元素的name属性和<soap:operation>元素的soapAction属性。@WebMethod注解被放置在服务方法声明的上面。

@WebMethod注解支持如下的属性:

@WebMethod注解支持的属性

下面的代码片段展示了@WebMethod注解的使用:

@WebMethod (name="processOrder")
public String processOrder(Order order) {
    // ...
}

JAX-WS webservice注解支持一系列的其它注解,比如:@RequestWrapper, @ResponseWrapper, @Oneway等等。它们中的一些将稍后解释。

发布服务

发布服务意味着将服务组件注册到服务器上,并且通过endpoint URL使其可以被消费者使用。你将把OrderProcesswebservice发布到一个特定的endpoint URL上。在本例中的endpoint URL将会是http://localhost:8080/OrderProcess。你将开发一个服务器组件来发布你的OrderProcess服务。对这个例子来说,我们将使用Java 5提供的轻量级的web服务器来发布我们的服务。CXF提供了它自己独立的服务器组件--JaxWsServerFactoryBean来发布webservice。

下面的代码展示了服务器代码:

import javax.xml.ws.Endpoint;
public class Server {
    protected Server() throws Exception {
        System.out.println("Starting Server ...");
        OrderProcessImpl orderProcessImpl = new OrderProcessImpl();
        String address = "http://localhost:8080/OrderProcess";
        Endpoint.publish(address, orderProcessImpl);
    }   
    public static void main(String[] args) {
        new Server();
        Thread.sleep(50000);
        System.exit(0);
    } 
}

用于发布Endpoint类的静态方法提供了一个简便的方式来发布和测试JAX-WS webservice。这个方法接受endpoint URL和一个OrderProcessImpl类对象作为参数。这个publish方法在URL http://localhost:8080/OrderProcess上创建一个轻量级的web服务器,并且在那个位置部署这个服务。这个轻量级的web服务器在JVM中运行一分钟然后自动终止。一种能够看到服务的WSDL契约的方法是在web浏览器中访问这个URL:

http://localhost:8080/OrderProcess?wsdl

开发一个消费者

webservice的消费者调用服务方法来获得需要的结果。在这一部分,我们将开发一个Client类来查找我们的OrderProcess服务,并且调用它的processOrder方法。下面的代码展示了服务消费者组件:

public class Client {
    private static final QName SERVICE_NAME =
        new QName("http://order.demo/", "OrderProcessService");
    private static final QName PORT_NAME =
        new QName("http://order.demo/", "OrderProcessPort");
    private static final String WSDL_LOCATION =
        "http://localhost:8080/OrderProcess?wsdl";
    
    public static void main(String args[]) throws Exception {
        URL wsdlURL = new URL(WSDL_LOCATION);
        Service service = Service.create(wsdlURL, SERVICE_NAME);
        OrderProcess port = service.getPort(PORT_NAME, OrderProcess.class);
        Order order = new Order();
        order.setCustomerID("C001");
        order.setItemID("I001");
        order.setPrice(100.00);
        order.setQty(20);
        String result = port.processOrder(order);
        System.out.println("The order ID is " + result);
    }
}

客户端的代码做了以下的事情:

  1. 它首先创建了WSDL URL。这个WSDL URL是http://localhost:8080/OrderProcess?wsdl。这个URL表明了WSDL 文档的位置。

在运行客户端程序之前,你可以通过访问以上的URL来验证这个服务是否可用。如果你能够看见WSDL,那么就意味着这个OrderProcess服务被成功地发布了。

  1. 接下来创建了Service对象。这个Service对象使用了静态的create 方法被创建。这个方法接受WSDL URL和服务名称作为参数。这个服务名称OrderProcessService是一个QName,并且在WSDL文档中被映射到<wsdl:service>元素。<wsdl:service>元素定义了服务的endpoint。
  2. 使用Service对象,你通过调用getPort方法获得SEI句柄的代理组件。getPort方法接受port name和SEI类作为参数。接口名称OrderProcessPort是一个QName,并且在WSDL文档中被映射到<wsdl:port>元素。这个SEI类是OrderProcess
  3. 代理组件在接下来被用来调用服务方法processOrder。在调用方法之前,你必须安置好Orderbean,并把它传给processOrder方法。这个方法在服务器上被调用,并返回订单ID。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,425评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,058评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,186评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,848评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,249评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,554评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,830评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,536评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,239评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,505评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,004评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,346评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,999评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,060评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,821评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,574评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,480评论 2 267

推荐阅读更多精彩内容