手把手教你SSM框架开发WebService服务

Posted 小仇哥

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了手把手教你SSM框架开发WebService服务相关的知识,希望对你有一定的参考价值。

手把手教你SSM框架中开发WebService服务

手把手教你SSM框架中开发WebService服务

最近在做一个智能门禁的项目,需要把组织架构和人员信息同步到门禁系统,所以需要开发一个webservice服务供数据平台这边来调用同步数据~

一、Webservice简单介绍

  1. WebService,顾名思义就是基于Web的服务。它使用Web(HTTP)方式,接收和响应外部系统的某种请求,从而实现两个系统的远程调用,使两个系统进行数据交互,如应用:天气预报服务、银行ATM取款、使用邮箱账号登录各网站等。
  2. WebService简单来说就是一个公开的接口,就像是现在很多的天气应用,我们可以调用互联网上查询天气信息Web服务,然后将它嵌入到我们的程序(C/S或B/S程序)当中来,当用户从我们的网站看到天气信息时,他会认为我们为他提供了很多的信息服务,但其实我们什么也没有做,只是简单调用了一下服务器上的一段代码而已。他们之间的调用是跨语言的调用,Java、.Net、php,发送Http请求,使用的数据格式是XML格式。

二、Webservice相关术语

  1. XML. Extensible Markup Language -扩展性标记语言XML,用于传输格式化的数据,是Web服务的基础。
  2. namespace-命名空间 xmlns=“http://itcast.cn” 使用默认命名空间 xmlns:itcast=“http://itcast.cn”使用指定名称的命名空间。
  3. WSDL – WebService Description Language – Web服务描述语言。通过XML形式说明服务在什么地方-地址。通过XML形式说明服务提供什么样的方法 – 如何调用。
  4. SOAP-Simple Object Access Protocol(简单对象访问协议) Envelope – 必须的部分。以XML的根元素出现。 Headers – 可选的。Body – 必须的。在body部分,包含要执行的服务器的方法。和发送到服务器的数据。SOAP作为一个基于XML语言的协议用于有网上传输数据。SOAP = 在HTTP的基础上+XML数据。SOAP是基于HTTP的。

三、SSM框架下整合cxf

1. 第一步加入maven依赖

<!--引入CXF 用于发布webservice服务 by czc 2020-06-16 17:40 -->
          <dependency>
               <groupId>org.apache.cxf</groupId>
               <artifactId>cxf-rt-frontend-jaxws</artifactId>
               <version>3.2.10</version>
          </dependency>
          <dependency>
               <groupId>org.apache.cxf</groupId>
               <artifactId>cxf-core</artifactId>
               <version>3.2.10</version>
          </dependency>
          <dependency>
               <groupId>org.apache.cxf</groupId>
               <artifactId>cxf-rt-transports-http</artifactId>
               <version>3.2.10</version>
          </dependency>
          <dependency>
               <groupId>org.apache.cxf</groupId>
               <artifactId>cxf-rt-databinding-jaxb</artifactId>
               <version>3.2.10</version>
          </dependency>
          <dependency>
               <groupId>org.apache.cxf</groupId>
               <artifactId>cxf-rt-transports-http-jetty</artifactId>
               <version>3.2.10</version>
          </dependency>
          <dependency>
               <groupId>org.apache.cxf</groupId>
               <artifactId>cxf-rt-frontend-simple</artifactId>
               <version>3.0.4</version>
          </dependency>
          <dependency>
               <groupId>org.apache.cxf</groupId>
               <artifactId>cxf-rt-wsdl</artifactId>
               <version>3.2.10</version>
          </dependency>
          <dependency>
               <groupId>org.apache.cxf</groupId>
               <artifactId>cxf-rt-bindings-soap</artifactId>
               <version>3.2.10</version>
          </dependency>
          <dependency>
               <groupId>org.apache.ws.commons.schema</groupId>
               <artifactId>xmlschema-core</artifactId>
               <version>2.2.1</version>
          </dependency>
          <dependency>
               <groupId>org.eclipse.jetty</groupId>
               <artifactId>jetty-util</artifactId>
               <version>9.4.11.v20180605</version>
          </dependency>
          <dependency>
               <groupId>org.eclipse.jetty</groupId>
               <artifactId>jetty-server</artifactId>
               <version>9.4.11.v20180605</version>
          </dependency>
          <dependency>
               <groupId>org.eclipse.jetty</groupId>
               <artifactId>jetty-security</artifactId>
               <version>9.4.11.v20180605</version>
          </dependency>
          <dependency>
               <groupId>org.eclipse.jetty</groupId>
               <artifactId>jetty-io</artifactId>
               <version>9.4.11.v20180605</version>
          </dependency>
          <dependency>
               <groupId>org.eclipse.jetty</groupId>
               <artifactId>jetty-http</artifactId>
               <version>9.4.11.v20180605</version>
          </dependency>
          <dependency>
               <groupId>org.eclipse.jetty</groupId>
               <artifactId>jetty-continuation</artifactId>
               <version>9.4.11.v20180605</version>
          </dependency>
          <dependency>
               <groupId>org.codehaus.woodstox</groupId>
               <artifactId>stax2-api</artifactId>
               <version>3.1.4</version>
          </dependency>
          <dependency>
               <groupId>org.codehaus.woodstox</groupId>
               <artifactId>woodstox-core-asl</artifactId>
               <version>4.1.4</version>
          </dependency>
          <dependency>
               <groupId>com.thoughtworks.xstream</groupId>
               <artifactId>xstream</artifactId>
               <version>1.4.12</version>
          </dependency>

相关依赖比较多,最初其实只添加了几个核心的,但是在运行的时候抛异常了,所以一步步增加了其他依赖,这里大家根据自己实际情况来就行~

2. web.xml配置
接着我们需要配置一个CXF的servlet:

<!-- 定义一个cxf的servlet by czc 2020-06-16 17:45 -->
     <servlet>
          <servlet-name>CXFServlet</servlet-name>
          <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
     </servlet>
     <servlet-mapping>
          <servlet-name>CXFServlet</servlet-name>
          <url-pattern>/webserviceProxy/*</url-pattern>
     </servlet-mapping>

之后只要我们访问webserviceProxy/*这个地址就会进入CXF的servlet中。

3. 整合Spring配置
这里有三个主要类

  • 实体类 UserBean
  • IHrmDataSynchronizedService接口。
  • 实现IHrmDataSynchronizedService 接口的HrmDataSynchronizedService 类。
    部分代码如下:
  • UserBean.java—>
import java.io.Serializable;
/**
 * 员工信息数据同步类
 * @author czc
 */
public class UserBean implements Serializable 
    private static final long serialVersionUID = 1L;
    private String orgCode; // 组织机构编码
    private String uid; // 员工编号
    private String realName; // 员工姓名
    
 	....省略get() set()方法......
 	
  • IHrmDataSynchronizedService—>
import javax.jws.WebService;
/**
 * @author czc
 * @version 1.0
 * @desc 同步 user,org的webService接口 当项目 启动的时候新建一个线程进行发布
 * @Date 2020-6-1 10:46
 */
@WebService(targetNamespace = "http://service.face.ai.XXX/")
public interface IHrmDataSynchronizedService 
    /**
     * 给业务系统调用接口将新用户信息同步给业务系统
     * @param xmlData
     * @return
     * @throws Exception
     */
    String createUsers(String xmlData) throws Exception;
    .....其他方法略.....
    
  • HrmDataSynchronizedService
@Component("hrmDataSynchronizedService")
@WebService(serviceName = "HrmDataSynchronizedService",endpointInterface = "package.IHrmDataSynchronizedService",targetNamespace = "http://service.face.ai.XXX/")
public class HrmDataSynchronizedService implements IHrmDataSynchronizedService 
    private static Logger logger = LoggerFactory.getLogger(HrmDataSynchronizedService.class);
    private static final String hrm_wdsl = ProjectEnvProp.get("face.service.hrm.wdsl");
    public HrmDataSynchronizedService() throws UnknownHostException 
        super();
        // webservice注册方法 在tomcat里面需要打开 在weblogic里面不需要 可以自动发布
        // http://localhost:8888/webserviceProxy/HrmDataSynchronizedService?wsdl
        // weblogic默认地址=ip+port+rootName+serviceName?wsdl
	    // InetAddressUtils inetAddressUtils = new InetAddressUtils();
		// String hrm_wdsl = "http://"+inetAddressUtils.getLocalIp()+":8888/webserviceProxy/HrmDataSynchronizedService"; // 自动获取ip
        logger.info("webService is starting .........................................");
        Endpoint.publish(hrm_wdsl,this);
        logger.info("webService is started .........................................");
        logger.info("webservice url:"+ hrm_wdsl);
    
    /**
     * @Description: 创建用户
     * @Param: [xmlData]
     * @return: java.lang.String
     * @Author: czc
     * @Date: 2020-6-5 10:24
     */
    @Override
    public String createUsers(String xmlData) throws Exception 
        logger.info("创建用户报文:\\n"+xmlData);
        CommonResp addPerson;
        try
            // 将xml转化为javaBean对象 方便进行转化
            List<UserBean> users = (List<UserBean>) XstreamUtils.toObject(xmlData,new Class[]UserBean.class,"users");
            // 要返回的结果对象集合
            List<UserResult> userResults = new ArrayList<UserResult>();
            UserResult result;
            // 循环每一个对象
            for (UserBean user:users) 
                result = new UserResult();
                result.setUid(user.getUid()); // 员工工号
                // 发接口 根据人员编号获取单个人员信息
               ------省略调用第三方接口查询方法------
                if (personInfoResp != null) 
                    result.setCode(HrmATTResult.API_USER_EXIST);
                    result.setMsg("用户创建失败,该用户已存在!");
                 else 
                    ------省略调用第三方接口创建方法------
                    String code = addPerson.getCode();
                    if (code.equals("0")) // 返回码,0 – 成功,其他- 失败
                         ------省略调用第三方接口创建人脸方法------
                        result.setCode(HrmATTResult.API_CREATE_USER_SUC);
                        result.setMsg("用户创建成功!");
                    else
                        logger.info("用户创建失败,失败原因为:"+addPerson.getMsg());
                        result.setCode(HrmATTResult.API_OTHER_ERR);
                        result.setMsg("用户创建失败,失败原因为:"+addPerson.getMsg());
                    
                
                // 将结果添加到集合中
                userResults.add(result);
            
            String resultXml = XstreamUtils.toXml(userResults, new Class[] UserResult.class , "results");
            logger.info("创建用户返回报文:\\n" + resultXml);
            return resultXml;
        catch (Exception e)
            logger.error(e.getMessage());
            throw e;
        
    

这里是对createUser方法的实现,其中涉及xml转object,object转xml等在这里不再赘述~
接下来就是整合Spring了,由于需要使用到CXF的标签,所以我们需要添加额外的命名路径如下:

<?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:aop="http://www.springframework.org/schema/aop"
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:tx="http://www.springframework.org/schema/tx"
        xmlns:jaxws="http://cxf.apache.org/jaxws"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
                              http://www.springframework.org/schema/beans/spring-beans.xsd
                              http://www.springframework.org/schema/aop 
                              http://www.springframework.org/schema/aop/spring-aop.xsd
                              http://www.springframework.org/schema/context 
                              http://www.springframework.org/schema/context/spring-context.xsd
                              http://www.springframework.org/schema/tx 
                              http://www.springframework.org/schema/tx/spring-tx.xsd
                              http://cxf.apache.org/jaxws
                              http://cxf.apache.org/schemas/jaxws.xsd">
     <!-- 启用注解 -->
    <context:annotation-config />
     <!-- 加载sprin-cxfg配置文件 by czc 2020-06-16 21:45-->
     <import resource="classpath:META-INF/cxf/cxf.xml"/>
     <import resource="classpath:META-INF/cxf/cxf-servlet.xml"/>
     <!--自动扫描webservice-->
     <context:component-scan base-package="com.zjtlcb.cloudplat.module.front.ai.face.service"/>
     <!--定义webservice发布接口-->
     <jaxws:endpoint implementor="#hrmDataSynchronizedService" address="/HrmDataSynchronizedService" />
</beans>

#hrmDataSynchronizedService指的是我们在HrmDataSynchronizedService类中所自定义的名字,/HrmDataSynchronizedService则是我们需要访问的地址。
之后我们运行发现启动报Cannot create a secure XMLInputFactory异常了(这个是XML问题,也记载一下)~~~~

 <!-- CXF监听器 解决Cannot create a secure XMLInputFactory问题 by czc 2020-06-22 10:45 -->
     <listener>
          <listener-class>XXX.front.ai.face.utils.SealManageListener</listener-class>
     </listener>
  • 自定义SealManageListener类
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import java.util.Properties;
/**
 * @author czc
 * @version 1.0
 * @desc  解决Cannot create a secure XMLInputFactory问题
 * @Date 2020-6-22 10:08
 */
public class SealManageListener implements ServletContextListener
    @Override
    public void contextInitialized(ServletContextEvent sce) 
        Properties props=  System.getProperties();
        props.setProperty("org.apache.cxf.stax.allowInsecureParser","1");
        props.setProperty("UseSunHttpHandler", "true");
    
    @Override
    public void contextDestroyed(ServletContextEvent sce) 
    

解决完相关异常后我们运行项目输入该地址:http://127.0.0.1:8080/ssm/webserviceProxy/HrmDataSynchronizedService?wsdl

如果出现如下界面:

则说明我们的webservice发布成功了。
接下来只需要通过客户端调用这个接口即可获得返回结果了。。。。。

四、客户端

  1. 把上面打开的wsdl页面另存为.wsdl文件,然后放到Java工程里,然后file-new-webservice client,wsdl地址在工程中选择刚刚的.wsdl文件即可,这样我们就可以生成客户端代理类了,写一个main()方法就可以完成调用~

  2. Wsimport实现方式

概念理解:
WSDL – WebService Description Language – Web服务描述语言。
通过XML形式说明服务在什么地方-地址。address location
通过XML形式说明服务提供什么样的方法 – 如何调用。operation
注意:该种方式使用简单,但一些关键的元素在代码生成时写死到生成代码中,不方便维护,所以仅用于测试。

实现方式:
(1)Wsimport命令介绍
Wsimport就是jdk(1.6版本之后)提供的的一个工具,他的作用就是根据WSDL地址生成客户端代码;
Wsimport位置JAVA_HOME/bin
Wsimport常用的参数:
-s,生成java文件的
-d,生成class文件的,默认的参数
-p,指定包名的,如果不加该参数,默认包名就是wsdl文档中的命名空间的倒序;
public class WebserviceClient 
    public static void main(String[] args) 
         //创建服务访问点集合的对象 ,wsdl文档中:<wsdl:service name="HrmDataSynchronizedService">
        HrmDataSynchronizedService hrmDataSynchronizedService = new HrmDataSynchronizedService(); 
        //获取服务实现类,wsdl中:<wsdl:portType name="IHrmDataSynchronizedService">
        IHrmDataSynchronizedService iHrmDataSynchronizedService = iHrmDataSynchronizedService.getPort(IHrmDataSynchronizedService.class);
        //根据服务访问点的集合中的服务访问点的绑定对象来获得绑定的服务类
        HrmDataSynchronizedServiceSoapBinding hrmDataSynchronizedServiceSoapBinding = iHrmDataSynchronizedService.getHrmDataSynchronizedServiceSoapBinding();
        Object users = mobileCodeWSSoap.createUsers(args);
        System.out.println(users);
    


其他比如Axis、service等调用方式网上一大堆,这里也不再赘述~

五、使用CXF时因缺少对应jar包而报错问题汇总

org.apache.catalina.LifecycleException: A child Container failed during start
缺少:cxf-core-x.x.x.jar(最核心的包)

Java.lang.ClassNotFoundException: org.apache.cxf.binding.soap.SoapBindingConfiguration
缺少:cxf-rt-bindings-soap-x.x.x.jar (soap协议)

org.apache.cxf.jaxb.JAXBDataBinding:
缺少:cxf-rt-databinding-jaxb-x.x.x.jar

加载Application文件时出现Unable to locate Spring NamespaceHandler for XML schema namespace
[http://cxf.apache.org/jaxws]
缺少:cxf-rt-frontend-jaxws-x.x.x.jar

org/apache/cxf/frontend/spring/ClientProxyFactoryBeanDefinitionParser错误:
缺少:cxf-rt-frontend-simple-x.x.x.jar

class path resource [META-INF/cxf/cxf-servlet.xml] cannot be opened because it does not exist
缺少:cxf-rt-transports-http-x.x.x.jar

org.apache.cxf.BusException: No DestinationFactory was found for the namespace http://cxf.apache.org/transports/udp.
缺少:cxf-rt-transports-udp-x.x.x.jar

org.apache.cxf.ws.discovery.internal.WSDiscoveryServiceImpl startup
警告: Could not start WS-Discovery Service.
javax.xml.ws.WebServiceException: java.lang.NullPointerException
不能初始化配置的bean服务,也就是运行到时出错
缺少:cxf-rt-ws-addr-x.x.x.jar

java.lang.ClassNotFoundException: org.apache.cxf.ws.policy.AssertionInfoMap
缺少:cxf-rt-ws-policy-x.x.x.jar

java.lang.ClassNotFoundException: org.apache.cxf.wsdl.service.factory.ReflectionServiceFactoryBean
缺少:cxf-rt-wsdl-x.x.x.jar

java.lang.ClassNotFoundException: org.apache.neethi.AssertionBuilderFactory
缺少:neethi-x.x.x.jar

java.lang.ClassNotFoundException: org.slf4j.LoggerFactory
缺少:slf4j-api-x.x.x.jar

java.lang.ClassNotFoundException: org.apache.ws.commons.schema.resolver.URIResolver
缺少:xmlschema-core-x.x.x.jar

总结

没太多需要总结的了,因为第一次做,踩过不少缺jar包的坑,命名空间的坑,各种的,遇到了,逐一解决就好,总体上不难的~

以上是关于手把手教你SSM框架开发WebService服务的主要内容,如果未能解决你的问题,请参考以下文章

手把手教你SOAP访问webservice并DOM解析返回的XML数据(转)

手把手教你用Java实现一套简单的鉴权服务(SpringBoot,SSM)(万字长文)

手把手教你用Java实现一套简单的鉴权服务(SpringBoot,SSM)(未完待续...)

SSM springmvc mybaits websocket 服务器框架

一小时:手把手教你入门express建议收藏

一小时:手把手教你入门express建议收藏