web service学习笔记

0. web service内容

  1. web service是什么
  2. web service使用场景
  3. web service三要素
  4. java的web service规范
  5. 使用jdk方式发布web service
  6. 使用cxf方式发布web service
  7. cxf整合spring web
  8. cxf拦截器

1. web service是什么

web service出现的目的:为了支持处于“异构网络”的应用程序之间交互(通信)出现的;

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

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

2. web service使用场景

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

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

    电商系统和物流公司需要数据交互(查询物流信息)

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

在这里插入图片描述

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

3. web service的三要素

  1. WSDL(Web Service Definition/Description Language)

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

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

4. web service规范

  1. JAX-WS规范

    全称:Java Api For Xml-Based WebService;
    早期的时候是叫JAX-RPC(Java Api For Xml-Rmote Procedure Call);
    JAX-RPC目前已经被JAX-WS取代;
    
    采用WSDL做为服务的描述语言;
    采用标准的Soap协议传输xml数据;
    
  2. JAX-RS规范

    是Java针对REST风格的请求制定的一套web服务规范;
    
    支持JAX-RS规范的框架
    - CXF
    - RESTEasy :JBOSS的
    - RESTLet:比较早的rest框架,比JAX-RS规范还要早
    - Jersey
    

5. 使用jdk方式开发web service

5.1 服务端

  1. 开发服务接口(interface)SEI(Service Endpoint Interface)

  2. 开发服务实现

  3. 发布web service服务

  4. 注意:

    接口和实现类上上都加上@WebService

5.2 开发jdk方式的客户端

  1. 根据服务端的wsdl创建客户端代码

    jdk提供了创建客户端代码的工具:wsimport

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

  2. 发起远程调用

  3. 测试使用PostMan发起远程服务调用

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
 <soap:Body>
   <a:sayHello xmlns:a="http://impl.service.etoak.com/">
     <arg0>asdasdasdasdasd</arg0>
   </a:sayHello>
 </soap:Body>
 </soap:Envelope>

JdkServer

import com.etoak.service.impl.HelloServiceImpl;
import javax.xml.ws.Endpoint;

public class JdkServer {
    public static void main(String[] args) {
        Endpoint.publish("http://localhost:8080/hello",new HelloServiceImpl());
        System.out.println("Server start");
    }
}

jdkClient

import com.etoak.service.impl.HelloServiceImpl;
import com.etoak.service.impl.HelloServiceImplService;

public class jdkClient {
    public static void main(String[] args) {
        //创建服务视图,视图是从wsdl文件的service标签的name属性获取
        HelloServiceImplService soap = new HelloServiceImplService();
        //获取服务实现类,实现类从wsdl的proType的name属性获取的
        HelloServiceImpl soapPort = soap.getHelloServiceImplPort();
        //获取查询方法,从portType的operation标签获取
        String result = soapPort.sayHello("ZS");
        System.out.println(result);
    }
}

6. 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上面。

6.1 下载CXF二进制包

下载地址:http://cxf.apache.org/download.html

在这里插入图片描述

6.2 解压到当前目录

​ 工具:cxf框架提供了wsdl2java命令

​ 作用:创建客户端代码

​ 使用方式:wsdl2java -d e:/client http://localhost:9000/user?wsdl

​ -d参数:表示客户端代码创建到哪个目录中

6.3 maven依赖

maven依赖说明:

http://cxf.apache.org/docs/using-cxf-with-maven.html

<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>

6.4 使用Cxf开发服务端 和客户端

  1. 开发服务端
    • 创建服务接口
    • 创建服务实现
    • 发布服务
  2. 开发客户端
    • 根据wsdl创建客户端代码
    • 调用远程服务

CxfServer

import com.etoak.service.UserService;
import com.etoak.service.impl.UserServiceImpl;
import org.apache.cxf.endpoint.Server;
import org.apache.cxf.jaxws.JaxWsServerFactoryBean;

public class CxfServer {
    public static void main(String[] args) {
        //创建jaxWSServerFactoryBean
        JaxWsServerFactoryBean factoryBean = new JaxWsServerFactoryBean();
        //2.设置服务接口(wsdl地址)
        factoryBean.setAddress("http://localhost:8080/user");
        //3.设置服务接口
        factoryBean.setServiceClass(UserService.class);
        //4、设置服务实现类
        factoryBean.setServiceBean(new UserServiceImpl());
        //5、创建并启动服务
        Server server = factoryBean.create();
        server.start();
        System.out.println("Server start");
    }
}

CxfClient

import com.etoak.service.User;
import com.etoak.service.UserService;
import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;

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

7. cxf整合spring web

​ 实现根据id查询用户信息

  1. 搭建客户端工程
  2. 创建客户端代码
  3. 配置spring容器
  4. 调用远程服务

服务器端
web.xml

<!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>
    <servlet>
        <servlet-name>cxf</servlet-name>
        <servlet-class>
            org.apache.cxf.transport.servlet.CXFServlet
        </servlet-class>
        <init-param>
            <param-name>config-location</param-name>
            <param-value>classpath:spring-cxf.xml</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>cxf</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>
</web-app>

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:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:jaxws="http://cxf.apache.org/jaxws"
       xmlns:core="http://cxf.apache.org/core"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
        http://cxf.apache.org/jaxws
        http://cxf.apache.org/schemas/jaxws.xsd
        http://cxf.apache.org/core
        http://cxf.apache.org/schemas/core.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-4.2.xsd">
        <context:component-scan base-package="com.etoak"/>
	    <!--整合数据源-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="username" value="root" />
        <property name="password" value="etoak" />
        <property name="url" value="jdbc:mysql://localhost:3306/et2004" />
        <property name="driverClassName" value="com.mysql.jdbc.Driver" />
    </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" >
            <array>
                <bean class="com.github.pagehelper.PageInterceptor">
                    <property name="properties">
                        <props>
                            <prop key="reasonable">true</prop>
                        </props>
                    </property>
                </bean>
            </array>
        </property>
    </bean>
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.etoak.mapper"></property>
    </bean>

    <!--配置服务-->
        <!--
            JaxWsServiceFactoryBean
            设置服务地址
            设置服务接口
            设置服务实现
            创建服务
        -->
        <!--服务地址:http://ip:port/path/ws/user?wdls-->
    <jaxws:server address="/User" serviceClass="com.etoak.service.USerService">
        <jaxws:serviceBean>
            <bean class="com.etoak.service.impl.UserServiceImpl"></bean>
        </jaxws:serviceBean>
    </jaxws:server>

</beans>

UserServiceImpl

import com.etoak.bean.User;
import com.etoak.mapper.UserMapper;
import com.etoak.service.USerService;
import org.springframework.beans.factory.annotation.Autowired;

import javax.jws.WebService;

@WebService
public class UserServiceImpl implements USerService {
    @Autowired
    UserMapper userMapper;
    @Override
    public User getById(int id) {
        return userMapper.getById(id);
    }
}

客户端
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:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:jaxws="http://cxf.apache.org/jaxws"
       xmlns:core="http://cxf.apache.org/core"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
        http://cxf.apache.org/jaxws
        http://cxf.apache.org/schemas/jaxws.xsd
        http://cxf.apache.org/core
        http://cxf.apache.org/schemas/core.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-4.2.xsd">
        <context:component-scan base-package="com.etoak"/>
        <!--配置服务-->
        <!--
            //JaxWsServiceFactoryBean
            设置服务地址
            设置服务接口

            创建服务代理对象
        -->
    <!--远程服务接口的代理对象-->

    <jaxws:client address="http://localhost:8080/ws/User" id="userService"
                  serviceClass="com.etoak.service.USerService">

    </jaxws:client>
</beans>

SpringClient

import com.etoak.service.USerService;
import com.etoak.service.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringClient {
    public static void main(String[] args) {
        ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-cxf.xml");
        USerService userService = ioc.getBean("userService", USerService.class);
        User user = userService.getById(2);
        System.out.println(user.getName()+"-"+user.getAge());
    }
}

生成客户端代码
在这里插入图片描述

8. cxf拦截器

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

1.1 拦截器分类

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

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

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

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

  3. 按定义者:Cxf官方定义的拦截器、自定义拦截器;

1.2 拦截器的几个API

InterceptorPhaseInterceptorSoapInterceptor

​ 阶段拦截器:协议化之前、发送前、接收前、执行前、执行后等等

​ 拦截阶段定义在了Phase.java中

1.3 配置和使用官方拦截器

​ 这里以日志拦截器LoggingInInterceptor和LoggingOutInterceptor为例

1.3.1 在服务端配置官方拦截器
<jaxws:server address="/user"
              serviceClass="com.etoak.service.UserService">
  <jaxws:serviceBean>
    <bean class="com.etoak.service.impl.UserServiceImpl" />
  </jaxws:serviceBean>
  <!-- 服务端配置In拦截器 -->
  <jaxws:inInterceptors>
    <bean class="org.apache.cxf.interceptor.LoggingInInterceptor" />
  </jaxws:inInterceptors>
  <!-- 服务端配置Out拦截器 -->
  <jaxws:outInterceptors>
    <bean class="org.apache.cxf.interceptor.LoggingOutInterceptor" />
  </jaxws:outInterceptors>
</jaxws:server>
1.3.2 在客户端配置官方拦截器
<jaxws:client address="http://localhost:8080/ws/User" id="userService"
                  serviceClass="com.etoak.service.USerService">
        <jaxws:inInterceptors>
            <bean class="org.apache.cxf.interceptor.LoggingInInterceptor"/>
        </jaxws:inInterceptors>

        <jaxws:outInterceptors>
            <bean class="org.apache.cxf.interceptor.LoggingOutInterceptor"/>
        </jaxws:outInterceptors>
    </jaxws:client>

1.4 自定义拦截器

1.4.1 实现内容

​ 使用自定义拦截器实现用户验证

​ 注意:自定义拦截器需要继承AbstractPhaseInterceptor

1.4.2 客户端创建、配置(使用)自定义拦截器
  1. 创建拦截器

    package com.etoak.interceptor;
    
    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;
    import org.apache.cxf.phase.AbstractPhaseInterceptor;
    import org.apache.cxf.phase.Phase;
    import org.w3c.dom.Document;
    import org.w3c.dom.Element;
    
    import javax.xml.namespace.QName;
    import java.util.List;
    
    public class AuthOutInterceptor extends AbstractPhaseInterceptor<SoapMessage> {
    
      private String name;
      private String password;
    
      /**
         * <bean>
         *   <constructor-arg name="name" value="" />
         *   <constructor-arg name="password" value="" />
         * </bean>
         * @param name
         * @param password
         */
      public AuthOutInterceptor(String name, String password) {
        super(Phase.PREPARE_SEND);
        this.name = name;
        this.password = password;
      }
    
      /**
         * 操作Soap消息
         * <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
         *
         *   <soap:Header>
         *     <et2004>
         *       <name>zs</name>
         *       <password>123</password>
         *     </et2004>
         *   </soap:Header>
         *
         *   <soap:Body>
         *   	<ns2:getById xmlns:ns2="http://service.etoak.com/">
         *   	  <arg0>3</arg0>
         *   	</ns2:getById>
         *   </soap:Body>
         * </soap:Envelope>
         *
         * @param message
         * @throws Fault
         */
      @Override
      public void handleMessage(SoapMessage message) throws Fault {
        // 先创建Document文档对象
        Document doc = DOMUtils.createDocument();
    
        // 创建<et2004>元素
        Element et2004 = doc.createElement("et2004");
    
        // 创建<name>元素, 并为<name>添加文本值
        Element nameElement = doc.createElement("name");
        nameElement.setTextContent(this.name);
    
        // 创建<password>元素, 并为<password>添加文本值
        Element passwordElement = doc.createElement("password");
        passwordElement.setTextContent(this.password);
    
        // 为<et2004>元素添加<name>和<password>
        et2004.appendChild(nameElement);
        et2004.appendChild(passwordElement);
    
        // 为<soap:Header>添加<et2004>
        List<Header> headers = message.getHeaders();
        headers.add(new Header(new QName(""), et2004));
      }
    }
    
  2. 配置拦截器

    <jaxws:client id="userService"
                  address="http://localhost:8080/ws/user"
                  serviceClass="com.etoak.service.UserService">
      <!-- 客户端配置In拦截器 -->
      <jaxws:inInterceptors>
          <bean class="org.apache.cxf.interceptor.LoggingInInterceptor" />
      </jaxws:inInterceptors>
    
      <!-- 客户端配置Out拦截器 -->
      <jaxws:outInterceptors>
            <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>
    
  3. 发送soap报文

    <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
      <soap:Header>
        <et2004>
          <name>zs</name>
          <password>123456</password>
        </et2004>
      </soap:Header>
      <soap:Body>
        <ns2:getById xmlns:ns2="http://service.etoak.com/">
          <arg0>3</arg0>
        </ns2:getById>
      </soap:Body>
    </soap:Envelope>
    
  4. 响应的soap报文

1.4.2 服务端创建、配置(使用)自定义拦截器
  1. 创建拦截器

    package com.etoak.interceptor;
    
    import org.apache.commons.lang3.StringUtils;
    import org.apache.cxf.binding.soap.SoapMessage;
    import org.apache.cxf.headers.Header;
    import org.apache.cxf.interceptor.Fault;
    import org.apache.cxf.phase.AbstractPhaseInterceptor;
    import org.apache.cxf.phase.Phase;
    import org.w3c.dom.Element;
    import org.w3c.dom.NodeList;
    
    import javax.xml.namespace.QName;
    
    public class AuthInInterceptor extends AbstractPhaseInterceptor<SoapMessage> {
    
      public AuthInInterceptor() {
        //拦截阶段
        super(Phase.PRE_INVOKE);
      }
    
      /**
         * 解析xml
         * <soap:Header>
         *     <et2004>
         *         <name>zs</name>
         *         <password>123456</password>
         *     </et2004>
         * </soap:Header>
         *
         * @param message
         * @throws Fault
         */
      @Override
      public void handleMessage(SoapMessage message) throws Fault {
        // 先获取et2004 Header元素
        Header header = message.getHeader(new QName("et2004"));
        if(header == null) {
          throw new Fault(new RuntimeException("请传入用户名和密码"));
        }
    
        Element et2004 =  (Element)header.getObject();
    
        // 获取name元素节点
        NodeList nameNode = et2004.getElementsByTagName("name");
        if (nameNode == null || nameNode.getLength() != 1) {
          throw new Fault(new RuntimeException("name格式不正确"));
        }
    
        // 获取password元素节点
        NodeList pwdNode = et2004.getElementsByTagName("password");
        if (pwdNode == null || pwdNode.getLength() != 1) {
          throw new Fault(new RuntimeException("password格式不正确"));
        }
    
        String name = nameNode.item(0).getTextContent();
        String password = pwdNode.item(0).getTextContent();
    
        if(StringUtils.equals(name, "zs") &&
           StringUtils.equals(password, "123456")) {
          System.out.println("验证成功");
          return;
        } else {
          throw new Fault(new RuntimeException("用户名或密码错误"));
        }
      }
    }
    
    
  2. 配置拦截器

    <jaxws:server address="/user"
                  serviceClass="com.etoak.service.UserService">
      <jaxws:serviceBean>
        <bean class="com.etoak.service.impl.UserServiceImpl" />
      </jaxws:serviceBean>
    
      <!-- 服务端配置In拦截器 -->
      <jaxws:inInterceptors>
        <bean class="org.apache.cxf.interceptor.LoggingInInterceptor" />
        <bean class="com.etoak.interceptor.AuthInInterceptor" />
      </jaxws:inInterceptors>
      <!-- 服务端配置Out拦截器 -->
      <jaxws:outInterceptors>
        <bean class="org.apache.cxf.interceptor.LoggingOutInterceptor" />
      </jaxws:outInterceptors>
    </jaxws:server>
    

1.5 全局拦截器

​ 全局拦截器可以为所有web service添加拦截

  1. 引入Schema

在这里插入图片描述

  1. 为所有web service配置全局拦截器

    <!-- 全局拦截器,可以为所有的web service设置拦截 -->
    <core:bus>
      <core:inInterceptors>
        <bean class="org.apache.cxf.interceptor.LoggingInInterceptor" />
        <bean class="com.etoak.interceptor.AuthInInterceptor" />
      </core:inInterceptors>
    
      <core:outInterceptors>
        <bean class="org.apache.cxf.interceptor.LoggingOutInterceptor" />
      </core:outInterceptors>
    </core:bus>
    
    

2. 整合springboot

​ 使用cxf提供的cxf-spring-boot-starter-jaxws

2.1 关于CXFServlet配置的拦截地址

  1. 自动配置的拦截地址

    不能以 /* 结尾

cxf:
   path: /ws
 或者
 cxf:
   path: /ws/
  1. 手动配置
 @Bean
 public ServletRegistrationBean<CXFServlet> cxfServletRegistration() {
   ServletRegistrationBean<CXFServlet> servletRegistrationBean = 
     new ServletRegistrationBean<>();
   servletRegistrationBean.setServlet(new CXFServlet());
   servletRegistrationBean.addUrlMappings("/ws/*");
   return servletRegistrationBean;
 }

CxfApp

@SpringBootApplication
public class CxfApp {
    public static void main(String[] args) {
        SpringApplication.run(CxfApp.class);
    }
    @Autowired
    HelloService helloService;
    @Autowired
    Bus bus;
    //第一种发布服务方式
    @Bean
    public JaxWsServerFactoryBean helloService(){
        JaxWsServerFactoryBean factoryBean = new JaxWsServerFactoryBean();
        factoryBean.setAddress("/hello");
        factoryBean.setServiceClass(HelloService.class);
        factoryBean.setServiceBean(helloService);
        factoryBean.getInInterceptors().add(new LoggingInInterceptor());
        Server server = factoryBean.create();
        server.start();
        return factoryBean;
    }
    //第二种发布服务方式
    @Bean
    public EndpointImpl endpoint(){
        //添加全局拦截 给所有的服务添加out拦截
        bus.getOutFaultInterceptors().add(new LoggingOutInterceptor());
        EndpointImpl endpoint = new EndpointImpl(bus,helloService);
        endpoint.publish("/hello2");
        return endpoint;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值