場景
某SpringMVC項(xiàng)目原本為一個HTTP的WEB服務(wù)項(xiàng)目,之后想在該項(xiàng)目中添加WebService支持,使該項(xiàng)目同時提供HTTP服務(wù)和WebService服務(wù)。其中WebService服務(wù)通過 /ws/** 地址攔截。
配置
通過配置讓SpringMVC支持WebService。
依賴
首先通過Maven引入必要依賴包。
- org.apache.cxf
- org.apache.neethi
- com.ibm.wsdl4j
- org.apache.XmlSchema
Web.xml
通過配置Web.xml使Spring框架具備WebService特性,這里通過添加Servlet(這里使用CXFServlet)實(shí)現(xiàn)。假設(shè)SpringMVC本身的DispatcherServlet已經(jīng)啟用,則在第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的接口配置單獨(dú)分離出來。配置如下:
<beans xmlns="http://www./schema/beans"
xmlns:xsi="http://www./2001/XMLSchema-instance"
xmlns:p="http://www./schema/p"
xmlns:jaxws="http://cxf./jaxws"
xmlns:context="http://www./schema/context"
xmlns:mvc="http://www./schema/mvc"
xsi:schemaLocation="
http://www./schema/beans
http://www./schema/beans/spring-beans.xsd
http://www./schema/mvc
http://www./schema/mvc/spring-mvc.xsd
http://cxf./jaxws
http://cxf./schemas/jaxws.xsd
http://www./schema/context
http://www./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" />
<!-- 接口的實(shí)現(xiàn)類聲明 -->
<jaxws:endpoint id="ticketDecodeAuthService"
implementorClass="com.xxx.apps.web.ws.server.decode.XXXServiceImpl"
address="/ticketDecodeAuth" />
</beans>
接口編寫
對應(yīng)上文聲明的接口文檔在寫在相應(yīng)的位置上(如本文例子則寫在com.xxx.apps.web.ws.server.decode包中)
代碼如下:
@WebService
@SOAPBinding(style = Style.RPC)
public interface XXXService {
public WSReturn getAuth(String userName, String password) throws Exception;
}
接口實(shí)現(xiàn)類:
@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;
}
}
發(fā)布接口效果
啟動SpringMVC項(xiàng)目,根據(jù)配置文件定義,接口地址類似:http://ip:port/項(xiàng)目名/ws/**
若本例配置則有如下接口可以查看:
查看所有接口列表
http://ip:port/項(xiàng)目名/ws
某具體端口(XXXService)為例
這也是客戶端調(diào)用時候的地址
http://ip:port/項(xiàng)目名/ws/XXXService?wsdl
這里可以看到端口的規(guī)范定義
客戶端編寫
客戶端代碼
通過CXF的動態(tài)代理方式編寫,以反射方式將class直接引入可以實(shí)現(xiàn)統(tǒng)一調(diào)用方法。這樣該Client即可調(diào)用任意接口。
代碼如下:
/**
* webservice服務(wù)客戶端
* @author WSY
*
*/
public class WSClient {
private static Logger logger = LoggerFactory.getLogger(WSClient.class);
/**
* 調(diào)用代理
* @param cls 服務(wù)接口代理
* @param method 方法名
* @param wsdl wsdl地址
* @param params 參數(shù)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(不要打?qū)崿F(xiàn)類)打成包。本文中將Client代碼也寫在了Service端了,所以將WSClient也一并打包進(jìn)去。這樣在編寫對應(yīng)的客戶端時候,僅需專注于功能實(shí)現(xiàn)即可。
<?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>
客戶端項(xiàng)目實(shí)現(xiàn)
依賴
首先通過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調(diào)用
通過直接調(diào)用jar包中的WSClient即可調(diào)用遠(yuǎn)程WebService接口。
調(diào)用示例代碼如下:
/** 這里將調(diào)用注釋復(fù)制過來
* 調(diào)用代理
* @param cls 服務(wù)接口代理
* @param method 方法名
* @param wsdl wsdl地址
* @param params 參數(shù)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的必要配置
雖然在項(xiàng)目中看不到,但是這些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的類路徑一定要統(tǒng)一
如服務(wù)端的 XXXService.java 在 com.xxx.web.ws.server 中,則在客戶端的XXXService.java類也應(yīng)該在相同的路徑即: com.xxx.web.ws.server 。 所以為方便起見,用Ant直接打包比較方便,不容易錯。
客戶端并發(fā)問題
本例中調(diào)用WSClient,通過反射機(jī)制調(diào)用,共用一個Factory,因此在并發(fā)時候容易出現(xiàn)問題,需要在WSClient中加鎖 原文地址:https://blog.csdn.net/tzdwsy/article/details/51938786
|