一、Xfire部署构建
使用Xfire来构建一套java的webservice服务接口是很容易的。现在xfire已经停止开发,被apache合并为CXF项目。我们下载Xfire项目使用jar包,扔进到项目的buildpath中去。核心的jar包括如下:
我们在src目录下新建一个文件夹META-INF,再建它的一个字文件夹xfire,里面建立文件services.xml。之后的结构如下:
我们直接看一下servics.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://xfire.codehaus.org/config/1.0">
<service>
<!-- webserviceq名称,调用时需要指定这个 -->
<name>readerService</name>
<!-- 这个一般是自己公司的网址,意义不大 -->
<namespace>http://test/HelloService</namespace>
<!-- 接口类 -->
<serviceClass>com.xfire.servlet.IReaderService</serviceClass>
<!-- 实现类 -->
<implementationClass>com.xfire.servlet.ReaderService</implementationClass>
</service>
</beans>
<beans xmlns="http://xfire.codehaus.org/config/1.0">
<service>
<!-- webserviceq名称,调用时需要指定这个 -->
<name>readerService</name>
<!-- 这个一般是自己公司的网址,意义不大 -->
<namespace>http://test/HelloService</namespace>
<!-- 接口类 -->
<serviceClass>com.xfire.servlet.IReaderService</serviceClass>
<!-- 实现类 -->
<implementationClass>com.xfire.servlet.ReaderService</implementationClass>
</service>
</beans>
很多人以为这样就行了,不,还没行,你指定了这个,那别人怎么访问呢。怎么把相应的请求转发到xfire那里,让它进行处理呢。这里又需要修改web.xml了。
修改后如下:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
id="WebApp_ID" version="3.0">
<servlet>
<servlet-name>XFireServlet</servlet-name>
<servlet-class>org.codehaus.xfire.transport.http.XFireConfigurableServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>XFireServlet</servlet-name>
<url-pattern>/services/*</url-pattern>
</servlet-mapping>
</web-app>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
id="WebApp_ID" version="3.0">
<servlet>
<servlet-name>XFireServlet</servlet-name>
<servlet-class>org.codehaus.xfire.transport.http.XFireConfigurableServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>XFireServlet</servlet-name>
<url-pattern>/services/*</url-pattern>
</servlet-mapping>
</web-app>
其实也就是添加了一个servlet和对应的mapping。接下来,我们在浏览器上直接输入:
二、使用Xstream进行java对象和XML格式的转化
我经历的项目使用webservice一般是和第三方厂商进行数据交互,所以制定系统对接时采取XML为基础的接口操作规范文档是很重要的。不管是我们发布webservice服务接口还是作为客户端调用别人的webservice接口,和自身原有系统整合时都难免需要将XML和POJO对象进行互相转化而便于开发。这时候我们可以使用XML解析器对XML进行解析后重新创建java对象,反之亦然。但这样做代码并不经济,我们需要对XML进行历遍和检索才能转成合格的java对象。所以,Xstream帮我们完成了这样重复的工作,我们只需要正确使用它,就可以很容易将两者相互转化。
Xstream是一种OXMapping 技术,是用来处理XML文件序列化的框架,在将JavaBean序列化,或将XML文件反序列化的时候,不需要其它辅助类和映射文件,使得XML序列化不再繁索。Xstream也可以将JavaBean序列化成Json或反序列化,使用非常方便。
不需要在配置文件中进行额外配置,只需要往工程中导入jar包即可,我所使用的jar包为:
Xstream提供了很多功能接口来自定义转化的需求。我们重点对下面几个接口的功能做一下介绍:
首先我们代码实例都使用这样的javabean来进行讲解:
package test.xstream.test;
public class Author {
private String name;
public Author(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
1、xStream.alias(String, Class),作用:将序列化中的类全量名称,用别名替换。如果不显示调用该接口,XML的tag会使用该javabean的类全量路径。
不使用别名alias时序列化出来的xml:
<test.xstream.test.Author>
<name>name</name>
</test.xstream.test.Author>
使用别名alias时序列化出来的xml:
<Author>
<name>name</name>
</Author>
使用方法:xstream.alias("Author",Author.class)
2、xstream.aliasField(String, Class, String),作用:使用别名替代属性名。
不使用别名aliasField时序列化出来的xml:
<Author>
<name>name</name>
</Author>
使用别名aliasField时序列化出来的xml:
<Author>
<author>name</author>
</Author>
使用方法:xstream.aliasFiel("author", Author.class, "name"),其实就是将类中的属性“name”指定别名“author”输入或输出。
3、xstream.registerLocalConverter(Class definedIn, String fieldName, Converter converter),作用:自定义转换器,可以将指定的class的属性(该属性一般是List类型)转入到特定的转换器中进行处理。
代码说明:
xstream.registerLocalConverter( AppBsChangeResult.class, "entityIDs", new ListConverter("accID"))
public class AppBsChangeResult extends AppBsBaseDriverResult {
private static Logger logger = Logger.getLogger(AppBsChangeResult.class);
private List<String> entityIDs = null;
public List<String> getEntityIDs() {
return entityIDs;
}
public void setEntityIDs(List<String> entityIDs) {
this.entityIDs = entityIDs;
}
}
public class ListConverter implements Converter {
public ListConverter(String tagName){
this.tagName = tagName;
}
private String tagName = null;
public String getTagName() {
return tagName;
}
public void setTagName(String tagName) {
this.tagName = tagName;
}
public void marshal(Object value, HierarchicalStreamWriter writer, MarshallingContext arg2) {
List<String> list = (List<String>) value;
for (String subval : list) {
// 为了转换成XML时,保证有<expands></expands>全标签,将值为空时不写标签只输出“”
if("".equals(subval) && list.size()==1){
writer.setValue(subval);
}else{
writer.startNode(tagName);
writer.setValue(subval);
writer.endNode();
}
}
}
public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
List<String> list = new ArrayList<String>();
while (reader.hasMoreChildren()) {
reader.moveDown();
String key = reader.getNodeName();
String value = null;
if (reader.hasMoreChildren()) {
value = (String)unmarshal(reader, context);
} else {
value = reader.getValue();
}
// insert into list
list.add(value);
reader.moveUp();
}
return list;
}
//覆写父类的方法,指明哪种java类型可以被转化器处理
public boolean canConvert(Class type) {
return type.equals(ArrayList.class);
}
}
执行xstream.registerLocalConverter( AppBsChangeResult.class, "entityIDs", new ListConverter("accID"))后,就可以将XML中N个accID标签的值转化为AppBsChangeResult类中属性"entityIDs",因为属性"entityIDs"是一个List<String>,所以就可以将N个accID的值都add进去这个List了。注意转化器的抽象接口marshal一般用在javabean对象转为XML的过程中(序列化),而抽象接口unmarshal用在XML转javabean对象的过程中(反序列化)。
4、xstream.addDefaultImplementation(Class, Class),作用:使用抽象类, 或父子类的转换
如果将抽象类或父类做为类的属性时, 将对象转化成xml时, 会在抽象属性对应的结点增class属性, 值为子类的包路径,
将class属性去掉办法:
xStream.addDefaultImplementation(Sun.class,Parent.class)
如果将抽象类或父类做为类的属性, 将xml串转化为对象时, 如果对应的结点带有class属性, 转化没有问题, 如果将xml串中的class属性去掉, 转换对象时, 就会抛异常:java.lang.InstantiationError
解决办法同样是:
xStream.addDefaultImplementation(Sun.class,Parent.class)
5、xstream.omitField(Class, String),作用: 定义某一个属性的值不进行xml序列化,即是对指定的字段不予处理。序列化时隐藏该字段,反序列化时该字段为null
6、xstream.useAttributeFor(Class, String),xstream.useAttributeFor(String, Class)和xstream.useAttributeFor(Class)
- xstream.useAttributeFor(String.class); 对所有String类型的字段定义为属性tag显示
- xstream.useAttributeFor("name", String.class); 对所有String类型的字段名成为name 定义为属性tag显示
- xstream.useAttributeFor(Person.class, "name"); 把字段节点设置成属性,单独使用以上方法
<test.Person>
<name>张三</name>
<age>19</age>
<string>李四</string>
<string>王五</string>
<string>赵六</string>
</test.Person>
将以下面XML格式输出:
<test.Person name="张三">
<age>19</age>
<string>李四</string>
<string>王五</string>
<string>赵六</string>
</test.Person>
7、xstream.addImplicitCollection(Class, String),作用:省略集合根节点
public class Person {
private String name;
private int age;
private List friends;
public Person(String name, int age, String... friends) {
this.name = name;
this.age = age;
this.friends = Arrays.asList(friends);
}
}
public class XstreamTest {
public static void main(String[] args) {
Person bean = new Person("张三", 19, "李四", "王五", "赵六");
XStream xStream = new XStream(new DomDriver());
xStream.addImplicitCollection(Person.class, "friends");
//序列化
String xml = xStream.toXML(bean);
System.out.println(xml);
//反序列化
bean=(Person)xStream.fromXML(xml);
System.out.println(bean);
}
}
xStream.addImplicitCollection(Person.class, "friends")使用前后XML对照如下。
使用前:
<test.Person>
<name>张三</name>
<age>19</age>
<friends class="java.util.Arrays$ArrayList">
<a class="string-array">
<string>李四</string>
<string>王五</string>
<string>赵六</string>
</a>
</friends>
</test.Person>
使用后:
<test.Person>
<name>张三</name>
<age>19</age>
<a class="string-array">
<string>李四</string>
<string>王五</string>
<string>赵六</string>
</a>
</test.Person>