在之前的文章中,有实现一个图书馆的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相互转换。
在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。
@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; } } }
在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参数。