首页 > 设计 > WEB开发 > 正文

cxf

2019-11-02 18:24:59
字体:
来源:转载
供稿:网友

1.   CXF

1.1.  WS常见框架和CXF概述

历史:

Axis:-->Axis2

XFire:-->(WebService框架)

Celtrix:(ESB框架)

CXF(XFire+Celtrix)

优点:

CXF号称是SOA框架,我们做WS只会用到XFire。

CXF内置Jetty Web服务器。

使用CXF开发WebServer端组件都需要“接口”和“实现类”两部分。

支持多种数据格式:xml和JSON(Restful)。

并可以与SPRing进行快速无缝的整合

灵 活的 部 署 : ant(build.xml)  maven(pom.xml)

可 以运 行 有Tomcat,Jboss,Jetty(内 置web 服 务器),IBMWebsphere,BeaWebLogic上面。

 

官网地址:http://cxf.apache.org/

为了方便使用CXF下的工具,把CXF/bin配置到PATH里去。当然最好先配置CXF_HOmE,然后在Path里面直接引用。

 

常用jar库文件:

除了jetty服务器相关的类外,有7个必须的包:

asm是字节码相关的包;

logging是日志相关的包;

cxf是框架核心包;

geronimo-servlet是servlet相关包;

neethi是网络安全相关包;

wsdl4j是wsdl解析用的包;

xmlschema是schema相关的包。

1.2.  wsdl2java工具

在cxf中,也提供了一个用于生成客户端调用代码的工具。它的功能就如同wsimport一样。

先让我们了解一下cxf的wsdl2java工具,可以生成一堆客户端调用的代码。

此工具位于cxf_home/bin目录下。参数与wsimport有所不同。

它主要包含以下参数:

-d参数,指定代码生成的目录。

-p参数,指定生成的新的包结构。

需要说明的是,由于wsdl2java是根据jdk1.7生成的本地代码,所以,需要对生成的代码做一点点修改。

Wsdl2java  -d . -p cn.itsource.ws.cxf.aahttp://localhost:9998/hi?wsdl

1、注意:由于使用的是apache-cxf较新版本,所以支持jdk1.7的。对于生成的Service要进行稍微的修改。

在jdk1.6中 的javax.xml.ws.Service的 构 造 方 法 接 收 二个 参 数 为:(URLurl,QName qname);

在jdk1.7中的javax.xml.ws.Service的构造方法中接 收三个参数为:(URLurl,QName qname,WebServiceFeature... features);

2、将生成的代码,拷贝到项目目录,然后使用以前相同方法调用。

注意:如果你对@WebMethod设置了header=true参数,将会在调用时多传递一个参数。它参数可以直接传null值。对于这种情况,可以将header=true修改成header=false然后再重要获取客户端源代码。

3、大家可能发现了,使用cxf生成的客户端代码与wsimport差不多,甚至是一样,

那为什么还要使用cxf呢?它较好的发布方式、较容易的与其他服务器集成、及与Spring的完美结合都不得不让我们使用cxf。

1.3.  ServerFactoryBean和ClientProxyFactoryBean方式

此时服务类和接口,都可以不使用@WebService标注。(因为这套标准在JDK1.6之前就有了)

//服务端发布

1.创建ServerFactoryBean的对象,用于发布服务

2.设置服务发布地址

3.设置服务发布的接口

4.设置服务的发布对象

5.使用create方法发布服务

      JaxWsServerFactoryBeanbean=newJaxWsServerFactoryBean();

      // 2.设置服务发布地址

      bean.setAddress("http://localhost:9998/hi");

      // 3.设置服务发布的接口

      bean.setServiceClass(IHelloService.class);//接口设置ServiceClass

      // 4.设置服务的发布对象

      bean.setServiceBean(new HelloServiceImpl());

      //============拦截器==========//

      bean.getInInterceptors().add(newLoggingInInterceptor());

      bean.getInInterceptors().add(new AuthInInterceptor());

      //============拦截器==========//

      // 5.使用create方法发布服务

      bean.create();

      System.out.println("服务发布成功!");

 

 

 

 

 

其中设置接口实现类或设置服务类对象任意一个都可以。

//客户端:

1.创建ClientProxyFactoryBean的对象,用于接收服务

2.设置服务的发布地址,表示去哪里过去服务

3.设置服务的发布接口,使用本地的代理接口

4.通过create方法返回接口代理实例

5.调用远程方法

   JaxWsProxyFactoryBeanbean=newJaxWsProxyFactoryBean();

      // 2.设置服务的发布地址,表示去哪里过去服务

      bean.setAddress("http://192.168.1.254:9999/hi");//不能加?wsdl

      // 3.设置服务的发布接口,使用本地的代理接口

      bean.setServiceClass(IHelloService.class);

      //===================拦截器=======================//

      bean.getOutInterceptors().add(newLoggingOutInterceptor()); //先获取框子,直接往里面放

      //自定义拦截器

      bean.getOutInterceptors().add(new AuthOutInterceptor("admin","1")); //先获取框子,直接往里面放

      //===================拦截器=======================//

      // 4.通过create方法返回接口代理实例

      IHelloServicehelloService= (IHelloService)bean.create();

      System.out.println(helloService.getClass().getName());

     

      System.out.println(helloService.sayHello("小猪"));

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

1.4.  JaxWsServerFactoryBean和JaxWsProxyFactoryBean方式

支持@WebService等注解

JaxWsServerFactoryBean是ServerFactoryBean的子类,也是功能扩展类。

但在CXF的API文档中没有提供此类API,请通过查看源代码的方式获取此类的帮助。此类,必须要在被发布为服务的类和接口上添加@WebService注解,如果不加注解,虽然不出错,但也不会对外暴露任何方法。使用此类生成的wsdl文件更加规范,建议使用该类。

服务端:

1.创建JaxWsServerFactoryBean的对象,用于发布服务

2.设置服务发布地址

3.设置服务发布的接口

4.设置服务的发布对象

5.使用create方法发布服务

这种方式发布必须使用接口。

客户端:

1.创建JaxWsProxyFactoryBean的对象,用于接收服务

2.设置服务的发布地址,表示去哪里过去服务

3.设置服务的发布接口,使用本地的代理接口

4.通过create方法返回接口代理实例

5.调用远程方法

1.5.  为CXF客户端和服务端添加拦截器

使用自带的日志拦截器:可以用来捕获soap的消息

LoggingInInterceptor–信息输入时的拦截器-请求

LoggingOutInterceptor–信息输出时的拦截器-响应

1.5.1.  服务端添加拦截器

    // 1.创建JaxWsServerFactoryBean的对象,用于发布服

      JaxWsServerFactoryBean bean = newJaxWsServerFactoryBean();

      // 2.设置服务发布地址

      bean.setAddress("http://localhost:9998/hi");

      // 3.设置服务发布的接口

      bean.setServiceClass(IHelloService.class);//接口设置ServiceClass

      // 4.设置服务的发布对象

      bean.setServiceBean(new HelloServiceImpl());

      //============拦截器==========//

      bean.getInInterceptors().add(new LoggingInInterceptor());

      bean.getInInterceptors().add(new AuthInInterceptor());

      //============拦截器==========//

      // 5.使用create方法发布服务

      bean.create();

      System.out.println("服务发布成功!")

1.5.2.  客户端添加拦截器

   // 1.创建JaxWsProxyFactoryBean的对象,用于接收服务

      JaxWsProxyFactoryBeanbean=newJaxWsProxyFactoryBean();

      // 2.设置服务的发布地址,表示去哪里过去服务

      bean.setAddress("http://localhost:9998/hi");//不能加?wsdl

      // 3.设置服务的发布接口,使用本地的代理接口

      bean.setServiceClass(IHelloService.class);

      //===================拦截器=======================//

      bean.getOutInterceptors().add(newLoggingOutInterceptor()); //先获取框子,直接往里面放

      //自定义拦截器

      bean.getOutInterceptors().add(new AuthOutInterceptor("admin","1")); //先获取框子,直接往里面放

      //===================拦截器=======================//

      // 4.通过create方法返回接口代理实例

      IHelloServicehelloService= (IHelloService)bean.create();

      System.out.println(helloService.getClass().getName());

     

      System.out.println(helloService.sayHello("小猪"));

1.5.3.  为CXF添加自定义拦截器完成权限控制、

场景就是做服务的访问权限判断

拦截器使用的步骤:

1、声明一个类,来充当拦截器。这个类需要继承或实现某个接口或基类。

2、完成拦截器的逻辑。

3、把拦截器配置到访问或者发布逻辑中。

 

自 定 义 拦 截 器 需 要 实 现Interceptor接 口 , 一 般 的 我 们 直 接 继承AbstractPhaseInterceptor类。

做一个权限控制例子。

要求客户端把请求发送到服务端的时候,服务端根据客户端发送的SAOP消息,从<Header>里取出登录信息,以做权限验证。那么应该在客户端添加一个自定义的输出拦截器,在服务端添加一个自定义的输入拦截器。

1.1.1     客户端添加权限拦截器

得在拦截器里拼装一个SOAP消息的Header片段,如下:

<soap:Header>

  <authHeader>

    <username>用户名</username>

    <passWord>密码</password>

  </authHeader>

</soap:Header>

AuthOutInterceptor类:

publicclass AuthOutInterceptor

                   extendsAbstractPhaseInterceptor<SoapMessage>{

  private Stringusername;

  private Stringpassword;

  publicAuthOutInterceptor(String username,String password) {

    //在哪个阶段拦截

    super(Phase.PREPARE_SEND);//准备发送SOAP消息的时候,启用拦截器,添加

    this.username=username;

    this.password=password;

  }

  public voidhandleMessage(SoapMessage msg) throws Fault {

    //使用工具创建dom对象

    Documentdoc = DOMUtils.createDocument();

    //使用DOM对象创建符合XML规范的子元素

    //创建头信息中认证信息的根

    Element root =doc.createElement("autherHeader");

    //创建头信息中认证信息的内容

    Element nameEle =doc.createElement("username");

    Element pwdEle =doc.createElement("password");

    nameEle.setTextContent(username);

    pwdEle.setTextContent(password);

    root.appendChild(nameEle);

    root.appendChild(pwdEle);

    Headerheader =new Header(new QName("sirius"),root);

    msg.getHeaders().add(header);

  }

}

客户端代码:

publicvoid testCxf() {

  JaxWsProxyFactoryBeanbean = new JaxWsProxyFactoryBean();

  //设置访问地址

  bean.setAddress("http://localhost:9527/hello");

  //设置服务接口,直接使用本项目中的接口

  bean.setServiceClass(IHelloKity.class);

  //通过create方法返回接口实例

  bean.getOutInterceptors().add(

  new AuthOutInterceptor("sirius","1314"));

  bean.getOutInterceptors().add(newLoggingOutInterceptor());

  IHelloKity hello =(IHelloKity) bean.create();

  String ret =hello.sayHi("sirius", 18);

  System.out.println(ret);

}

添加拦截后SOAP内容如下:

信息:Outbound Message

---------------------------

ID:1

Address:http://localhost:9527/hello

Encoding:UTF-8

Content-Type:text/xml

Headers:{Accept=[*/*], SOAPAction=[""]}

Payload:

<soap:Envelopexmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">

<soap:Header>

<autherHeader>

<username>sirius</username>

<password>1314</password>

</autherHeader>

</soap:Header>

<soap:Body>

<ns2:sayHixmlns:ns2="http://cxf.ws.itc

<arg0>sirius</arg0>

<arg1>18</arg1>

</ns2:sayHi>

</soap:Body>

</soap:Envelope>

1.5.4.  服务端添加权限验证拦截器

服务端从SOAP中去取Header消息,若取到,再根据以下格式解析XML,得到username和password数据。

<soap:Header>

<authHeader>

<username>用户名</username>

<password>密码</password>

</authHeader>

</soap:Header>

AuthInInterceptor类:

publicclass AuthInInterceptor

            extendsAbstractPhaseInterceptor<SoapMessage> {

publicAuthInInterceptor() {

  super(Phase.PRE_INVOKE);

  System.out.println("构造器");

}

publicvoid handleMessage(SoapMessage msg) throws Fault {

  List<Header>headers = msg.getHeaders();

  if (headers == null|| headers.isEmpty()) {

    throw new Fault(newIllegalArgumentException("没有header"));

  }

  Header header=headers.get(0);

//在客户端的过滤器里,把element放入了header中,所以这里可以

  Element root =(Element) header.getObject();

  String username =

         root.getElementsByTagName("username").item(0).getTextContent();

  String password =

         root.getElementsByTagName("password").item(0).getTextContent();

  //查询数据库验证

  if (!"sirius".equals(username) || !"1314".equals(password)) {

    thrownewFault(newIllegalArgumentException("用户名或密码不对!"));

  }

  System.out.println("恭喜你可以开始了");

  }

}

服务端代码:

publicstatic void main(String[] args) {

  JaxWsServerFactoryBeanbean = new JaxWsServerFactoryBean();

  //绑定到发布地址的端口

  bean.setAddress("http://localhost:9527/hello");

  //设置服务接口,如果没有接口,则为本类

  bean.setServiceClass(IHelloKity.class);

  //设置服务类对象

  bean.setServiceBean(newHelloKityWS());

  //发布服务

  bean.getInInterceptors().add(newAuthInInterceptor());

  bean.create();

  System.out.println("启动OK");

}

 

 

package cn.itsource.ws.cxf.server;

 

import java.util.List;

 

import org.apache.cxf.binding.soap.SoapMessage;

import org.apache.cxf.headers.Header;

import org.apache.cxf.interceptor.Fault;

importorg.apache.cxf.phase.AbstractPhaseInterceptor;

import org.apache.cxf.phase.Phase;

import org.w3c.dom.Element;

 

public class AuthInInterceptor extendsAbstractPhaseInterceptor<SoapMessage> {

 

         publicAuthInInterceptor() {

                   super(Phase.PRE_INVOKE);//必须在执行之前,接收之后

                   //TODO Auto-generated constructor stub

         }

 

         @Override

         publicvoid handleMessage(SoapMessage soapMessage) throws Fault {

                  

                   //获取所有的头信息

                   List<Header>headers = soapMessage.getHeaders();

                   if(null == headers || headers.size()==0) {

                            thrownew Fault(new IllegalaccessException("请携带认证信息!"));

                   }

                 

                   //很确定认证头就是第一个

                   Headerheader = headers.get(0);

                   if(null == header) {

                            thrownew Fault(new IllegalAccessException("请携带认证信息!"));

                   }

                  

                   //获取AuthInfo节点

                   Elementele = (Element) header.getObject();

                  

                   StringuserName = ele.getElementsByTagName("userName").item(0).getTextContent();

                   Stringpassword =ele.getElementsByTagName("password").item(0).getTextContent();

                  

                   //可以使用用户名和密码到数据库中做验证,现在写死。

                   if("admin".equals(userName)&&"1".equals(password)) {

                            return;//直接返回就是放行

                   }

                   thrownew Fault(new IllegalAccessException("用户名和密码不正确!"));

         }

 

}

 

 

package cn.itsource.ws.cxf.client;

 

import javax.xml.namespace.QName;

 

import org.apache.cxf.binding.soap.SoapMessage;

import org.apache.cxf.headers.Header;

import org.apache.cxf.helpers.DOMUtils;

import org.apache.cxf.interceptor.Fault;

importorg.apache.cxf.phase.AbstractPhaseInterceptor;

import org.apache.cxf.phase.Phase;

import org.w3c.dom.Document;

import org.w3c.dom.Element;

 

/**

 *

 *@author Administrator

 *

 */

public class AuthOutInterceptor extendsAbstractPhaseInterceptor<SoapMessage> {

 

         privateString userName;

         privateString password;

         publicAuthOutInterceptor(String userName,String password) {

                   super(Phase.PREPARE_SEND);//设置拦截器的阶段

                   this.userName= userName;

                   this.password= password;

         }

 

         /**

          * 把权限认证的信息加到SOAP的Header里面

          * <soap:Header>

                   <authInfo>

                            <userName>admin</userName>

                            <password>1</password>

                   </authInfo>

                  

          </soap:Header>

          */

         @Override

         publicvoid handleMessage(SoapMessage message) throws Fault {

                   //第一步:通过工具类获取Document对象,就可以使用对象创建权限信息的节点

                   Documentdocument = DOMUtils.createDocument();

                   //第二步:创建权限信息的节点

                   ElementauthInfoEle = document.createElement("authInfo");

                  

                   ElementuserNameEle = document.createElement("userName");

                   userNameEle.setTextContent(userName);

                   ElementpasswordEle = document.createElement("password");

                   passwordEle.setTextContent(password);

                   authInfoEle.appendChild(userNameEle);

                   authInfoEle.appendChild(passwordEle);

                  

                   //localPart是一个唯一标识,可以随便写

                   QNameqName = new QName("xxxx");// <autoInfo></authInfo>

                   //QNameqName = new QName(namespaceURI, localPart)//<autoInfoxmlns="http://itsource.cn"></authInfo>

                   //QNameqName = new QName(namespaceURI, localPart, prefix)//<autoInfoxmlns:xxx="http://itsource.cn"></authInfo>

                  

                   //有两个参数:后面的一个为要添加节点,第一个为修改该节点的Namespace

                   Headerheader = new Header(qName , authInfoEle);

                   //第三步:把权限信息的节点添加到header里面

                   message.getHeaders().add(header);

         }

 

         publicString getUserName() {

                   returnuserName;

         }

 

         publicvoid setUserName(String userName) {

                   this.userName= userName;

         }

 

         publicString getPassword() {

                   returnpassword;

         }

 

         publicvoid setPassword(String password) {

                   this.password= password;

         }

 

        

}

 

 

 

 

 

1.6.  CXF和Spring集成

1.6.1.  服务端部分

使用CXF的最大好处就是能快速的Spring进行集成,现在在服务端来做一个和集成Spring应用。分为如下6步:

l  建立标准的Web工程结构;

l  拷贝CXF的核心jar包;

l  拷贝Spring的核心jar包;

l  在web.xml中配置cxf的核心servlet--CXFServlet;

l  在Spring文件中配置CXF核心bean;

l  服务器端发布服务;

 

1.6.1.1.           建立标准的Web工程结构

1.6.1.2.           拷贝CXF的核心jar包;

官方提供的lib里有75个包,但不是所有的我们都需要,在这里抽取一个mini版的CXF库,一共7个包。

1.6.1.3.           添加Spring相关配置

导入17spring依赖包:

在resource中添加:

applicationContext.xml

在web.xml中配置spring上下文:

<context-param>

       <param-name>contextConfigLocation</param-name>

       <param-value>classpath:applicationContext.xml</param-value>

</context-param>

 

<listener>

<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>

</listener>

在web.xml中配置cxf的核心servlet—CXFServlet

在web.xml中添加CXF核心servlet

       <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>/ws/*</url-pattern>

       </servlet-mapping>

 

1.6.1.4.           在Spring文件中配置CXF核心bean

通过查看前面配置的CXFServlet,可以看出,如果没有在工程WEB-INF下有个cxf-servlet.xml,在servlet代码执行时不会创建spring环境。

这时我们直接在applicationContext中手动集成CXF和Spring

<!-- cxf的核心bean -->

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

<!-- SOAP相关配置 -->

<importresource="classpath:META-INF/cxf/cxf-extension-soap.xml" />

1.6.1.5.           服务器端发布服务

服务端的发布分为两种情况:

       <!—1、简单发布(不支持接口,实现类不能直接实例化)-->

       <beanid="hellobean" class="test.HelloKityWSImpl"/>

       <jaxws:endpointid="hello" implementor="#hellobean" address="/hi"/>

 

       <!-- 2、通过接口的方式发布服务 -->

<beanid="hellobean" class="test.HelloKityWSImpl" />

       <jaxws:server id="hello"address="/hi" serviceClass="test.IHelloKity">

              <jaxws:serviceBean>

                     <refbean="hellobean" />

              </jaxws:serviceBean>

      <!-- 添加拦截器 -->

              <jaxws:inInterceptors>

                     <beanclass="org.apache.cxf.interceptor.LoggingInInterceptor" />

              </jaxws:inInterceptors>

              <jaxws:outInterceptors>

                     <beanclass="org.apache.cxf.interceptor.LoggingOutInterceptor" />

              </jaxws:outInterceptors>

       </jaxws:server>

第一种达到的效果和最初使用JDK中的Endpoint.publish效果相同,可以直接理解为这样配置,由Spring容器自动调用上面方法发布服务。这种发布不支持接口,应用不广。

第二种方式配置更为细腻,做到了接口和实现类的分离,配置灵活性更大

红色部分是拦截器配置,与之前代码中添加的拦截器效果相相同。

完成了上面配置,整个服务端服务就发布成功了。

 

1.6.2.  客户端部分

使用CXF的最大好处就是能快速的Spring进行集成,现在在客户端来做一个和集成Spring应用。分为如下5步:

l  建立标准的Web工程结构;

l  拷贝CXF的核心jar包;

l  拷贝Spring的核心jar包;

l  在Spring文件中配置服务代理接口;

l  客户端使用服务;

 

1.6.2.1.           建立标准的Web工程结构

1.6.2.2.           拷贝CXF的核心jar包;

官方提供的lib里有75个包,但不是所有的我们都需要,在这里抽取一个mini版的CXF库,一共7个包。

1.6.2.3.           添加Spring相关配置

导入17spring依赖包:

在resource中添加:

applicationContext.xml

在web.xml中配置spring上下文:

<context-param>

       <param-name>contextConfigLocation</param-name>

       <param-value>classpath:applicationContext.xml</param-value>

</context-param>

 

<listener>

<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>

</listener>

 

1.6.2.4.           在Spring文件中配置服务代理接口

在客户端先找到需要消费服务的WSDL文件地址,使用wsimport导入服务生成客户端代码,保留接口和接口所依赖的domain。其他都删掉。

这时我们直接在applicationContext中手动配置需要消费的接口

<!-- 客户端通过jaxws:client标签配置以后,就可以当成本地bean来进行使用 -->

<jaxws:client  id="hello"

 serviceClass="ws.client.test.IHelloKity" 

 wsdlLocation="http://localhost:8080/ws/hi?wsdl"/>

 

1.6.2.5.           客户端使用服务

在客户端的编写测试代码:

public class TestClient {

@Test

public void testCall() throws Exception {

    ApplicationContext ctx =

         newClassPathXmlApplicationContext("applicationContext.xml");

       IHelloKityservice = ctx.getBean("hello", IHelloKity.class);

       System.out.println("service: " +service.sayHi("sirius"));

}

}

得到打印结果:

service : sirius,你好!


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表