[Web Service] Rop在进行响应或输入的序列化时存在线程安全问题

stamen 2012-05-17
    由于我原来的Marshaller是一个RopResponse对应一个实例,也即一个RopResponse类是共享一个单实例的Marshaller。当时,我认为Marshaller是线程安全的,但是经过测试,Marshaller是非线程安全的:
    可以看这篇文章:
http://jaxb.java.net/guide/Performance_and_thread_safety.html
    我马上会修复这个BUG的。
stamen 2012-05-18
RopResponseMarshaller的实现类包括以下两个:
  • JacksonJsonRopResponseMarshaller:内部使用org.codehaus.jackson.map.ObjectMapper进行JSON序列化
  • JaxbXmlRopResponseMarshaller:内部使用javax.xml.bind.Marshaller进行XML序列化


org.codehaus.jackson.map.ObjectMapper是线程安全的(参见:http://wiki.fasterxml.com/JacksonFAQThreadSafety),而javax.xml.bind.Marshaller是非线程安全的。原来的Rop对ObjectMapper和Marshaller都采用了单实例的方式,因此存在线程安全的问题。

关于javax.xml.bind.Marshaller非线程安全的说明参见:http://jaxb.java.net/guide/Performance_and_thread_safety.html

不但javax.xml.bind.Marshaller是非线程安全的,Spring OXM的Marshaller也是非线程安全的,参见其javadoc的说明:
引用

createMarshaller

protected Marshaller createMarshaller()
Return a newly created JAXB marshaller. JAXB marshallers are not necessarily thread safe.


原来的JaxbXmlRopResponseMarshaller:
public class JaxbXmlRopResponseMarshaller implements RopResponseMarshaller {

    private static Map<Class, Marshaller> marshallerMap = new HashMap<Class, Marshaller>();

    public void marshaller(RopResponse response, OutputStream outputStream) {
        try {
            Marshaller m = getMarshaller(response);
            m.marshal(response, outputStream);
        } catch (JAXBException e) {
            throw new RopException(e);
        }
    }

    private Marshaller getMarshaller(RopResponse response) throws JAXBException {
        if (!marshallerMap.containsKey(response.getClass())) {
            JAXBContext context = JAXBContext.newInstance(response.getClass());
            Marshaller marshaller = context.createMarshaller();//Marshaller是非线程安全的,不能多线程共用 !!!!
            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
            marshaller.setProperty(Marshaller.);
            marshaller.setProperty(Marshaller.JAXB_ENCODING, "utf-8");
            marshallerMap.put(response.getClass(), marshaller);
        }
        return marshallerMap.get(response.getClass());
    }
}

更改为:
public class JaxbXmlRopResponseMarshaller implements RopResponseMarshaller {

    private static Map<Class, JAXBContext> jaxbContextHashMap = new ConcurrentHashMap<Class, JAXBContext>();

    public void marshaller(RopResponse response, OutputStream outputStream) {
        try {
            Marshaller m = buildMarshaller(response);
            m.marshal(response, outputStream);
        } catch (JAXBException e) {
            throw new RopException(e);
        }
    }


    public Marshaller buildMarshaller(RopResponse response) throws JAXBException {
        if (!jaxbContextHashMap.containsKey(response.getClass())) {
            JAXBContext context = JAXBContext.newInstance(response.getClass());//JAXBContext是线程安全的
            jaxbContextHashMap.put(response.getClass(), context);
        }
        JAXBContext context = jaxbContextHashMap.get(response.getClass());
        Marshaller marshaller = context.createMarshaller();//每次创建一个新的Marshaller
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.setProperty(Marshaller.JAXB_ENCODING, "utf-8");
        return marshaller;
    }
}


其实,根据测试创建Marshaller的时间很短,只有1毫秒左右,而真正慢的是创建JAXBContext,大家100毫秒左右。所以,这样改后,对Rop序列化的性能不会有什么影响,因为JAXBContext是共享的。

已经同步到git中。
Global site tag (gtag.js) - Google Analytics