JAX-WS传递自定义的Java类型

大兔子大兔子 提交于 2019-11-29 20:36:25

在之前的文章中,有实现一个图书馆的WebService。可以在这篇文章中http://my.oschina.net/xpbug/blog/224912 找到。

然而,之前的图书馆系统接口所接收的参数和返回的类型,都非常简单,只是int和String两种类型。如果我想让接口接收和返回自定义的复杂类型,该如何做?这篇文章将展示如何将之前的图书馆系统改造为更复杂的实现。

图书馆服务接口

首先定义图书馆提供了哪些服务,让我用接口表示:

@WebService(name="Library", targetNamespace="http://library.mycompany.com")
public interface Library {
    @WebResult(name="result",targetNamespace="http://library.mycompany.com")
    public Book addBook(@WebParam(name="book", targetNamespace="http://library.mycompany.com")Book book);
    @WebResult(name="result",targetNamespace="http://library.mycompany.com")
    public Book getBook(@WebParam(name="id")int id);
    @WebResult(name="bookArray")
    public Book[] getBooksArray();
    @WebResult(name="bookList")
    public List<Book> getBookList();
    @WebResult(name="bookMap")
    public Map<Integer, Book> getBookMap();
    @WebResult(name="result")
    public boolean deleteBook(@WebParam(name="id")int id);
}

由上可以看到,新的图书馆系统所提供的服务,接收和返回的均是封装好的Book类型或者Book的集合。这需要参数和返回类型符合JAXB的规范,JAVA类型和XML可以通过JAXB相互转换。

实现Book类型

在JAXB2.0的时候,List和Array可以被转为XML,但Map不可以,但在2.1中,Map已经可以被转为XML。Array型的XML在被转为Java时,会封装成List。

由于Array,List和Map均可以被JAXB转为XML,只剩下Book类型,需要做一些设计,才可以被JAXB转为XML。

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

@XmlRootElement(name="book", namespace="library.mycompany.com")
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name="book", namespace="library.mycompany.com")
public class Book {
    @XmlElement(name = "id", namespace = "")
    private int id;
    @XmlElement(name = "name", namespace = "")
    private String name;
    @XmlElement(name = "author", namespace = "")
    private String author;
    
    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;
    }
    public String getAuthor() {
        return author;
    }
    public void setAuthor(String author) {
        this.author = author;
    }
}

我们的Book类型还算简单,有时候需要更加复杂的自定义类型,这时候可能需要@XmlJavaTypeAdapter的支持,详细内容请学习JAXB。

实现图书馆SEI

@WebService(endpointInterface="com.mycompany.Library")
public class LibraryImpl implements Library {
    private static Map<Integer, Book> store = new HashMap<Integer, Book>();
    private static int currentId=0;

    @Override
    public Book addBook(Book book) {
        book.setId(++currentId);
        store.put(book.getId(), book);
        return book;
    }

    @Override
    public Book getBook(int id) {
        return store.get(id);
    }

    @Override
    public Book[] getBooksArray() {
        List<Book> l = new ArrayList<Book>();
        Iterator<Integer> it = store.keySet().iterator();
        while(it.hasNext()) {
            l.add(store.get(it.next()));
        }
        Book[] r = l.toArray(new Book[1]);
        System.out.println(r.length);
        return r;
    }
    
    @Override
    public List<Book> getBookList() {
        List<Book> l = new ArrayList<Book>();
        Iterator<Integer> it = store.keySet().iterator();
        while(it.hasNext()) {
            l.add(store.get(it.next()));
        }
        return l;
    }
    
    @Override
    public Map<Integer, Book> getBookMap() {
        return store;
    }

    @Override
    public boolean deleteBook(int id) {
        if (store.containsKey(id)) {
            store.remove(id);
            return true;
        } else {
            return false;
        }
    }
}

sun-jaxws.xml

在WEB-INF下创建sun-jaxws.xml文件:

<?xml version="1.0" encoding="UTF-8"?>
<endpoints xmlns='http://java.sun.com/xml/ns/jax-ws/ri/runtime' version='2.0'>
    <endpoint
        name='library'
        implementation='com.mycompany.LibraryImpl'
        url-pattern='/service'/>
</endpoints>

部署

使用wsgen命令,产生WSDL,XSD和相应的Java文件。我创建的是Maven project,所以使用的是Maven中的wsgen插件。

      <plugin>
        <groupId>org.jvnet.jax-ws-commons</groupId>
        <artifactId>jaxws-maven-plugin</artifactId>
        <version>2.3</version>
        <executions>
          <execution>
            <id>wsgen-from-jdk</id>
            <phase>process-classes</phase>
            <goals>
              <goal>wsgen</goal>
            </goals>
            <configuration>
              <executable>${tool.wsgen}</executable>
              <sei>com.mycompany.LibraryImpl</sei>
              <genWsdl>true</genWsdl>
            </configuration>
          </execution>
        </executions>
      </plugin>

运行命令: mvn package

得到可部署的war包library.war. War包的内部结构如图:

最后将产生的library.war丢到tomcat下。验证webservice已经产生: http://localhost:8080/library/service

生成客户端

因为客户端也是用Maven创建的项目,所以wsimport命令使用的是Maven中的插件。在pom.xml中:

      <plugin>
        <groupId>org.jvnet.jax-ws-commons</groupId>
        <artifactId>jaxws-maven-plugin</artifactId>
        <version>2.3</version>
        <executions>
          <execution>
            <id>wsimport-from-jdk</id>
            <goals>
              <goal>wsimport</goal>
            </goals>
            <configuration>
              <executable>${tool.wsimport}</executable>
              <wsdlUrls>
                <wsdlUrl>http://localhost:8080/library/service?wsdl</wsdlUrl>
              </wsdlUrls>
              <verbose>true</verbose>
              <xdebug>true</xdebug>
            </configuration>
          </execution>
        </executions>
      </plugin>

运行mvn generate-sources, 得到wsdl生成的java文件。将java文件copy到项目中:

上面红色框中,除了AuthorHandler和LoggerHandler是自己写的,其它的都是wsimport产生的。

最后填写App.java中的main函数

public class App {
    public static void main( String[] args ) {
        Book newBook = new Book();
        newBook.setAuthor("xpbug");
        newBook.setName("java");
        Book rBook = createPort().addBook(newBook);
        printBook(rBook);
        
        newBook = new Book();
        newBook.setAuthor("cat");
        newBook.setName("c++");
        rBook = createPort().addBook(newBook);
        printBook(rBook);
        
        System.out.println(createPort().deleteBook(rBook.getId()));
        
        rBook = createPort().getBook(1);
        printBook(rBook);
        
        List<Book> list = createPort().getBooksArray();
        System.out.println(list.size());
        for (Book i : list) {
            printBook(i);
        }
        
        list = createPort().getBookList();
        System.out.println(list.size());
        for (Book i : list) {
            printBook(i);
        }
        
        BookMap map = createPort().getBookMap();
        for (Entry i : map.getEntry()) {
            System.out.println(i.getKey());
            printBook(i.getValue());
        }
    }
    
    public static Library createPort() {
        Library port = new LibraryImplService().getLibraryImplPort();
        return port;
    }
    
    public static void printBook(Book book) {
        System.out.println("[id="+book.getId()+"; name="+book.getName()+"; author="+book.getAuthor()+"]");
    }
}

可以直接在eclipse中运行App。

常见问题

在编写webservice的过程中,明明逻辑正确,参数也正确,但后台总是报NullPointException, 这种时候,就需要注意namespace了。查看client端发出的参数的namespace是否跟server端接收方参数类型的namespace一致。如果不一致,接收方只会接到一个null参数。


易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!