zoukankan      html  css  js  c++  java
  • 如何在SpringMVC项目中部署WebService服务并打包生成客户端

    场景

    某SpringMVC项目原本为一个HTTP的WEB服务项目,之后想在该项目中添加WebService支持,使该项目同时提供HTTP服务和WebService服务。其中WebService服务通过 /ws/** 地址拦截。

    配置

    通过配置让SpringMVC支持WebService。

    依赖

    首先通过Maven引入必要依赖包。

    • org.apache.cxf
    • org.apache.neethi
    • com.ibm.wsdl4j
    • org.apache.XmlSchema

    Web.xml

    通过配置Web.xml使Spring框架具备WebService特性,这里通过添加Servlet(这里使用CXFServlet)实现。假设SpringMVC本身的DispatcherServlet已经启用,则在第2启动顺序添加CXFServlet。并添加servlet-mapping匹配请求。
    配置如下

    <!-- 在上下文中添加配置文件 -->
    <context-param>
        <param-name>patchConfigLocation</param-name>
        <param-value>
            /WEB-INF/applicationServlet.xml
            /WEB-INF/webservice.xml
        <param-value>
    </context-param>
    <!-- 添加servlet -->
    <servlet>
        <servlet-name>ws</servlet-name>
        <servlet-class>org.apache.cxf.trasport.servlet.CXFServlet</servlet-class>
        <load-on-startup>2</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>ws</servlet-name>
        <url-pattern>/ws/**</url-pattern>
    </servlet-mapping>
    

    webservice.xml

    将webservice的接口配置单独分离出来。配置如下:

    <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:jaxws="http://cxf.apache.org/jaxws"
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:mvc="http://www.springframework.org/schema/mvc"
        xsi:schemaLocation="
            http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/mvc
            http://www.springframework.org/schema/mvc/spring-mvc.xsd
            http://cxf.apache.org/jaxws
            http://cxf.apache.org/schemas/jaxws.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd">
        <!-- cxf必要配置 -->
        <import resource="classpath:META-INF/cxf/cxf.xml" />  
        <import resource="classpath:META-INF/cxf/cxf-servlet.xml" />  
        <import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" />
    
        <!-- 接口的实现类声明 -->
        <jaxws:endpoint id="ticketDecodeAuthService" 
            implementorClass="com.xxx.apps.web.ws.server.decode.XXXServiceImpl" 
            address="/ticketDecodeAuth" />
    
    </beans>

    接口编写

    对应上文声明的接口文档在写在相应的位置上(如本文例子则写在com.xxx.apps.web.ws.server.decode包中)
    代码如下:

    @WebService
    @SOAPBinding(style = Style.RPC)
    public interface XXXService {
    
        public WSReturn getAuth(String userName, String password) throws Exception;
    
    }

    接口实现类:

    @WebService
    @SOAPBinding(style = Style.RPC)
    @SuppressWarnings("deprecation")
    public class XXXServiceImpl implements XXXService {
    
        private static final Logger LOGGER = Logger.getLogger(XXXServiceImpl.class);
    
        @Override
        public WSReturn getAuth(String userName, String password) throws Exception {
            // WSReturn 是自定义的通用接口返回包装,可以用别的
            WSReturn res = new WSReturn();
            // TODO : your code here
            return res;
        }
    
    }

    发布接口效果

    启动SpringMVC项目,根据配置文件定义,接口地址类似:http://ip:port/项目名/ws/**
    若本例配置则有如下接口可以查看:

    查看所有接口列表

    http://ip:port/项目名/ws

    某具体端口(XXXService)为例

    这也是客户端调用时候的地址
    http://ip:port/项目名/ws/XXXService?wsdl
    这里可以看到端口的规范定义

    客户端编写

    客户端代码

    通过CXF的动态代理方式编写,以反射方式将class直接引入可以实现统一调用方法。这样该Client即可调用任意接口。
    代码如下:

    /**
     * webservice服务客户端
     * @author WSY
     *
     */
    public class WSClient {
    
            private static Logger logger = LoggerFactory.getLogger(WSClient.class);
    
            /**
             * 调用代理
             * @param cls 服务接口代理
             * @param method 方法名
             * @param wsdl wsdl地址
             * @param params 参数Object[]
             * @return
             * @throws Exception
             */
            @SuppressWarnings("rawtypes")
            public static WSReturn invoke(Class cls,String method,String wsdl, Object[] params) throws Exception{
                synchronized(WSClient.class){
                    logger.info("[WSClient invoking] - class:"+cls.getName()+"; method:"+method+"; wsdl:"+
                            wsdl+"; params:"+getParams(params));
    
                    JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
                    factory.getInInterceptors().add(new LoggingInInterceptor());
                    factory.getOutInterceptors().add(new LoggingOutInterceptor());
                    factory.setServiceClass(cls);
                    factory.setAddress(wsdl);
                    Object cInstance = factory.create();
                    Method invokeMethod = null;
                    for(Method m : cls.getDeclaredMethods()){
                        if(m.getName().equalsIgnoreCase(method)){
                            invokeMethod = m;
                            break;
                        }
                    }
                    if(invokeMethod == null)
                        throw new Exception("ERROR:method not found");
    
                    WSReturn res = (WSReturn) invokeMethod.invoke(cInstance, params);
                    return res;
                }
    
                private static String getParams(Object[] params){
                    StringBuilder sb = new StringBuilder("{");
                    for(Object b : params){
                        sb.append(b).append(",");
                    }
                    if(sb.length()==1)
                        return "{}";
                    else 
                        return sb.substring(0,sb.length()-1)+"}";
                }
        }
    }
    

    打包

    写个Ant脚本将一些必要的Java类和定义的Interface(不要打实现类)打成包。本文中将Client代码也写在了Service端了,所以将WSClient也一并打包进去。这样在编写对应的客户端时候,仅需专注于功能实现即可。

    <?xml version="1.0"?>
    <project name="tws-interfaces" default="jar" basedir=".">
    
        <!-- Give user a chance to override without editing this file or typing -D -->
        <property name="coredir" location="." />
        <property name="classdir" location="${basedir}/target/classes" />
    
        <target name="jar" description="Build the jars for core">
            <delete file="${coredir}/webservice-interfaces-1.0.jar" />
            <jar destfile="${coredir}/webservice-interfaces-1.0.jar">
                <fileset dir="${classdir}">
                    <include name="**/com/xxx/apps/web/ws/server/**/*Service.class" />
                    <include name="**/com/xxx/apps/web/ws/server/tokenservice/**/*.class" />
                    <include name="**/com/xxx/apps/web/ws/server/WSReturn.class"/>
                    <include name="**/com/xxx/apps/comm/ResultState.class"/>
                    <include name="**/com/xxx/apps/web/ws/server/wsclient/WSClient.class"/>
                    <include name="**/com/xxx/apps/comm/RespResult.class"/>
                    <exclude name="**/com/xxx/apps/web/ws/server/**/*Impl.class" />
                </fileset>
            </jar>
            <copy todir="../xxxclient/lib" file="./webservice-interfaces-1.0.jar"></copy>
        </target>
    
    </project>

    客户端项目实现

    依赖

    首先通过Maven引入必要依赖包。

    • org.apache.cxf.cxf-rt-frontend-jaxws
    • org.apache.cxf.cxf-rt-databinding-aegis
    • org.apache.cxf.cxf-rt-transports-http
    • org.apache.cxf.cxf-rt-transports-http-jetty
    • commons-codec.commons-codec

      最重要的:引入server端打包好的jar包,里边有WSClient和必要的接口

          <dependency>
              <groupId>com.xxx</groupId>
              <artifactId>xxxserver</artifactId>
              <version>1.0</version>
              <scope>system</scope>
              <systemPath>${project.basedir}/lib/webservice-interfaces-1.0.jar</systemPath>
          </dependency>

    WSClient调用

    通过直接调用jar包中的WSClient即可调用远程WebService接口。
    调用示例代码如下:

    /** 这里将调用注释复制过来
         * 调用代理
         * @param cls 服务接口代理
         * @param method 方法名
         * @param wsdl wsdl地址
         * @param params 参数Object[]
         * @return
         * @throws Exception
         */
    WSReturn res= WSClient.invoke(XXXService.class
                    , "getAuth"
                    ,endpoints.get(XXXService.class.getName())
                    , new Object[]{"admin","admin"});
            if(token.getStatusId() == ResultState.SUCESS){
                tokenValue = (String) token.getMap().get("token");
            } else {
                logger.error("获取token失败:"+token.getMsg());
            }

    一些坑

    一定要引入cxf的必要配置

    虽然在项目中看不到,但是这些xml文件在cxf的jar包中。

        <!-- cxf必要配置 -->
        <import resource="classpath:META-INF/cxf/cxf.xml" />  
        <import resource="classpath:META-INF/cxf/cxf-servlet.xml" />  
        <import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" />

    Interface的类路径一定要统一

    如服务端的 XXXService.javacom.xxx.web.ws.server 中,则在客户端的XXXService.java类也应该在相同的路径即: com.xxx.web.ws.server 。 所以为方便起见,用Ant直接打包比较方便,不容易错。

    客户端并发问题

    本例中调用WSClient,通过反射机制调用,共用一个Factory,因此在并发时候容易出现问题,需要在WSClient中加锁

    原文地址:https://blog.csdn.net/tzdwsy/article/details/51938786
  • 相关阅读:
    我们可以用微服务创建状态机吗?
    MyBatis 实现一对多有几种方式,怎么操作的?
    说几个 zookeeper 常用的命令?
    使用 RabbitMQ 有什么好处?
    消息基于什么传输?
    如何获取自动生成的(主)键值?
    vue打包压缩
    mysqldump数据库全备份_MySQL
    mysql的binlog
    开启BinLog_MySQL
  • 原文地址:https://www.cnblogs.com/jpfss/p/11065557.html
Copyright © 2011-2022 走看看