webservice -CXF

web service

web service是什么

  • web service出现的目的:为了支持“异构网络”中的应用程序之间交互出现的;

  • web service被定义成一组模块化的api,可以通过网络进行远程调用;

  • web service是一个跨语言、跨平台的远程调用技术;

web service使用场景

  • 不同公司系统之间的数据交互

    注册微信公众号的公司 和 腾讯公司需要交互数据

    电商系统和物流公司(四通一达 + 顺丰)需要数据交互(查询物流信息)

  • 一些公共的数据服务:手机号归属地查询天气预报服务股票行情英文翻译

  • 同一公司,不同系统(有可能不是同一种开发语言)之间的数据交互

web service的三要素

  • WSDL(Web Service Definition/Description Language)

web服务定义/描述语言

  1. 用于描述具体服务,定义客户端和服务端之间数据交互时传递的数据格式(请求和响应的数据)
  2. 每一个web service对应为一个wsdl文档
  3. WSDL可以认为是web service说明书
  • SOAP(Simple Object Access Protocol)

简单对象访问协议,基于Http协议,使用XML传递消息;
它是一种轻量级的通信协议;
用于不同应用之间的通信;
使用Http协议进行通信;
独立于平台、编程语言,基于XML,简单并可扩展

  • UDDI

web service规范

  • JAX-WS规范

全称:Java Api For Xml-Based WebService;
早期的时候是叫JAX-RPC(Java Api For Xml-Rmote Procedure Call);
JAX-RPC目前已经被JAX-WS取代;

JDK5.0开始支持JAX-WS的2.0版本;
JDK1.6.0_13版本开始支持JAX-WS的2.1版本
JDK7支持了JAX-WS的2.2版本

采用标准的Soap协议传输数据,Soap协议是基于应用层的Http协议,传输xml数据;
采用WSDL做为服务的描述语言;

  • JAX-RS规范

是Java针对REST风格的请求制定的一套web服务规范;
没有随着JDK1.6发布

支持JAX-RS规范的框架

  • CXF
  • RESTEasy :JBOSS的
  • RESTLet:比较早的rest框架,比JAX-RS规范还要早
  • Jersey

使用JDK方式开发web service

开发

HelloService.java==>接口

package com.etoak.service;
// 这个@WebService需要同时写在接口和实现类上
@WebService
public interface HelloService {
    String sayHello(String name);
}

HelloServiceImpl.java==>实现

package com.etoak.service.impl;
@WebService
public class HelloServiceImpl implements HelloService {
    @Override
    public String sayHello(String name) {
        System.out.println("server invoke");
        return "Hello " + name;
    }
}

JdkService.java==>启动服务

package com.etoak;
public class JdkServer {
    public static void main(String[] args) {
        // 参数一:服务地址
        // 参数二:要发布的服务
        Endpoint.publish("http://localhost:8000/hello",
                new HelloServiceImpl());
    }
}

调用

将开发的webservice拉取到本地

cmd命令

wsimport -keep http://localhost:8000/hello?wsdl

将文件粘贴到工程中

JdkClient.java==>客户端调用

package com.etoak;
public class JdkClient {
    public static void main(String[] args) {
        HelloServiceImplService service = new HelloServiceImplService();

        HelloServiceImpl soap = service.getHelloServiceImplPort();

        String result = soap.sayHello("web service");
        System.out.println(result);
    }
}

使用CXF

CXF框架是Apache的顶级开源项目

官方地址:cxf.apache.org

Apache CXF = Celtix + Xfire,开始叫 Apache CeltiXfire,后来更名为 Apache CXF了。

​ Apache CXF 是一个开源的 web Service 框架,CXF 帮助您构建和开发 web Services,它支持多种协议,比如:SOAP1.1,1,2 XML/HTTP、RESTful 或者CORBA。

灵活的部署方式: 可以运行在Tomcat、Jboss、Jetty(内置)、weblogic上面。

下载CXF二进制包

解压到当前目录

主要用到bin目录下的一个命令:wsdl2java

这个命令用来根据wsdl创建客户端代码;

wsdl2java -d E:\xxx wsdl地址

maven依赖

<dependency>
	<groupId>org.apache.cxf</groupId>
	<artifactId>cxf-rt-frontend-jaxws</artifactId>
	<version>3.1.18</version>
</dependency>
<dependency>
	<groupId>org.apache.cxf</groupId>
	<artifactId>cxf-rt-transports-http</artifactId>
	<version>3.1.18</version>
</dependency>
<dependency>
	<groupId>org.apache.cxf</groupId>
	<artifactId>cxf-rt-transports-http-jetty</artifactId>
	<version>3.1.18</version>
</dependency>

使用CXF开发服务器与客户端

开发

  • 添加依赖
  • 实体类:略
  • 定义服务接口UserService.java
package com.etoak.service;
/**
 * 定义服务接口
 */
@WebService(serviceName = "UserService", portName = "UserServiceSoap")
public interface UserService {
    User getById(Integer id);
}
  • 实现UserServiceImpl.java
package com.etoak.service.impl;
@WebService(serviceName = "UserService", portName = "UserServiceSoap")
public class UserServiceImpl implements UserService {
    @Override
    public User getById(Integer id) {
        return new User(id, "zs", 23);
    }
}
  • 启动服务CxfService.java
package com.etoak;
public class CxfServer {
    public static void main(String[] args) {
        // 1. 创建JaxWsServerFactoryBean
        JaxWsServerFactoryBean factory = new JaxWsServerFactoryBean();

        // 2. 设置wsdl地址
        factory.setAddress("http://localhost:8001/user");

        // 3. 设置要发布的服务
        factory.setServiceClass(UserService.class);

        // 4. 设置服务实现
        factory.setServiceBean(new UserServiceImpl());

        // 5. 创建服务并启动服务
        Server server = factory.create();
        server.start();
        System.out.println("server start");
    }
}

调用

  • 拉取文件

    • 到bin目录下启动cmd

      `wsdl2java -d E:\xxx wsdl地址`
      
  • 调用CxfClient.java

package com.etoak;
public class CxfClient {
    public static void main(String[] args) {
        // 1. 创建JaxWsProxyFactoryBean
        JaxWsProxyFactoryBean factory =
            new JaxWsProxyFactoryBean();
        // 2. 设置wsdl访问地址
        factory.setAddress("http://localhost:8001/user");
        // 3. 设置服务接口
        factory.setServiceClass(UserService.class);
        // 4. 创建远程服务的代理对象
        UserService userService = (UserService)factory.create();
        User user = userService.getById(100);
        System.out.println(user.getId() + " - "
                + user.getName() + " - "
                + user.getAge());
    }
}

调用其他webservice【以手机号码归属地查询服务为例】

  • 拉取文件【网址在网上获得】,文件存放在E盘下cxf文件夹内
wsdl2java -d E:/cxf http://ws.webxml.com.cn/WebServices/MobileCodeWS.asmx?wsdl
  • 将文件复制到工程中

  • 创建文件调用服务

    • jdk方式

      package cn.com;
      public class Client {
          public static void main(String[] args) throws Exception{
              MobileCodeWS service = new MobileCodeWS();
              MobileCodeWSSoap soap = service.getMobileCodeWSSoap();
      		
              //以下两个方法由该服务提供
              /**
              *获得国内手机号码归属地省份、地区和手机卡类型信息
      		*输入参数:mobileCode = 字符串(手机号码,最少前7位数字),userID = 字符串(商业用户ID) 免费用户为空字符串;
      		*返回数据:字符串(手机号码:省份 城市 手机卡类型)。
              */
              String result = soap.getMobileCodeInfo("15508633260","");
              /**
              *获得国内手机号码归属地数据库信息
      		*输入参数:无;返回数据:一维字符串数组(省份 城市 记录数量)。
              */
              ArrayOfString databaseInfo = soap.getDatabaseInfo();
      
              System.out.println(result);
              System.out.println(databaseInfo.getString());
          }
      }
      
    • cxf方式

      package cn.com;
      public class Cxf {
          public static void main(String[] args) {
              JaxWsProxyFactoryBean factoryBean = new JaxWsProxyFactoryBean();
      
              factoryBean.setAddress("http://ws.webxml.com.cn/WebServices/MobileCodeWS.asmx?wsdl");
      
              factoryBean.setServiceClass(MobileCodeWSSoap.class);
      
              MobileCodeWSSoap soap = (MobileCodeWSSoap)factoryBean.create();
      		//同上
              String result = soap.getMobileCodeInfo("15508633260","");
              ArrayOfString databaseInfo = soap.getDatabaseInfo();
      
              System.out.println(result);
              System.out.println(databaseInfo.getString());
          }
      }
      

cxf整合spring

Maven依赖

javax.servlet-api
spring-web(不是spring-webmvc)
spring-context
cxf-rt-frontend-jaxws
cxf-rt-transports-http
lombok

服务端

  • 在web.xml中配置CXF
<web-app>
  <!-- CXFServlet -->
  <servlet>
    <servlet-name>CXF</servlet-name>
    <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
    <!-- 如果不配置config-location,默认查找/WEB-INF/cxf-servlet.xml -->
    <init-param>
      <param-name>config-location</param-name>
      <param-value>classpath:spring-cxf.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>CXF</servlet-name>
    <url-pattern>/ws/*</url-pattern>
  </servlet-mapping>
</web-app>
  • 创建服务接口
package com.etoak.service;
/**
 * 用户服务接口
 */
@WebService(serviceName = "UserServiceWS", portName = "UserServiceSoap")
public interface UserService {

    User getById(Integer id);

}
package com.etoak.mapper;
public interface UserMapper {
    User getById(int id);
}
  • 创建服务接口实现
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.etoak.mapper.UserMapper">
    <select id="getById" parameterType="int" resultType="User">
        select id,name,age from t_springmvc_user where id = #{value}
    </select>
</mapper>
package com.etoak.service.impl;
@WebService(serviceName = "UserServiceWS",portName = "UserServiceSoap")
@Service
public class UserServiceImpl implements UserService {

    @Autowired
    UserMapper userMapper;

    @Override
    public User getById(Integer id) {
        return userMapper.getById(id);
    }
}
  • 使用spring发布web service服务

spring-cxf.xml

    <context:component-scan base-package="com.etoak" />
    <!-- 数据源
         SqlSessionFactoryBean
         MapperScannerConfigurer
     -->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
       <property name="driverClassName" value="com.mysql.jdbc.Driver" />
       <property name="url" value="jdbc:mysql://127.0.0.1:3306/et1912" />
       <property name="username" value="root" />
       <property name="password" value="root" />
       <!-- 初始化连接数  -->
       <property name="initialSize" value="20" />
       <!-- 最大活跃连接数 -->
       <property name="maxActive" value="50" />
       <property name="validationQuery" value="select 1" />
    </bean>

    <bean class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="typeAliasesPackage" value="com.etoak.bean" />
        <property name="mapperLocations"
                  value="classpath:/mappers/*.xml" />
        <property name="plugins" >
            <list>
                <bean class="com.github.pagehelper.PageInterceptor">
                    <property name="properties">
                        <props>
                            <prop key="reasonable">true</prop>
                        </props>
                    </property>
                </bean>
            </list>
        </property>
    </bean>
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.etoak.mapper" />
    </bean>
<!--
    1. JaxWsServerFactoryBean
    2. wsdl地址 : http://localhost:8001/ws/user
    3. 设置服务接口
    4. 设置服务实现
    5. 创建服务并启动服务
-->
<jaxws:server address="/user" serviceClass="com.etoak.service.UserService">
    <jaxws:serviceBean>
        <ref bean="userServiceImpl" />
    </jaxws:serviceBean>
</core:bus>

客户端

  • Maven依赖

cxf-rt-frontend-jaxws
cxf-rt-transports-http
cxf-rt-transports-http-jetty
spring-context

  • 根据wsdl创建客户端代码,

    • 开启服务,利用以下命令获得客户端代码

    • wsdl2java -d e:/client http://localhost:8080/ws/user?wsdl 
      
  • 配置spring bean

<context:component-scan base-package="com.etoak" />
    <!--
        JaxWsProxyFactoryBean
        setAddress:地址
        setServiceClass: 服务接口
        创建服务代理
    -->
<jaxws:client id="userService" address="http://localhost:9090/ws/user" serviceClass="com.etoak.service.UserService">
</jaxws:client>
  • 客户端调用
package com.etoak;
public class SpringClient {
    public static void main(String[] args) {
        
        ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-cxf.xml");

        UserService userService = (UserService)ioc.getBean("userService");

        User user = userService.getById(2);
        System.out.println(user.getId()+"+"+user.getName()+"+"+user.getAge());
    }
}

CXF拦截器

Cxf可以在web service发送前后,动态操作请求和响应的报文数据(xml的soap消息);

  • 拦截器分类

    • 按消息方向:In拦截器、Out拦截器

      客户端发送请求,先走Out拦截器,结果回来之前走In拦截器;

      服务端接收请求,先走In拦截器,响应之前走Out拦截器;

    • 拦截位置:服务端拦截、客户端拦截;

    • 按定义者:Cxf官方定义的拦截器、使用方定义的拦截(自定义拦截器);

  • 拦截的几个API

    • ​ Interceptor**、PhaseInterceptor阶段拦截、**SoapInterceptor
    • 阶段拦截器:定义了拦截器需要在哪个阶段(封装协议前、发送前、发送后、接收后…)进行拦截。详细阶段在Phase.java`类中定义

配置官方拦截器

  • 服务端拦截器

    • 配置In拦截器
    <jaxws:server address="/user" serviceClass="com.etoak.service.UserService">
      <jaxws:serviceBean>
          <ref bean="userServiceImpl" />
      </jaxws:serviceBean>
    
      <!-- 服务器接收请求:使用In拦截器 -->
      <jaxws:inInterceptors>
        <!-- 使用官方定义的日志拦截器: 默认拦截阶段 -> receive -->
        <bean class="org.apache.cxf.interceptor.LoggingInInterceptor" />
      </jaxws:inInterceptors>
    </jaxws:server>
    
    • 配置Out拦截器
    <jaxws:server address="/user"
      serviceClass="com.etoak.service.UserService">
      <jaxws:serviceBean>
          <ref bean="userServiceImpl" />
      </jaxws:serviceBean>
      
      <!-- 服务器响应结果:使用Out拦截器 -->
      <jaxws:outInterceptors>
        <!-- 使用官方定义的日志拦截器 -->
        <bean class="org.apache.cxf.interceptor.LoggingOutInterceptor" />
      </jaxws:outInterceptors>
    </jaxws:server>
    
  • 客户端拦截器

    • 配置In拦截器
    <jaxws:client id="userService"
      address="http://localhost:9090/ws/user"
      serviceClass="com.etoak.service.UserService">
    
      <!-- 客户端In拦截器:接收服务器响应的某个阶段(receive)执行 -->
      <jaxws:inInterceptors>
        <bean class="org.apache.cxf.interceptor.LoggingInInterceptor" />
      </jaxws:inInterceptors>
    </jaxws:client>
    
    • 配置Out拦截器
    <jaxws:client id="userService"
      address="http://localhost:9090/ws/user"
      serviceClass="com.etoak.service.UserService">
    
      <!-- 客户端Out拦截器:客户端在发送请求的某个阶段(pre-stream)执行 -->
      <jaxws:outInterceptors>
        <bean class="org.apache.cxf.interceptor.LoggingOutInterceptor" />
      </jaxws:outInterceptors>
    </jaxws:client>
    

配置自定义拦截器

  1. 自定义拦截器需要继承AbstractPhaseInterceptor
  2. 使用自定义拦截器实现用户验证
  3. 用户请求Soap消息
  4. 验证失败返回的Soap消息
  • 客户端创建、配置自定义拦截器

    • 创建拦截器
    package com.etoak.interceptor;
    public class AuthOutInterceptor
      extends AbstractPhaseInterceptor<SoapMessage> {
    
      private String name;
      private String password;
    
      public AuthOutInterceptor(String name, String password) {
          // 调用父类的构造方法,传入默认的拦截阶段
          super(Phase.PREPARE_SEND);
          this.name = name;
          this.password = password;
      }
      /**
       * 处理请求数据
       * <soap:Header>
       *     <et1912>
       *         <name>zs</name>
       *         <password>123456</password>
       *     </et1912>
       * </soap:Header>
       *
       * @param message
       * @throws Fault
       */
      @Override
      public void handleMessage(SoapMessage message) 
        throws Fault {
        // 创建Document
        Document document = DOMUtils.createDocument();
        Element et1912 = document.createElement("et1912");
        // 创建子元素<name> <password>
        Element nameElement = document.createElement("name");
        nameElement.setTextContent(this.name);
    
        Element passwordElement = document.createElement("password");
        passwordElement.setTextContent(this.password);
    
        // 将<name>和<password>元素添加到et1912
        et1912.appendChild(nameElement);
        et1912.appendChild(passwordElement);
        Header header = new Header(new QName(""), et1912);
        message.getHeaders().add(header);
      }
    }
    
    • 配置拦截器
    <jaxws:client id="userService"
      address="http://localhost:9090/ws/user"
      serviceClass="com.etoak.service.UserService">
    
        <!-- 客户端In拦截器:接收服务器响应的某个阶段(receive)执行 -->
        <jaxws:inInterceptors>
          <bean class="org.apache.cxf.interceptor.LoggingInInterceptor" />
        </jaxws:inInterceptors>
    
        <!-- 客户端Out拦截器: -->
        <jaxws:outInterceptors>
          <!-- 客户端在发送请求的某个阶段(pre-stream)执行 -->
          <bean class="org.apache.cxf.interceptor.LoggingOutInterceptor" />
    
          <!-- 配置自定义拦截器-->
          <bean class="com.etoak.interceptor.AuthOutInterceptor">
            <constructor-arg name="name" value="zs" />
            <constructor-arg name="password" value="123456" />
          </bean>
        </jaxws:outInterceptors>
    </jaxws:client>
    
    • 发送soap报文
    <soap:Envelope 
      xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
      <soap:Header>
      	<et1912>
      	  <name>zs</name>
      	  <password>123456</password>
      	</et1912>
      </soap:Header>
      <soap:Body>
      	<ns2:getById 
           xmlns:ns2="http://service.etoak.com/">
      	  <arg0>1</arg0>
      	</ns2:getById>
      </soap:Body>
    </soap:Envelope>
    
    • 错误响应
    <soap:Envelope 
      xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
      <soap:Body>
        <soap:Fault>
          <faultcode>soap:Server</faultcode>
          <faultstring>用户名格式错误</faultstring>
        </soap:Fault>
      </soap:Body>
    </soap:Envelope>
    
    • 正确响应
    <soap:Envelope 
      xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
      <soap:Body>
        <ns2:getByIdResponse 
          xmlns:ns2="http://service.etoak.com/">
          <return>
            <age>22</age>
            <id>1</id>
            <name>admin</name>
          </return>
        </ns2:getByIdResponse>
      </soap:Body>
    </soap:Envelope>
    
  • 服务端创建、配置自定义拦截器

    • 创建拦截器
    package com.etoak.interceptor;
    public class AuthInInterceptor
      extends AbstractPhaseInterceptor<SoapMessage> {
    
      public AuthInInterceptor(String phase) {
          super(phase);
      }
    
      @Override
      public void handleMessage(SoapMessage message) throws Fault {
        Header et1912 = message.getHeader(new QName("et1912"));
        if(et1912 != null) {
          // 解析soap header
          Element et1912Ele = (Element)et1912.getObject();
    
          // 获取name元素 并校验name元素
          NodeList nameList = et1912Ele.getElementsByTagName("name");
          if(nameList == null || nameList.getLength() != 1) {
              throw new Fault(new RuntimeException("用户名格式错误"));
          }
    
          // 获取password元素,并校验password元素
          NodeList passwordList =
            et1912Ele.getElementsByTagName("password");
          if(passwordList == null || passwordList.getLength() != 1) {
              throw new Fault(new RuntimeException("密码格式错误"));
          }
    
          // 走到这里,表示校验通过,获取用户名和密码
          String name = nameList.item(0).getTextContent();
          String password = passwordList.item(0).getTextContent();
    
          // 校验用户名和密码 (根据用户名和密码查询数据库)
          if("zs".equals(name) && "123456".equals(password)) {
              System.out.println("校验成功!");
              return;
          } else {
            throw new Fault(new RuntimeException("用户名或密码错误"));
                }
        }
    
        // 走到这里,表示soap消息中没有<et1912>的Header元素
        throw new Fault(new RuntimeException("请传入用户名和密码"));
      }
    }
    
    • 配置拦截器
    <jaxws:server address="/user"
      serviceClass="com.etoak.service.UserService">
      <jaxws:serviceBean>
          <ref bean="userServiceImpl" />
      </jaxws:serviceBean>
    
      <!-- 服务器接收请求:使用In拦截器 -->
      <jaxws:inInterceptors>
        <!-- 使用官方定义的日志拦截器: 默认拦截阶段 -> receive -->
        <bean class="org.apache.cxf.interceptor.LoggingInInterceptor" />
    
        <!-- 使用自定义拦截器 -->
        <bean class="com.etoak.interceptor.AuthInInterceptor">
          <!-- 执行阶段:方法执行前拦-->
          <constructor-arg name="phase" value="pre-invoke" />
        </bean>
      </jaxws:inInterceptors>
    
      <!-- 服务器响应结果:使用Out拦截器 -->
      <jaxws:outInterceptors>
        <!-- 使用官方定义的日志拦截器 -->
        <bean class="org.apache.cxf.interceptor.LoggingOutInterceptor" />
      </jaxws:outInterceptors>
    </jaxws:server>
    
    • 配置全局拦截器
    <jaxws:server address="/user"
        serviceClass="com.etoak.service.UserService">
        <jaxws:serviceBean>
            <ref bean="userServiceImpl" />
        </jaxws:serviceBean>
    </jaxws:server>
    
    <jaxws:server address="/student" serviceClass="com.etoak.service.StudentService">
        <jaxws:serviceBean>
            <ref bean="studentServiceImpl" />
        </jaxws:serviceBean>
    
    </jaxws:server>
    
    <core:bus>
     <core:inInterceptors>
       <!-- 使用官方定义的日志拦截器: 默认拦截阶段 -> receive -->
       <bean class="org.apache.cxf.interceptor.LoggingInInterceptor" />
    
       <!-- 使用自定义拦截器 -->
       <bean class="com.etoak.interceptor.AuthInInterceptor">
           <!-- 执行阶段:方法执行前拦-->
           <constructor-arg name="phase" value="pre-invoke" />
       </bean>
     </core:inInterceptors>
     <core:outInterceptors>
       <!-- 使用官方定义的日志拦截器 -->
       <bean class="org.apache.cxf.interceptor.LoggingOutInterceptor" />
     </core:outInterceptors>
    </core:bus>
    
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值