web service学习笔记

it2023-10-10  64

0. web service内容

web service是什么web service使用场景web service三要素java的web service规范使用jdk方式发布web service使用cxf方式发布web servicecxf整合spring webcxf拦截器

1. web service是什么

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

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

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

2. web service使用场景

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

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

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

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

在这里插入图片描述

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

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

4. web service规范

JAX-WS规范

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

JAX-RS规范

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

5. 使用jdk方式开发web service

5.1 服务端

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

开发服务实现

发布web service服务

注意:

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

5.2 开发jdk方式的客户端

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

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

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

发起远程调用

测试使用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开发服务端 和客户端

开发服务端 创建服务接口创建服务实现发布服务 开发客户端 根据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查询用户信息

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

服务器端 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 拦截器分类

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

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

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

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

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

1.2 拦截器的几个API

​ Interceptor、PhaseInterceptor、SoapInterceptor

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

​ 拦截阶段定义在了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 客户端创建、配置(使用)自定义拦截器

创建拦截器

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)); } }

配置拦截器

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

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

响应的soap报文

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

创建拦截器

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("用户名或密码错误")); } } }

配置拦截器

<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添加拦截

引入Schema

为所有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配置的拦截地址

自动配置的拦截地址

不能以 /* 结尾

cxf: path: /ws 或者 cxf: path: /ws/ 手动配置 @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; } }
最新回复(0)