在之前的文章中,有实现一个图书馆的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参数。
来源:oschina
链接:https://my.oschina.net/u/254689/blog/228888