6、CXF与spring的整合(WebService的整合)

一、环境搭建

  • 这里只是初步整合,我们直接将CXF中的lib目录中的所有jar包全部加入(除去jetty相关包、geronimo-servlet_3.0_spec-1.0.jar)。
  • 将之前工程中的包拷贝到本工程(cxf_spring)中,
    分别是org.fkjava.cxf.ws.domain、org.fkjava.cxf.ws.service、org.fkjava.cxf.ws.service.impl、org.fkjava.cxf.ws、org.fkjava.cxf.ws.impl、org.fkjava.cxf.ws.util、org.fkjava.cxf.ws.server
  • 相关的配置文件

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/applicationContext.xml</param-value>
    </context-param>
    
    <!-- 使用一个监听器加载spring容器,保证web应用启动时加载spring -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    
    <!-- 下面的配置表明所有来自/myService/*的请求都交给CXFServlet处理 -->
    <servlet>
        <servlet-name>cxf</servlet-name>
        <!-- 下面这个类在API文档中是查不到的 -->
        <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>
</web-app>

说明:这里主要配置了spring的配置文件地址,spring监听器、CXF核心控制器。

applicationContext.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" 
         xmlns:aop="http://www.springframework.org/schema/aop"
         xmlns:tx="http://www.springframework.org/schema/tx"
         xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
           http://cxf.apache.org/jaxws
           http://cxf.apache.org/schemas/jaxws.xsd">
    <!-- web应用的类加载路径有两类:
    1.WEB-INF/classes目录。2.WEB-INF/lib目录下,两者的唯一区别是前者是散的class文件,后者是打成jar包的class文件
     -->
    <import resource="classpath:META-INF/cxf/cxf.xml"/>
        
    <bean id="userService" class="org.fkjava.cxf.impl.service.impl.UserServiceImpl"/>
</beans>

说明:

  • 这里我们需要将CXF相关的配置文件加入进去,同时配置相关的业务类。
  • spring配置文件(applicationContext.xml)中注入CXF提供的schema、xml配置。
    • 对于schema的配置:
      beans标签中加入xmlns:jaxws=http://cxf.apache.org/jaxws然后在xsi:schemaLocation中加入http://cxf.apache.org/jaxws(命名空间)http://cxf.apache.org/schemas/jaxws.xsd(物理路径)。
    • 对于xml的配置:
      web应用的类加载路径有两类:1.WEB-INF/classes目录。2.WEB-INF/lib目录下,两者的唯一区别是前者是散的class文件,后者是打成jar包的class文件。
      <import resource="classpath:META-INF/cxf/cxf.xml"/>
      其实导入的cxf-servlet.xmlcxf.extension-soap.xml一般不需要,在之后的版本中可以不需要导入了,一般只是为了兼容性才会导入。

二、进行开发

2.1 暴露WebService

spring的配置文件中使用jaxws:endpoint元素来暴露WebService,这里有两种方式:

<1>直接给定服务器提供者的类名(不太好用)

<jaxws:endpoint
        implementor="org.fkjava.cxf.ws.impl.HelloWorldWs"
        address="/fkjava">
</jaxws:endpoint>

然后我们就可以将工程部署到tomcat中,在浏览器中使用
地址http://localhost:8080/cxf_spring/myService访问我们的WebService工程。

1

  • 新建一个java工程进行测试(工程Call_CXFSpring
    首先还是使用命令:wsdl2java http://localhost:8080/cxf_spring/myService/fkjava?wsdl将相关文件考出来。之后刷新工程,在工程中新建一个类MyClient.java访问WebService即可。

MyClient.java

package org.fkjava.cxf.ws.client;
import org.apache.cxf.endpoint.Client;
import org.apache.cxf.frontend.ClientProxy;
import org.fkjava.cxf.ws.auth.AddHeaderInterceptor;
import org.fkjava.cxf.ws.Entry;
import org.fkjava.cxf.ws.HelloWorld;
import org.fkjava.cxf.ws.StringCat;
import org.fkjava.cxf.ws.impl.HelloWorldWs;

public class MyClient {

    public static void main(String[] args) {
        HelloWorldWs factory = new HelloWorldWs();
        HelloWorld hw = factory.getHelloWorldWsPort();
        System.out.println(hw.sayHi("张三"));
        
        StringCat sc = hw.getAllCats();
        for(Entry entry : sc.getEntries()){
            System.out.println(entry.getKey() + ":" + entry.getValue().getName());
        }
    }
}

注意:myService指定我们的服务名字(在web.xml中配置),而fkjava指定服务提供者的地址(在applicationContext.xml中配置)。同时这种集成不好,因为将地址写死了,没有使用spring的注入方式。
我们看HelloWorldWs.java这个服务类:

package org.fkjava.cxf.ws.impl;
import java.util.Date;
import java.util.List;
import java.util.Map;
import javax.jws.WebService;
import org.fkjava.cxf.ws.HelloWorld;
import org.fkjava.cxf.ws.domain.Cat;
import org.fkjava.cxf.ws.domain.User;
import org.fkjava.cxf.ws.service.UserService;
import org.fkjava.cxf.ws.service.impl.UserServiceImpl;

@WebService(endpointInterface = "org.fkjava.cxf.ws.HelloWorld", serviceName = "HelloWorldWs")
public class HelloWorldWs implements HelloWorld {

    @Override
    public String sayHi(String name) {
        return name + "您好!" + "现在的时间是: " + new Date();
    }

    @Override
    public List<Cat> getCatsByUser(User user) {
        //这里我们需要手工new,如果交给spring管理可以将对象注入进来
        UserService service = new UserServiceImpl();
        return service.getCatByUser(user);
    }
    
    //新增方法
    @Override
    public Map<String, Cat> getAllCats() {
        UserService service = new UserServiceImpl();
        return service.getAllCats();
    }
}

可以看到其中的实际业务处理类还是使用的手工方法进行实例化,没有使用spring的注入方式。

<2>使用注入方式

spring的配置文件中改变暴露WebService的方式:

<bean id="helloWorldWs" class="org.fkjava.cxf.ws.impl.HelloWorldWs">
        <property name="userService" ref="userService"></property>
    </bean>
    <jaxws:endpoint
        implementor="#helloWorldWs"
        address="/fkjava">
    </jaxws:endpoint>

说明:#号是为了让服务器知道这个名字不是一个类 ,而只是一个id
然后改造HelloWorldWs.java服务类:

package org.fkjava.cxf.ws.impl;
import java.util.Date;
import java.util.List;
import java.util.Map;
import javax.jws.WebService;
import org.fkjava.cxf.ws.HelloWorld;
import org.fkjava.cxf.ws.domain.Cat;
import org.fkjava.cxf.ws.domain.User;
import org.fkjava.cxf.ws.service.UserService;
import org.fkjava.cxf.ws.service.impl.UserServiceImpl;

@WebService(endpointInterface = "org.fkjava.cxf.ws.HelloWorld", serviceName = "HelloWorldWs")
public class HelloWorldWs implements HelloWorld {
    
    private UserService userService;
    @Override
    public String sayHi(String name) {
        return name + "您好!" + "现在的时间是: " + new Date();
    }

    @Override
    public List<Cat> getCatsByUser(User user) {
        return userService.getCatByUser(user);
    }
    
    //新增方法
    @Override
    public Map<String, Cat> getAllCats() {
        return userService.getAllCats();
    }
    public void setUserService(UserService userService) {
        this.userService = userService;
    }
}

然后我们再次使用之前的地址进行访问,发现效果还是一样的。

2.2 添加拦截器

在上面配置的基础上进行添加,在applicationContext.xml中:

<jaxws:endpoint
    implementor="#helloWorldWs"
    address="/fkjava">
    <!-- 如果要添加out拦截器,则使用 jaxws:outInterceptors标签定义 -->
    <jaxws:inInterceptors>
        <!--这里是临时定义的一个嵌套bean,这是cxf提供的拦截器-->
        <bean class="org.apache.cxf.interceptor.LoggingInInterceptor"/>
        <!--这里是引用容器中已有的一个bean-->
        <!--<ref bean="anotherInterceptor"/>-->
        <!--这里将我们自己的权限控制器引进来-->
        <bean class="org.fkjava.cxf.ws.auth.AuthInterceptor"/>
    </jaxws:inInterceptors>
</jaxws:endpoint>

此时,和以前一样我们需要在客户端(工程Call_CXFSpring)那边加上拦截器,添加上用户名和密码的头,然后对服务器进行访问。以此来进行验证。客户端中我们运行类:
MyClient.java

package org.fkjava.cxf.ws.client;
import org.apache.cxf.endpoint.Client;
import org.apache.cxf.frontend.ClientProxy;
import org.fkjava.cxf.ws.auth.AddHeaderInterceptor;
import org.fkjava.cxf.ws.Entry;
import org.fkjava.cxf.ws.HelloWorld;
import org.fkjava.cxf.ws.StringCat;
import org.fkjava.cxf.ws.impl.HelloWorldWs;

public class MyClient {

    public static void main(String[] args) {
        HelloWorldWs factory = new HelloWorldWs();
        HelloWorld hw = factory.getHelloWorldWsPort();
        
        Client client = ClientProxy.getClient(hw) ;//调用此方法,以远程WebService的代理为参数
        client.getOutInterceptors().add(new AddHeaderInterceptor("大熊", "111"));
        
        System.out.println(hw.sayHi("张三"));
        StringCat sc = hw.getAllCats();
        for(Entry entry : sc.getEntries()){
            System.out.println(entry.getKey() + ":" + entry.getValue().getName());
        }
    }
}

访问服务端。


2

三、另一种整合方式

传统的SSH项目中,一般在本地是有相关的数据的,但是有一种场景是我们需要调用别人的数据,但是别人的数据不可能让我们进行修改等操作,那么别人只会暴露一个WebService,而我们就需要去调用别人的WebService服务器。我们在访问别人时,只能得到WSDL文档。此时我们的项目中就不需要业务逻辑组件了,直接去调用别人的WebService的一个代理即可。

3.1 环境

首先我们拷贝上面的工程,改名为cxf_springClient,然后需要增加struts2的相关jar包:

asm-3.3.jar(会和CXF包中的重复,删掉低版本)、
asm-commons-3.3.jar、
asm-tree-3.3.jar、
commons-lang3-3.2.jar、
commons-fileupload-1.3.1.jar、
commons-io-2.2.jar、
commons-lang-2.4.jar(会和CXF中的重复,删掉低版本)、
freemarker-2.3.22.jar、
javassist-3.11.0.GA.jar、
ognl-3.0.6.jar、
struts2-core-2.3.24.jar、
xworks-core-2.3.24.jar。
strut22-spring-plugin-2.3.24.1.jar(用于和spring集成的插件)

将原有的一些包删掉,留下权限包org.fkjava.cxf.ws.auth。然后我们启动工程Auth_Server提供远程服务。在web工程中生成相应的包及程序。

3.2 配置

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/applicationContext.xml</param-value>
    </context-param>

    <!-- struts2 -->
    <filter>
        <filter-name>struts2</filter-name>
        <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>struts2</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    
    <!-- 使用一个监听器加载spring容器,保证web应用启动时加载spring -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
</web-app>

说明:这里我们不再需要CXFservlet了,因为不需要业务类了,我们直接调用远程业务即可。同时配置struts2的核心控制器。

applicationContext.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" 
         xmlns:aop="http://www.springframework.org/schema/aop"
         xmlns:tx="http://www.springframework.org/schema/tx"
         xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
           http://cxf.apache.org/jaxws
           http://cxf.apache.org/schemas/jaxws.xsd">

    <import resource="classpath:META-INF/cxf/cxf.xml"/>
        
    <!-- action依赖的是远程WebService的代理,所以这里不能配置本地的业务逻辑组件,而是需要
    配置远程WebService代理,为了保证该WebService代理对象可以自动装配给action,因此应该保证该id的值与action中的setter方法名对应 -->
    <jaxws:client   id="hw"
                    serviceClass="org.fkjava.cxf.ws.HelloWorld"
                    address="http://localhost:8888/myService">
                    <jaxws:outInterceptors>
                        <bean class="org.fkjava.cxf.ws.auth.AddHeaderInterceptor">
                            <constructor-arg value="大熊"/>
                            <constructor-arg value="111"/>
                        </bean>
                    </jaxws:outInterceptors>
    </jaxws:client>
</beans>

说明:这里我们同样不需要业务类了,但是需要配置远程业务代理类(serviceClass)。同时注意:不要将端口设置为8080,会冲突。可以看到我们使用serviceClass将接口配置进去,而这里的id不能随便写,要和action中定义的接口名一致。由于远程服务器设置了权限拦截,所以这里使用过滤器将用户名和密码设置到wsdl文档的头中。然后在这个类中我们需要使用execute方法去获取相应的数据。

struts.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
    "http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
    <constant name="struts.enable.DynamicMethodInvocation" value="false" />
    <constant name="struts.devMode" value="true" />
    <package name="default" namespace="/" extends="struts-default">
        <action name="listCats" class="org.fkjava.cxf.ws.action.ListCatsAction">
            <result name="success">
                /WEB-INF/content/listCats.jsp
            </result>
        </action>
    </package>
</struts>

3.3 Action

ListCatsAction .java

package org.fkjava.cxf.ws.action;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.fkjava.cxf.ws.Entry;
import org.fkjava.cxf.ws.Cat;
import org.fkjava.cxf.ws.HelloWorld;
import org.fkjava.cxf.ws.StringCat;
import com.opensymphony.xwork2.ActionSupport;

public class ListCatsAction extends ActionSupport {
    
    private HelloWorld hw ;
    private Map<String, Cat> cats;
    
    public HelloWorld getHw() {
        return hw;
    }
    public void setHw(HelloWorld hw) {
        this.hw = hw;
    }

    public Map<String, Cat> getCats() {
        return cats;
    }
    public void setCats(Map<String, Cat> cats) {
        this.cats = cats;
    }
    
    //此处的action调用的是远程WebService方法
    public String execute(){
        StringCat sc = hw.getAllCats();
        Map<String, Cat> map = new HashMap<String, Cat>();
        for(Entry entry : sc.getEntries()){
            map.put(entry.getKey(), entry.getValue());
        }
        setCats(map);
        return SUCCESS;
    }
}

说明:此时我们就可以使用地址http://localhost:8080/cxf_springClient/listCats进行访问了,当然会报找不到jsp的错误。

3.4 listCats.jsp

content/listCats.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
            pageEncoding="UTF-8" isELIgnored="false"%>
<%@taglib prefix="s" uri="/struts-tags"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>所有的猫</title>
</head>
<body>
    <ul>
        <s:iterator var="entry" value="cats" >
            <li>${entry.key }-->${entry.value.name }-->${entry.value.color }</li>
        </s:iterator>
    </ul>
</body>
</html>

说明:此时访问就可以看到

1

这样就表示成功。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,711评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,932评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,770评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,799评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,697评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,069评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,535评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,200评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,353评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,290评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,331评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,020评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,610评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,694评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,927评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,330评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,904评论 2 341

推荐阅读更多精彩内容