今天在對接的客戶用到了webservice,最終采用wsimport生成本地代理方式以SDK的形式對接,但是想的完整的總結一下ws的幾種調用方式。
發布服務的IP地址是:192.168.125.116
客戶端訪問ws服務的IP是:192.168.125.115
1.發布ws服務:
參考:https://www.cnblogs.com/qlqwjy/p/9644078.html
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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://cxf.apache.org/jaxws http://cxf.apache.org/jaxws http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <jaxws:endpoint id="userServiceWS" implementor="cn.xm.exam.service.impl.webservice.UserWebServiceImpl" address="/userServiceWS"> </jaxws:endpoint> </beans>
web.xml如下:
<servlet> <servlet-name>CXFServlet</servlet-name> <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>CXFServlet</servlet-name> <url-pattern>/WS/*</url-pattern> </servlet-mapping>
package cn.xm.exam.service.webservice; import java.util.Set; import javax.jws.WebService; import cn.xm.exam.bean.system.User; @WebService public interface UserWebService { /** * 根據用戶身份證號碼查詢用戶信息及其角色信息及其權限信息 * * @param useridcard * @return user */ public User getUserByUseridcard(String useridcard) throws Exception; /** * 根據用戶編號查詢角色code的集合 * * @param userid * @return 角色集合 */ public Set<String> getRoleByUserid(String userid) throws Exception; }
package cn.xm.exam.service.impl.webservice; import java.util.Set; import javax.annotation.Resource; import javax.jws.WebService; import org.springframework.stereotype.Service; import cn.xm.exam.bean.system.User; import cn.xm.exam.mapper.system.UserMapper; import cn.xm.exam.service.webservice.UserWebService; import cn.xm.exam.utils.ValidateCheck; @Service @WebService(targetNamespace = "http://webservice.service.exam.xm.cn") public class UserWebServiceImpl implements UserWebService { @Resource private UserMapper userMapper; @Override public User getUserByUseridcard(String useridcard) { if (ValidateCheck.isNull(useridcard)) { return null; } User user = null; try { user = userMapper.getUserByUseridcard(useridcard); } catch (Exception e) { e.printStackTrace(); } return user; } @Override public Set<String> getRoleByUserid(String userid) { if (ValidateCheck.isNull(userid)) { return null; } Set<String> role = null; try { role = userMapper.getRoleByUserid(userid); } catch (Exception e) { e.printStackTrace(); } return role; } }
啟動之后查看ws服務:
查看wsdl:
2.訪問ws的幾種方式
1.wsimport生成本地代理的方式(簡單--類似於SDK訪問)
生成本地代碼(如果需要下載源碼,加上 -keep 參數即可)
wsimport http://192.168.125.116:85/Exam/WS/userServiceWS?wsdl
發現雖然我們只是發布了一個webservice,但是下載的時候會將接口中依賴的bean等信息也一起發布。
打包
jar cvf ./test.jar ./
放入eclipse進行測試
import java.net.MalformedURLException; import java.net.URL; import javax.xml.namespace.QName; import cn.xm.exam.service.webservice.Exception_Exception; import cn.xm.exam.service.webservice.User; import cn.xm.exam.service.webservice.UserWebService; import cn.xm.exam.service.webservice.UserWebServiceImplService; public class Test { public static void main(String[] args) throws Exception_Exception, MalformedURLException { // 創建的時候用URL指定WSDL的地址;用QName指定targetNamespace和name。當然可以不指定,不指定使用的是默認值 // UserWebServiceImplService userWebServiceImplService = new UserWebServiceImplService(); UserWebServiceImplService userWebServiceImplService = new UserWebServiceImplService( new URL("http://192.168.125.116:85/Exam/WS/userServiceWS?wsdl"), new QName("http://webservice.service.exam.xm.cn", "UserWebServiceImplService")); UserWebService userWebServiceImplPort = userWebServiceImplService .getUserWebServiceImplPort(); User userByUseridcard = userWebServiceImplPort .getUserByUseridcard("140581197705070518"); System.out.println(userByUseridcard.getUsername()); } }
結果:
返回從192.168.125.116返回數據庫查詢的信息。 也就是說我們調用ws接口的時候實際底層是http+xml,因此我們可以通過ws接口調用到服務器的信息。
2. CXF動態調用:(推薦這種)
依賴的包:
測試代碼如下:
import javax.xml.namespace.QName; import org.apache.cxf.jaxws.endpoint.dynamic.JaxWsDynamicClientFactory; public class Test2 { public static void main(String[] args) { JaxWsDynamicClientFactory dcf = JaxWsDynamicClientFactory.newInstance(); org.apache.cxf.endpoint.Client client = dcf.createClient("http://localhost:85/Exam/WS/userServiceWS?wsdl"); // url為調用webService的wsdl地址 QName name = new QName("http://webservice.service.exam.xm.cn/", "getUserByUseridcard");// namespace是命名空間,methodName是方法名 Object[] objects; try { objects = client.invoke(name, "140581197705070518");// 第一個參數是上面的QName,第二個開始為參數,可變數組 System.out.println(objects); } catch (Exception e) { e.printStackTrace(); } } }
注意:
(1)QName的第一個參數必須帶斜杠,也就是必須寫為http://webservice.service.exam.xm.cn/,否則會報下面異常:
org.apache.cxf.common.i18n.UncheckedException: No operation was found with the name {http://webservice.service.exam.xm.cn}getUserByUseridcard.
at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:331)
at Test2.main(Test2.java:15)
(2)發布的webservice的接口和實現類要在同一個包,或者在實現類用注解聲明發布的webservice地址。
CXF發布用的是業務類(xxxServiceImpl.java),那么默認的命名空間就會是業務類所在包(路徑),而對外界暴露的則是接口類(XXXService.java),那么對於客戶端(第三方)調用訪問時,需要按照接口類所在包(路徑)進行命名空間的定義。所以需要接口與實現在同一個包或者在實現類指定接口地址。
如下:(最好以/結尾---下面沒有以斜杠結尾)
@Service @WebService(targetNamespace = "http://webservice.service.exam.xm.cn") public class UserWebServiceImpl implements UserWebService {
。。。
(3)查看namespace相關信息:(兩兩相同是因為上面發布的時候沒有以/結尾,最好以/結尾四個namespace就會相同)
3.通過axis包訪問(簡單)
所需5個jar包 axis-1.4.jar axis-jaxrpc-1.4.jar commons-logging-1.2.jar commons-discovery-0.2.jar wsdl4j-1.6.3.jar
這個在測試的時候直接返回bean報錯(解決辦法就是加類型轉換器),因此我返回的是String數據。接口和實現類修改如下:
package cn.xm.exam.service.webservice; import java.util.Set; import javax.jws.WebResult; import javax.jws.WebService; import cn.xm.exam.bean.system.User; @WebService public interface UserWebService { /** * 根據用戶身份證號碼查詢用戶信息及其角色信息及其權限信息 * * @param useridcard * @return user */ public String getUsernameByUseridcard(String useridcard) throws Exception; /** * 根據用戶編號查詢角色code的集合 * * @param userid * @return 角色集合 */ public Set<String> getRoleByUserid(String userid) throws Exception; }
package cn.xm.exam.service.impl.webservice; import java.util.Set; import javax.annotation.Resource; import javax.jws.WebService; import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; import org.springframework.stereotype.Service; import cn.xm.exam.bean.system.User; import cn.xm.exam.mapper.system.UserMapper; import cn.xm.exam.service.webservice.UserWebService; import cn.xm.exam.utils.ValidateCheck; @Service @WebService(targetNamespace = "http://webservice.service.exam.xm.cn") public class UserWebServiceImpl implements UserWebService { @Resource private UserMapper userMapper; @Override public String getUsernameByUseridcard(String useridcard) { if (ValidateCheck.isNull(useridcard)) { return null; } User user = null; try { user = userMapper.getUserByUseridcard(useridcard); } catch (Exception e) { e.printStackTrace(); } return user == null ? "" : user.getUsername(); } @Override public Set<String> getRoleByUserid(String userid) { if (ValidateCheck.isNull(userid)) { return null; } Set<String> role = null; try { role = userMapper.getRoleByUserid(userid); } catch (Exception e) { e.printStackTrace(); } return role; } }
測試代碼:
代碼一:
package zd.dms.wbws; import java.net.MalformedURLException; import java.net.URL; import java.rmi.RemoteException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.xml.namespace.QName; import javax.xml.rpc.ParameterMode; import javax.xml.rpc.ServiceException; import org.apache.axis.client.Call; import org.apache.axis.client.Service; import org.apache.axis.encoding.XMLType; public class AxisClient { public static void main(String[] args) { String webServiceURL = "http://localhost:85/Exam/WS/userServiceWS?wsdl"; String namespaceURI = "http://webservice.service.exam.xm.cn/"; String method = "getUsernameByUseridcard"; List<Map<String, Object>> params = new ArrayList<Map<String, Object>>(); Map<String, Object> map = new HashMap<String, Object>(); map.put("paramName", "arg0"); map.put("paramType", XMLType.XSD_STRING); map.put("paramValue", "140581197705070518"); params.add(map); String result = (String) AxisClient.getResult(webServiceURL, namespaceURI, method, null, params, XMLType.XSD_STRING); System.out.println(result); } /** * Axis方式訪問WebService接口 * * @param webServiceURL * webService地址,末尾為?wsdl時不需要寫 * @param namespaceURI * 命名空間 * @param method * 方法名稱 * @param soapAction * soapAction * @param params * 參數 * @param returnType * 返回值類型 * @return */ public static Object getResult(String webServiceURL, String namespaceURI, String method, String soapAction, List<Map<String, Object>> params, QName returnType) { if (null == params || params.size() == 0) return null; // 創建客戶端 Service service = new Service(); Call call = null; try { call = (Call) service.createCall(); // 設置訪問地址 call.setTargetEndpointAddress(new URL(webServiceURL)); // 是否使用SOAPAction if (null == soapAction || "".equals(soapAction)) { call.setUseSOAPAction(false); } else { call.setUseSOAPAction(true); // 設置SOAPAction地址 call.setSOAPActionURI(soapAction); } // 設置訪問方法名稱 call.setOperationName(new QName(namespaceURI, method)); // 添加解析類型的地址 call.setEncodingStyle(namespaceURI); // 設置返回值類型 call.setReturnType(returnType); // 添加參數名稱 Object[] obj = new Object[params.size()]; for (int i = 0; i < params.size(); i++) { Map<String, Object> map = params.get(i); call.addParameter(new QName((String) map.get("paramName")), (QName) map.get("paramType"), ParameterMode.IN); obj[i] = map.get("paramValue"); } return call.invoke(obj); } catch (ServiceException e) { e.printStackTrace(); return null; } catch (MalformedURLException e) { e.printStackTrace(); return null; } catch (RemoteException e) { e.printStackTrace(); return null; } } }
代碼二:
package zd.dms.wbws; import javax.xml.namespace.QName; import javax.xml.rpc.ParameterMode; import org.apache.axis.client.Call; import org.apache.axis.client.Service; import org.apache.axis.encoding.XMLType; public class TestUseWebservice2 { public static void main(String[] args) { Object ticket = testWS(); System.out.println(ticket); } public static Object testWS() { try { // wsdl的地址 String endpoint = "http://localhost:85/Exam/WS/userServiceWS?wsdl"; // 直接引用遠程的wsdl文件 // 以下都是固定格式 Service service = new Service(); Call call = (Call) service.createCall(); call.setTargetEndpointAddress(endpoint); // WSDL里面描述的方法名稱 填寫方法名是需加上命名空間,否則會找不到對應的方法 call.setOperationName(new QName("http://webservice.service.exam.xm.cn/", "getUsernameByUseridcard")); // 添加解析類型的地址 call.setEncodingStyle("webservice.service.exam.xm.cn"); // 給方法傳遞參數,並且調用方法 call.addParameter("arg0", XMLType.XSD_STRING, ParameterMode.IN); call.setReturnType(XMLType.XSD_ENTITY);// 設置返回類型 return call.invoke(new Object[] { "140581197705070518" }); } catch (Exception e) { e.printStackTrace(); return null; } } }
由於webservice采用SOAP協議,相當於http+xml,因此我們訪問的時候可以獲取到服務器的數據。
而RPC相當於接口和實現分離,我們獲取到接口,然后實現類可以從RPC的注冊中心進行獲取。當然RPC也可以獲取遠程的數據。其實webservice和RPC可以通過生成代理對象獲取遠程的數據。我們用RPC以本地方式調用服務,實則會向服務器的具體實現類發送請求並獲取數據。(當然這中間有對方法、請求參數的包裝以及解碼等工作)
CXF訪問參考:https://www.cnblogs.com/qlqwjy/p/9644078.html