Elim的博客

用来记录一些原创性的总结


Elim

JAXB之监听器

在进行marshal和unmarshal的时候JAXB为我们提供了对应的监听器,允许我们在marshal和unmarshal的过程中对当前对象做一些操作或者记录一些日志等。

marshal监听器

marshal过程中的监听器是对应的是Marshaller.Listener抽象类,其定义如下:

   public static abstract class Listener {
        
        public void beforeMarshal(Object source) {
        }

        
        public void afterMarshal(Object source) {
        }
    }

默认都是空实现,beforeMarshal方法用于在转换对象为XML之前回调,afterMarshal方法用于在转换对象为XML之后回调,参数source就是当前正在转换为XML的对象。监听器是通过Marshaller.setListener(Listener listener)来指定的,其会对当前Marshaller进行的对象中的每一个复杂对象转换为XML时回调。假设有下面这样的类定义:

@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name="root")
class OrgHolder {
	private Org org;

	public Org getOrg() {
		return org;
	}

	public void setOrg(Org org) {
		this.org = org;
	}
	
}

@XmlAccessorType(XmlAccessType.FIELD)
class Org {

	private String no;
	private String name;
	
	public String getNo() {
		return no;
	}
	public void setNo(String no) {
		this.no = no;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	
}

我们期望在转换对象为XML时记录转换过程,所以定义了如下这样的监听器:

class GlobalMarshalListener extends Marshaller.Listener {

	@Override
	public void beforeMarshal(Object source) {
		System.out.println("马上要被marshal的对象是:" + source);
	}

	@Override
	public void afterMarshal(Object source) {
		System.out.println("刚刚被marshal的对象是:" + source);
	}
	
}

运行如下测试程序:

@Test
public void test() throws Exception {
	
	OrgHolder holder = this.buildOrgHolder();
	JAXBContext jaxbContext = JAXBContext.newInstance(OrgHolder.class);
	Marshaller marshaller = jaxbContext.createMarshaller();
	marshaller.setListener(new GlobalMarshalListener());//指定Listener,全局的,对当前Marshaller中所有的对象marshal都起作用
	StringWriter writer = new StringWriter();
	marshaller.marshal(holder, writer);
	
}

private OrgHolder buildOrgHolder() {
	OrgHolder holder = new OrgHolder();
	Org org = new Org();
	org.setNo("A001");
	org.setName("XXX");
	holder.setOrg(org);
	return holder;
}

我们会看到如下这样的输出:

马上要被marshal的对象是:com.elim.learn.basic.jaxb.OrgHolder@5577140b
马上要被marshal的对象是:com.elim.learn.basic.jaxb.OrgHolder@5577140b
马上要被marshal的对象是:com.elim.learn.basic.jaxb.Org@1c6b6478
刚刚被marshal的对象是:com.elim.learn.basic.jaxb.Org@1c6b6478
刚刚被marshal的对象是:com.elim.learn.basic.jaxb.OrgHolder@5577140b
刚刚被marshal的对象是:com.elim.learn.basic.jaxb.OrgHolder@5577140b

从输出中我们可以看到根对应的marshal过程中对应的监听器方法被调用了两次,这应该是一个bug,所以需要确保监听器中进行的操作是幂等的。

unmarshal监听器

unmarshal过程中设置的监听器是Unmarshaller.Listener,其定义如下:

public static abstract class Listener {
    public void beforeUnmarshal(Object target, Object parent) {
    }

    public void afterUnmarshal(Object target, Object parent) {
    }
}

beforeUnmarshal方法将在当前对象被实例化,但是在XML转换为对象前调用,afterUnmarshal方法将在XML转换为对象后调用。参数target是当前正在被unmarshal的对象,parent是持有当前对象的引用的对象,即所谓的父对象。unmarshal过程中使用的Listener的示例如下:

class GlobalUnmarshalListener extends Unmarshaller.Listener {

	@Override
	public void beforeUnmarshal(Object target, Object parent) {
		System.out.println("马上要被unmarshal的对象是:" + target + ",该对象的父级对象是:" + parent);
	}

	@Override
	public void afterUnmarshal(Object target, Object parent) {
		System.out.println("刚刚被unmarshal的对象是:" + target + ",该对象的父级对象是:" + parent);
	}
	
}

测试代码如下:

@Test
public void test() throws Exception {
	
	OrgHolder holder = this.buildOrgHolder();
	JAXBContext jaxbContext = JAXBContext.newInstance(OrgHolder.class);
	Marshaller marshaller = jaxbContext.createMarshaller();
	StringWriter writer = new StringWriter();
	marshaller.marshal(holder, writer);
	
	Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
	unmarshaller.setListener(new GlobalUnmarshalListener());
	unmarshaller.unmarshal(new StringReader(writer.toString()));
	
}

输出如下:

马上要被unmarshal的对象是:com.elim.learn.basic.jaxb.OrgHolder@26ba2a48,该对象的父级对象是:null
马上要被unmarshal的对象是:com.elim.learn.basic.jaxb.Org@5f2050f6,该对象的父级对象是:com.elim.learn.basic.jaxb.OrgHolder@26ba2a48
刚刚被unmarshal的对象是:com.elim.learn.basic.jaxb.Org@5f2050f6,该对象的父级对象是:com.elim.learn.basic.jaxb.OrgHolder@26ba2a48
刚刚被unmarshal的对象是:com.elim.learn.basic.jaxb.OrgHolder@26ba2a48,该对象的父级对象是:null

Unmarshaller.Listener和Marshaller.Listener作用的都是当前unmarshal或marshal中对应的所有的对象,对每个对象进行转换时都将进行调用,如果我们只是希望对某个具体的对象进行转换时进行监听,则可以使用实例级别的监听,此种用法可以参考下一篇文章的介绍。

(注:本文由Elim写于2017年9月18日)