利用AXIS開發Webservice(四) —— 如何拋出自定義異常
關鍵字: webservice axis throw customer exception
今天進入AXIS之四,如何拋出一個你的自定義異常。本來想連傳文件一起介紹的。后來感覺一篇blog里太多的內容也不太好,看起來太辛苦,還是慢慢來,廢話不多進入正題。
上一篇介紹了如果在Server和Client端傳遞一個自己的對象。有些人也許會問傳遞異常行不行?答案是可以。只不過傳遞異常的配置要稍微復雜一些。空口無憑,我還是用點代碼來說明。今天的例子稍微復雜點,用一下數據庫(MySQL)。首先創建表和輸入測試數據。
- create table users(id integer primary key, name varchar(20) not null);
- insert into users values(1, 'Lincoln'),(2, 'Michael'),(4, 'Mahone'),(6, 'Sara');
一個user表,4條記錄。等會我們client段會發送一個SOAP request給server段,之后server段返回客戶要的數據,如果沒有則拋出一個自定義異常。表建立完成之后來編寫JavaBean。
- package com.chnic.bean;
- public class UserBean implements java.io.Serializable{
- private static final long serialVersionUID = 1L;
- private int id;
- private String name;
- public UserBean(){
- }
- public int getId() {
- return id;
- }
- public void setId(int id) {
- this.id = id;
- }
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- }
Bean有兩個屬性,Id和Name。client會根據ID來取要的Name。編寫完Bean之后我們來編寫Customer Exception的代碼。
- package com.chnic.exception;
- import java.rmi.RemoteException;
- public class NoSuchUserException extends RemoteException {
- private String errorMessage = "No such user: ";
- private int id;
- private static final long serialVersionUID = 1L;
- public NoSuchUserException() {
- }
- public void printErrorMessage(){
- System.out.println(errorMessage + id);
- }
- public int getId() {
- return id;
- }
- public void setId(int id) {
- this.id = id;
- }
- }
NoSuchUserException這個類會記錄在數據庫沒有相應數據的ID的值,然后返回給Client。值得注意的是,因為這個是個遠程異常。所以要繼承RemoteException這個類。兩個要transfer的Bean完成之后。我們來編寫Service Ojbect的代碼。
- package com.chnic.webservice;
- import java.sql.Connection;
- import java.sql.PreparedStatement;
- import java.sql.ResultSet;
- import java.sql.SQLException;
- import com.chnic.bean.UserBean;
- import com.chnic.exception.NoSuchUserException;
- import java.sql.DriverManager;
- public class CheckUserInfo {
- private String url = "jdbc:mysql://localhost:3306/test";
- private String user = "root";
- private String password = "root";
- public CheckUserInfo(){
- }
- public Connection getConn(){
- try {
- Class.forName("com.mysql.jdbc.Driver");
- return DriverManager.getConnection(url, user, password);
- } catch (ClassNotFoundException e) {
- e.printStackTrace();
- } catch (SQLException e) {
- e.printStackTrace();
- }
- return null;
- }
- public UserBean checkUser(int id) throws NoSuchUserException{
- Connection conn = null;
- try {
- conn = this.getConn();
- PreparedStatement statement =
- conn.prepareStatement("select * from users where id = ?");
- statement.setInt(1, id);
- ResultSet rs = statement.executeQuery();
- boolean flag = false;
- UserBean user = null;
- while(rs.next()){
- flag = true;
- user = new UserBean();
- user.setId(id);
- user.setName(rs.getString(2));
- }
- rs.close();
- if(flag)
- return user;
- else{
- NoSuchUserException userException = new NoSuchUserException();
- userException.setId(id);
- throw userException;
- }
- } catch (SQLException e) {
- e.printStackTrace();
- }finally{
- this.closeConn(conn);
- }
- return null;
- }
- public void closeConn(Connection conn){
- try {
- conn.close();
- } catch (SQLException e) {
- e.printStackTrace();
- }
- }
- }
因為是Demo代碼,代碼寫的比較粗糙,反正就是為了個演示。大家能看出來效果就好了。代碼很簡單,接收到一個id,然后在數據庫里做匹配。如果找到匹配的了返回那個userbean,如果沒找到就throw一個Exception出去。在這里多嘴一句。傳遞的Bean賦值的時候一定要用setXXX方法,不能用構造函數傳遞,否則傳遞過去之后屬性值會丟失。 你編寫的那個Bean一定要嚴格遵循JavaBean規范。
之后我們來看WSDD發布文件。比起之前我們看到的WSDD文件,這次的稍微有點點復雜。
- <deployment xmlns="http://xml.apache.org/axis/wsdd/"
- xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">
- <service name="AxisExceptionTest" provider="java:RPC">
- <namespace>http://faults.samples</namespace>
- <parameter name="className" value="com.chnic.webservice.CheckUserInfo"/>
- <parameter name="allowedMethods" value="checkUser"/>
- <parameter name="scope" value="Session"/>
- <operation name="checkUser"
- qname="operNS:checkUser"
- xmlns:operNS="getSingleUser"
- returnQName="getUserReturn"
- returnType="rtns:User"
- xmlns:rtns="http://faults.samples" >
- <parameter name="id" type="tns:int"
- xmlns:tns="http://www.w3.org/2001/XMLSchema"/>
- <fault name="NoSuchEmployeeFault"
- qname="fns:fault"
- xmlns:fns="http://faults.samples"
- class="samples.faults.NoSuchEmployeeFault"
- type="tns:NoSuchUserFault"
- xmlns:tns="http://faults.samples"/>
- </operation>
- <typeMapping qname="myns:NoSuchUserFault"
- xmlns:myns="urn:CustomerFault"
- type="java:com.chnic.exception.NoSuchUserException"
- serializer="org.apache.axis.encoding.ser.BeanSerializerFactory"
- deserializer="org.apache.axis.encoding.ser.BeanDeserializerFactory"
- encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
- <typeMapping qname="myns:User"
- xmlns:myns="urn:CustomerBean"
- type="java:com.chnic.bean.UserBean"
- serializer="org.apache.axis.encoding.ser.BeanSerializerFactory"
- deserializer="org.apache.axis.encoding.ser.BeanDeserializerFactory"
- encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
- </service>
- </deployment>
首先不同的是多了個命名空間也就是namespace節點,等會測試代碼中會看到用途。除了namespace之外還有operation這個節點和里面的parameter和fault子節點。先來介紹operation這個節點的屬性。
name:操作名稱或者方法名稱,這個值會和你server發布的相關方法名匹配,所以要和方法名相同。
qname:針對這個operation的限定名。
xmlns:針對這個qname的命名空間也就是namespace。(不明白的可以看上一篇博文)
returnQName:這個元素節點所對應的方法返回出來對象的Qname。
returnType:返回類型,注意和下面的typemapping的qname比較。
parameter節點是這個operation指代的方法的參數,fault節點代表要這個方法要拋出的異常。異常也需要被mapping。下面的typemapping做的也是這樣的事情。這兩個元素的節點的屬性和operation都是類似,對以一下大概就知道什么意思了。在這里也不多解釋了。現在來看測試代碼。
- package com.chnic.test;
- import java.rmi.RemoteException;
- import javax.xml.namespace.QName;
- import javax.xml.rpc.Call;
- import javax.xml.rpc.Service;
- import javax.xml.rpc.ServiceException;
- import javax.xml.rpc.ServiceFactory;
- import javax.xml.rpc.encoding.TypeMapping;
- import javax.xml.rpc.encoding.TypeMappingRegistry;
- import org.apache.axis.encoding.ser.BeanDeserializerFactory;
- import org.apache.axis.encoding.ser.BeanSerializerFactory;
- import com.chnic.bean.UserBean;
- import com.chnic.exception.NoSuchUserException;
- public class TestException {
- public static void main(String[] args){
- String uri = "http://faults.samples";
- String serviceName = "EmployeeInfoService";
- ServiceFactory serviceFactory;
- String url = "http://localhost:8080/axis/services/AxisExceptionTest";
- try {
- serviceFactory = ServiceFactory.newInstance();
- QName serQ = new QName(uri, serviceName);
- Service service = serviceFactory.createService(serQ);
- TypeMappingRegistry registry = service.getTypeMappingRegistry();
- TypeMapping map = registry.getDefaultTypeMapping();
- QName employeeQName = new QName("urn:CustomerBean", "User");
- map.register(UserBean.class, employeeQName,
- new BeanSerializerFactory(UserBean.class, employeeQName),
- new BeanDeserializerFactory(UserBean.class, employeeQName));
- QName faultQName = new QName("urn:CustomerFault", "NoSuchUserFault");
- map.register(NoSuchUserException.class, faultQName,
- new BeanSerializerFactory(NoSuchUserException.class, faultQName),
- new BeanDeserializerFactory(NoSuchUserException.class, faultQName));
- Call call = service.createCall();
- call.setTargetEndpointAddress(url);
- call.setOperationName( new QName(uri, "checkUser") );
- Object obj = call.invoke(new Object[]{ new Integer(3)});
- UserBean user = (UserBean) obj;
- System.out.println(user.getName());
- } catch (ServiceException e) {
- e.printStackTrace();
- }catch(NoSuchUserException e){
- e.printErrorMessage();
- }catch (RemoteException e) {
- e.printStackTrace();
- }
- }
- }
看到第一個申明的uri了么?我們通過這個uri和service來取得對應的service。 之后我們用TypeMappingRegistry得到一個默認的TypeMapping。在map里面映射我們的bean。之后和往常的代碼一樣沒有特別的。invoke喚起方法,返回UserBean,并打出UserBean的name。值得注意的是后面的catch部分,我們catch了一個我們自己申明的NoSuchUserException,抓住這個異常之后打出我們要的錯誤信息。
因為1 2 4 6在數據庫里都是有對應的數值的,所以當我要查找ID為3個user的name的時候,service就會返回一個NoSuchUserException給我。之后在本地抓住這個exception之后,控制臺打出了如下信息。
No such user: 3
如果輸入的是1 2 4 6的話,則會返回user的名字。在這里就不測試了。各位自己可以試試。