当前位置:   article > 正文

springboot整合webservice使用总结_webservices接口在springboot项目中的使用

webservices接口在springboot项目中的使用

        因为做的项目中用到了webservice,所以在此总结一下。

        一、webservice简介

        Web Service也叫XML Web Service, WebService是一种可以接收从Internet或者Intranet上的其它系统中传递过来的请求,轻量级的独立的通讯技术。是通过SOAP在Web上提供的软件服务,使用WSDL文件进行说明,并通过UDDI进行注册。WebService是一种跨编程语言和跨操作系统平台的远程调用技术。
        还可以从多个角度来理解WebService,从表面看,WebService就是一个应用程序向外界暴露出一个能通过Web进行调用的API,也就是说能用编程的方法通过Web来调用这个应用程序。我们把调用这个WebService的应用程序叫做客户端,而把提供这个WebService的应用程序叫做服务端。从深层次看,WebService是建立可互操作的分布式应用程序的新平台,是一个平台,是一套标准。它定义了应用程序如何在Web上实现互操作性,你可以用任何你喜欢的语言,在任何你喜欢的平台上写Web service ,只要我们可以通过Web service标准对这些服务进行查询和访问。

        二、webservice三要素:

        SOAP、WSDL、UDDI(UniversalDescriptionDiscovery andIntegration)三者构成了WebService的三要素。下面详细阐述这三大技术:

SOAP:

WebService通过HTTP协议发送请求和接收结果时,发送的请求内容和结果内容都采用XML格式封装,并增加了一些特定的HTTP消息头,以说明HTTP消息的内容格式,这些特定的HTTP消息头和XML内容格式就是SOAP协议。SOAP提供了标准的RPC(远程调用技术)方法来调用Web Service。

SOAP协议组成:

SOAP协议 = HTTP协议 + XML数据格式

SOAP协议定义了SOAP消息的格式,SOAP协议是基于HTTP协议的,SOAP也是基于XML的,XML是SOAP的数据编码方式。

WSDL

好比我们去商店买东西,首先要知道商店里有什么东西可买,然后再来购买,商家的做法就是张贴广告海报。 WebService也一样,WebService客户端要调用一个WebService服务,首先要有知道这个服务的地址在哪,以及这个服务里有什么方法可以调用,所以,WebService务器端首先要通过一个WSDL文件来说明自己家里有啥服务可以对外调用,服务是什么(服务中有哪些方法,方法接受的参数是什么,返回值是什么),服务的网络地址用哪个url地址表示,服务通过什么方式来调用。

WSDL(Web Services Description Language)就是这样一个基于XML的语言,用于描述Web Service及其函数、参数和返回值。它是WebService客户端和服务器端都能理解的标准格式。因为是基于XML的,所以WSDL既是机器可阅读的,又是人可阅读的,这将是一个很大的好处。一些最新的开发工具既能根据你的Web service生成WSDL文档,又能导入WSDL文档,生成调用相应WebService的代理类代码。

UDDI

uddi是一个跨产业,跨平台的开放性架构,可以帮助 Web 服务提供商在互联网上发布 Web 服务的信息。UDDI 是一种目录服务,企业可以通过 UDDI 来注册和搜索 Web 服务。简单来说,UDDI 就是一个目录,只不过在这个目录中存放的是一些关于 Web 服务的信息而已。

也就是说:
        soap:就是在与webservice通信时规定好的协议
        wsdl:就是webservice中的食谱,你可以找到webservice中有哪些材料(方法),怎去做这道菜(入参、方法、返回值)等
        uddi:wsdl是食谱的话,uddi就是菜单,可以用来注册和搜索web服务。

三、为什么要使用WebService

  1. 跨平台调用
  2. 跨语言调用
  3. 远程调用

四、springboot简单调用案例

1、引入依赖:
  1. <!-- CXF webservice -->
  2. <dependency>
  3. <groupId>org.apache.cxf</groupId>
  4. <artifactId>cxf-spring-boot-starter-jaxws</artifactId>
  5. <version>3.4.5</version>
  6. </dependency>
2、创建实体类:
  1. @Data
  2. @Builder
  3. public class UserDto {
  4. private Long id;
  5. private String name;
  6. private Integer age;
  7. private String address;
  8. }
3、创建WebService接口
  1. public interface IUserServer {
  2. UserDto getUser(Long str)
  3. }
4、创建WebService接口的实现类
  1. @Service
  2. @WebService
  3. public class UserServerImpl implements IUserServer {
  4. @Override
  5. public UserDto getUser(Long id) {
  6. return UserDto.builder()
  7. .id(id)
  8. .address("上海市浦东新区")
  9. .age(25)
  10. .name("laJi").build();
  11. }
  12. }

这里用到了注解@WebService,我这就只在实现类上使用了。这里介绍一下,先来看下它的定义:

  1. @Retention(RetentionPolicy.RUNTIME)
  2. @Target({ElementType.TYPE})
  3. public @interface WebService {
  4.     String name() default "";
  5.     String targetNamespace() default "";
  6.     String serviceName() default "";
  7.     String portName() default "";
  8.     String wsdlLocation() default "";
  9.     String endpointInterface() default "";
  10. }

name: 对应wsdl:portType标签,默认值为Java类或接口的名称;
targetNamespace: 命名空间,一般写为接口的包名倒序,默认值也是接口的包名倒序。对应wsd:definitions:targetNamespace 标签;
serviceName: WebService的服务名称,对应wsdl:service,默认值为WebService接口实现类的名称+“Service”,示例:UserServiceImplServicce
portName: 对应wsdl:port标签,默认值为:WebService接口实现类的名称+“Port”,示例:UserServiceImplPort
wsdlLocation: 指定用于定义WebService的WSDL文档的地址
endpoointInterfacce: WebService接口全路径

5、创建WebService配置类
  1. @Configuration
  2. @RequiredArgsConstructor
  3. public class CxfConfig {
  4. private final IUserServer userServer;
  5. /**
  6. * 注入Servlet,注意beanName不能为dispatcherServlet
  7. * @author Fang Ruichuan
  8. * @date 2022/11/14 19:16
  9. */
  10. @Bean
  11. public ServletRegistrationBean cxfServlet() {
  12. return new ServletRegistrationBean(new CXFServlet(), "/webservice/*");
  13. }
  14. @Bean(name = Bus.DEFAULT_BUS_ID)
  15. public SpringBus springBus() {
  16. return new SpringBus();
  17. }
  18. @Bean
  19. public Endpoint endpoint() {
  20. EndpointImpl endpoint = new EndpointImpl(springBus(), userServer);
  21. endpoint.publish("/api");
  22. return endpoint;
  23. }
  24. }

进行访问:http://localhost:8080/webservice
在这里插入图片描述
然后点击url
在这里插入图片描述

客户端:

  1. public class WebserviceClient {
  2.     public static void main(String[] args) {
  3.         JaxWsDynamicClientFactory dcf = JaxWsDynamicClientFactory.newInstance();
  4.         Client client = dcf.createClient("http://localhost:8080/webservice/api?wsdl");
  5.         final ObjectMapper mapper = new ObjectMapper();
  6.         try {
  7.             Object[] objects = client.invoke("getUser", 99L);
  8.             System.out.println(mapper.writeValueAsString(objects[0]));
  9.         } catch (Exception e) {
  10.             e.printStackTrace();;
  11.         }
  12.     }
  13. }

显示:

{"address":"上海市浦东新区","age":25,"id":99,"name":"laJi"}

五、进阶使用:

        webservice的服务端是以远程接口为主的,在java实现的webService主要是依靠的是CXF开发框架,而这个CXF框架可以直接将接口发布成webservice,同时还要考虑到整个项目之中的安全性问题。

相关注解:
1)@WebService

        1、serviceName: 对外发布的服务名,指定 Web Service 的服务名称:wsdl:service。缺省值为 Java 类的简单名称 + Service。(字符串)
        2、endpointInterface: 服务接口全路径, 指定做SEI(Service EndPoint Interface)服务端点接口,包名+实现类的接口的完整路径
        3、name:此属性的值包含XML Web Service的名称。在默认情况下,该值是实现XML Web Service的类的名称,wsdl:portType 的名称。缺省值为 Java 类的简单名称 + Service。(字符串)
        4、portName:  wsdl:portName。缺省值为 WebService.name+Port。
        5、targetNamespace:指定你想要的名称空间,默认是使用接口实现类的包名的反缀
        6、wsdlLocation:指定用于定义 Web Service 的 WSDL 文档的 Web 地址。Web 地址可以是相对路径或绝对路径。(字符串)

    注意:实现类上可以不添加Webservice注解  

2)@WebMethod

    注释表示作为一项 Web Service 操作的方法,将此注释应用于客户机或服务器服务端点接口(SEI)上的方法,或者应用于 JavaBeans 端点的服务器端点实现类。

    要点: 仅支持在使用 @WebService 注释来注释的类上使用 @WebMethod 注释
        1、operationName:指定与此方法相匹配的wsdl:operation 的名称。缺省值为 Java 方法的名称。(字符串)
        2、action:定义此操作的行为。对于 SOAP 绑定,此值将确定 SOAPAction 头的值。缺省值为 Java 方法的名称。(字符串)
        3、exclude:指定是否从 Web Service 中排除某一方法。缺省值为 false。(布尔值)  

3)@Oneway 

        注释将一个方法表示为只有输入消息而没有输出消息的 Web Service 单向操作。
        将此注释应用于客户机或服务器服务端点接口(SEI)上的方法,或者应用于 JavaBeans 端点的服务器端点实现类  

4)@WebParam 

        注释用于定制从单个参数至 Web Service 消息部件和 XML 元素的映射。
        将此注释应用于客户机或服务器服务端点接口(SEI)上的方法,或者应用于 JavaBeans 端点的服务器端点实现类。
        1、name :参数的名称。如果操作是远程过程调用(RPC)类型并且未指定partName 属性,那么这是用于表示参数的 wsdl:part 属性的名称。如果操作是文档类型或者参数映射至某个头,那么 -name 是用于表示该参数的 XML 元素的局部名称。如果操作是文档类型、参数类型为 BARE 并且方式为 OUT 或 INOUT,那么必须指定此属性。(字符串)   
        2、partName:定义用于表示此参数的 wsdl:part属性的名称。仅当操作类型为 RPC 或者操作是文档类型并且参数类型为BARE 时才使用此参数。(字符串)
        3、targetNamespace:指定参数的 XML 元素的 XML 名称空间。当属性映射至 XML 元素时,仅应用于文档绑定。缺省值为 Web Service 的targetNamespace。(字符串)
        4、mode:此值表示此方法的参数流的方向。有效值为 IN、INOUT 和 OUT。(字符串)
        5、header:指定参数是在消息头还是消息体中。缺省值为 false。(布尔值)

5)@WebResult 

        注释用于定制从返回值至 WSDL 部件或 XML 元素的映射。将此注释应用于客户机或服务器服务端点接口(SEI)上的方法,或者应用于 JavaBeans 端点的服务器端点实现类。
        1、name:当返回值列示在 WSDL 文件中并且在连接上的消息中找到该返回值时,指定该返回值的名称。对于 RPC 绑定,这是用于表示返回值的 wsdl:part属性的名称。对于文档绑定,-name参数是用于表示返回值的 XML 元素的局部名。对于 RPC 和 DOCUMENT/WRAPPED 绑定,缺省值为 return。对于 DOCUMENT/BARE 绑定,缺省值为方法名 + Response。(字符串)
        2、targetNamespace:指定返回值的 XML 名称空间。仅当操作类型为 RPC 或者操作是文档类型并且参数类型为 BARE 时才使用此参数。(字符串) 
        3、header:指定头中是否附带结果。缺省值为false。(布尔值)
        4、partName:指定 RPC 或 DOCUMENT/BARE 操作的结果的部件名称。缺省值为@WebResult.name。(字符串)

6)@HandlerChain

         注释用于使 Web Service 与外部定义的处理程序链相关联。只能通过对 SEI 或实现类使用 @HandlerChain 注释来配置服务器端的处理程序。
        但是可以使用多种方法来配置客户端的处理程序。可以通过对生成的服务类或者 SEI 使用 @HandlerChain 注释来配置客户端的处理程序。此外,可以按程序在服务上注册您自己的 HandlerResolver 接口实现,或者按程序在绑定对象上设置处理程序链。
        1、file:指定处理程序链文件所在的位置。文件位置可以是采用外部格式的绝对 java.net.URL,也可以是类文件中的相对路径。(字符串)
        2、name:指定配置文件中处理程序链的名称。

/**
 * WebService涉及到的有这些 "四解三类 ", 即四个注解,三个类
 * @WebMethod
 * @WebService
 * @WebResult
 * @WebParam
 * SpringBus
 * Endpoint
 * EndpointImpl
 *
 * 一般我们都会写一个接口,然后再写一个实现接口的实现类,但是这不是强制性的
 * @WebService 注解表明是一个webservice服务。
 *      name:对外发布的服务名, 对应于<wsdl:portType name="ServerServiceDemo"></wsdl:portType>
 *      targetNamespace:命名空间,一般是接口的包名倒序, 实现类与接口类的这个配置一定要一致这种错误
 *              Exception in thread "main" org.apache.cxf.common.i18n.UncheckedException: No operation was found with the name xxxx
 *              对应于targetNamespace="http://server.webservice.example.com"
 *      endpointInterface:服务接口全路径(如果是没有接口,直接写实现类的,该属性不用配置), 指定做SEI(Service EndPoint Interface)服务端点接口
 *      serviceName:对应于<wsdl:service name="ServerServiceDemoImplService"></wsdl:service>
 *      portName:对应于<wsdl:port binding="tns:ServerServiceDemoImplServiceSoapBinding" name="ServerServiceDemoPort"></wsdl:port>
 *
 * @WebMethod 表示暴露的服务方法, 这里有接口ServerServiceDemo存在,在接口方法已加上@WebMethod, 所以在实现类中不用再加上,否则就要加上
 *      operationName: 接口的方法名
 *      action: 没发现又什么用处
 *      exclude: 默认是false, 用于阻止将某一继承方法公开为web服务
 *
 * @WebResult 表示方法的返回值
 *      name:返回值的名称
 *      partName:
 *      targetNamespace:
 *      header: 默认是false, 是否将参数放到头信息中,用于保护参数,默认在body中
 *
 * @WebParam
 *       name:接口的参数
 *       partName:
 *       targetNamespace:
 *       header: 默认是false, 是否将参数放到头信息中,用于保护参数,默认在body中
 *       model:WebParam.Mode.IN/OUT/INOUT
 */

使用步骤:
        1)创建接口:
  1. @WebService(name = "UserSSSS" ,targetNamespace ="http://data.fucker.mother.com")
  2. public interface UserService {
  3. User getUser(String userName);
  4. }
        2)创建实现类:
  1. @Service
  2. @WebService(name = "UserSSSS" ,
  3. targetNamespace ="http://data.fucker.mother.com",
  4. endpointInterface = "com.mother.fucker.data.service.UserService")
  5. public class UserServerImpl implements UserService {
  6. @Override
  7. @WebMethod(operationName = "getUser")
  8. @WebResult(name = "User")
  9. public User getUser(@WebParam(name = "userName") String userName) {
  10. return new User("姓名","年龄","性别","工作");
  11. }
  12. }
        3)创建身份认证拦截器:(不需要可以不创建)
  1. import lombok.extern.slf4j.Slf4j;
  2. import org.apache.cxf.binding.soap.SoapMessage;
  3. import org.apache.cxf.binding.soap.saaj.SAAJInInterceptor;
  4. import org.apache.cxf.interceptor.Fault;
  5. import org.apache.cxf.phase.AbstractPhaseInterceptor;
  6. import org.apache.cxf.phase.Phase;
  7. import org.springframework.stereotype.Component;
  8. import org.w3c.dom.NodeList;
  9. import javax.xml.soap.SOAPException;
  10. import javax.xml.soap.SOAPHeader;
  11. import javax.xml.soap.SOAPMessage;
  12. @Component
  13. @Slf4j
  14. public class WebServiceAuthInterceptor extends AbstractPhaseInterceptor<SoapMessage> {
  15. /**
  16. * 用户名
  17. */
  18. private static final String USER_NAME = "wangdi";
  19. /**
  20. * 密码
  21. */
  22. private static final String USER_PASSWORD = "wangdi.com";
  23. private static final String NAME_SPACE_URI = "http://data.fucker.mother.com";
  24. /**
  25. * 创建拦截器
  26. */
  27. private SAAJInInterceptor interceptor = new SAAJInInterceptor();
  28. public WebServiceAuthInterceptor() {
  29. super(Phase.PRE_PROTOCOL);
  30. //添加拦截
  31. super.getAfter().add(SAAJInInterceptor.class.getName());
  32. }
  33. @Override
  34. public void handleMessage(SoapMessage message) throws Fault {
  35. //获取指定消息
  36. SOAPMessage soapMessage = message.getContent(SOAPMessage.class);
  37. if (null == soapMessage) {
  38. this.interceptor.handleMessage(message);
  39. soapMessage = message.getContent(SOAPMessage.class);
  40. }
  41. //SOAP头信息
  42. SOAPHeader header = null;
  43. try {
  44. header = soapMessage.getSOAPHeader();
  45. } catch (SOAPException e) {
  46. e.printStackTrace();
  47. }
  48. if (null == header) {
  49. throw new Fault(new IllegalAccessException("没有Header信息,无法实现用户认证处理!"));
  50. }
  51. //SOAP是基于XML文件结构进行传输的,所以如果要想获取认证信息就必须进行相关的结构约定
  52. NodeList usernameNodeList = header.getElementsByTagNameNS(NAME_SPACE_URI, "username");
  53. NodeList passwordNodeList = header.getElementsByTagNameNS(NAME_SPACE_URI, "password");
  54. if (usernameNodeList.getLength() < 1) {
  55. throw new Fault(new IllegalAccessException("没有用户信息,无法实现用户认证处理!"));
  56. }
  57. if (passwordNodeList.getLength() < 1) {
  58. throw new Fault(new IllegalAccessException("没有密码信息,无法实现用户认证处理!"));
  59. }
  60. String username = usernameNodeList.item(0).getTextContent().trim();
  61. String password = passwordNodeList.item(0).getTextContent().trim();
  62. if (USER_NAME.equals(username) && USER_PASSWORD.equals(password)) {
  63. log.info("用户访问认证成功!");
  64. } else {
  65. SOAPException soapException = new SOAPException("用户认证失败!");
  66. log.info("用户认证失败!");
  67. throw new Fault(soapException);
  68. }
  69. }
  70. }
        4)创建配置类:
  1. import com.mother.fucker.data.AuthInterceptor.WebServiceAuthInterceptor;
  2. import com.mother.fucker.data.service.UserService;
  3. import lombok.RequiredArgsConstructor;
  4. import org.apache.cxf.Bus;
  5. import org.apache.cxf.bus.spring.SpringBus;
  6. import org.apache.cxf.jaxws.EndpointImpl;
  7. import org.apache.cxf.transport.servlet.CXFServlet;
  8. import org.springframework.beans.factory.annotation.Autowired;
  9. import org.springframework.boot.web.servlet.ServletRegistrationBean;
  10. import org.springframework.context.annotation.Bean;
  11. import org.springframework.context.annotation.Configuration;
  12. import javax.xml.ws.Endpoint;
  13. @Configuration
  14. @RequiredArgsConstructor
  15. public class CxfConfig {
  16. private final UserService userServer;
  17. @Autowired
  18. private WebServiceAuthInterceptor interceptor;
  19. /**
  20. * 注入Servlet,注意beanName不能为dispatcherServlet
  21. * @author Fang Ruichuan
  22. * @date 2022/11/14 19:16
  23. */
  24. @Bean
  25. public ServletRegistrationBean cxfServlet() {
  26. return new ServletRegistrationBean(new CXFServlet(), "/webservice/*");
  27. }
  28. @Bean(name = Bus.DEFAULT_BUS_ID)
  29. public SpringBus springBus() {
  30. return new SpringBus();
  31. }
  32. @Bean
  33. public Endpoint endpoint() {
  34. EndpointImpl endpoint = new EndpointImpl(springBus(), userServer);
  35. endpoint.publish("/api");
  36. endpoint.getInInterceptors().add(this.interceptor);
  37. return endpoint;
  38. }
  39. /*用于发布多个WebService*/
  40. // @Bean
  41. // public Endpoint messageEndPoint2() {
  42. // EndpointImpl endpoint = new EndpointImpl(springBus(), this.ageInfoService);
  43. // endpoint.publish("/userInfoService");
  44. endpoint.getInInterceptors().add(this.interceptor);
  45. // return endpoint;
  46. // }
  47. }
5)创建客户端拦截器:(如有需要)
  1. import org.apache.cxf.binding.soap.SoapMessage;
  2. import org.apache.cxf.helpers.DOMUtils;
  3. import org.apache.cxf.interceptor.Fault;
  4. import org.apache.cxf.phase.AbstractPhaseInterceptor;
  5. import org.apache.cxf.phase.Phase;
  6. import org.apache.cxf.headers.Header;
  7. import org.w3c.dom.Document;
  8. import org.w3c.dom.Element;
  9. import javax.xml.namespace.QName;
  10. import java.util.List;
  11. public class ClientLoginInterceptor extends AbstractPhaseInterceptor<SoapMessage> {
  12. private String username;
  13. private String password;
  14. private static final String NAME_SPACE_URI = "http://data.fucker.mother.com";
  15. public ClientLoginInterceptor(String username, String password) {
  16. super(Phase.PREPARE_SEND);
  17. this.username = username;
  18. this.password = password;
  19. }
  20. @Override
  21. public void handleMessage(SoapMessage soapMessage) throws Fault {
  22. List<Header> headers = soapMessage.getHeaders();
  23. Document document = DOMUtils.createDocument();
  24. Element authority = document.createElementNS(NAME_SPACE_URI, "authority");
  25. Element username = document.createElementNS(NAME_SPACE_URI, "username");
  26. Element password = document.createElementNS(NAME_SPACE_URI, "password");
  27. username.setTextContent(this.username);
  28. password.setTextContent(this.password);
  29. authority.appendChild(username);
  30. authority.appendChild(password);
  31. headers.add(0, new Header(new QName("authority"), authority));
  32. }
  33. }
6) 新建客户端调用接口(使用动态代理)
  1. /**
  2. * 用户名
  3. */
  4. private static final String USER_NAME = "wangdi";
  5. /**
  6. * 密码
  7. */
  8. private static final String USER_PASSWORD = "wangdi.com";
  9. private static final String NAME_SPACE_URI = "http://server.webservice.gtp.sinotrans.com";
  10. public static void main(String[] args) {
  11. //打印日志
  12. LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
  13. List<Logger> loggerList = loggerContext.getLoggerList();
  14. loggerList.forEach(logger -> {logger.setLevel(Level.INFO); });
  15. JaxWsDynamicClientFactory dcf = JaxWsDynamicClientFactory.newInstance();
  16. Client client = dcf.createClient("http://localhost:8001/webservice/api?wsdl");
  17. try {
  18. client.getOutInterceptors().add(new ClientLoginInterceptor(USER_NAME, USER_PASSWORD));
  19. Object[] objects = client.invoke("getUser","aaa");
  20. System.out.println(JSON.toJSONString(objects[0]));
  21. } catch (Exception e) {
  22. e.printStackTrace();;
  23. }
  24. }

 7)当webservcie中参数是复杂类型的是:

  1. @Override
  2. @WebMethod(operationName = "getUser")
  3. public User getUser(@WebParam(name = "userName") User userName) {
  4. return new User("姓名","年龄","性别","工作");
  5. }

需要这样调用:

  1. //打印日志
  2. // LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
  3. // List<Logger> loggerList = loggerContext.getLoggerList();
  4. // loggerList.forEach(logger -> {logger.setLevel(Level.INFO); });
  5. JaxWsDynamicClientFactory dcf = JaxWsDynamicClientFactory.newInstance();
  6. Client client = dcf.createClient("http://localhost:1001/webservice/api?wsdl");
  7. try {
  8. //通过反射,创建OrderInfo对象,com.limp.service.OrderInfo 这个包名称必须和webservice的中的order路径一致
  9. Object user = Thread.currentThread().getContextClassLoader().
  10. loadClass("com.mother.fucker.data.User").newInstance();
  11. //初始化实体对象
  12. Method m = user.getClass().getMethod("setName", String.class);
  13. m.invoke(user, "uuid213-281heq-2131");
  14. Object[] objects = client.invoke("getUser",user);
  15. System.out.println(JSON.toJSONString(objects[0]));
  16. } catch (Exception e) {
  17. e.printStackTrace();;
  18. }

本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号