您的位置:首页 > 其它

JAXB 线程安全问题解决办法

2015-01-14 09:00 337 查看
项目开发中涉及到对象与XML的转换,考虑到代码的简洁性,一直在使用java内置的JAXB来完成这项任务
一直在用的方法:
static Map<String,Marshaller> mars
static Map<String,Unmarshaller> umars
这样做的好处是对于相同的类名,不用重复创建marshaller。
但在一个银行项目上线中发现,经常会发生ArrayIndexOutOfBoundsException,经过google一番搜索,发现问题在于marshaller的marshal方法不是线程安全的,而JAXBContext.newInstance()是线程安全的。为了解决这一问题,需要将代码进行改造
为了尽量少的重复创建marshaller对象(创建marshaller对象的操作官方认为是比较耗CPU的)这里使用对象池的概念,开发包使用commons-pool,这里用到了KeyedObjectPool

static KeyedObjectPool marPool = new GenericKeyedObjectPool(new JaxbMarshallerFactory());
static KeyedObjectPool unmarPool = new GenericKeyedObjectPool(new JaxbUnmarshallerFactory());
class JaxbUnmarshallerFactory extends JaxbMarshallerFactory{
@Override
public Object makeObject(Object key) throws Exception {
Class<?> clazz = (Class<?>)key;
if(contextMap.containsKey(key)){
JAXBContext context = JAXBContext.newInstance(clazz);
contextMap.put(clazz, context);
}
return contextMap.get(clazz).createUnmarshaller();
}
}
class JaxbMarshallerFactory implements KeyedPoolableObjectFactory{

protected Map<Class<?>,JAXBContext> contextMap = new HashMap<Class<?>,JAXBContext>();

@Override
public Object makeObject(Object key) throws Exception {
Class<?> clazz = (Class<?>)key;
if(!contextMap.containsKey(clazz)){
JAXBContext context = JAXBContext.newInstance(clazz);
contextMap.put(clazz, context);
}
return contextMap.get(clazz).createMarshaller();
}

@Override
public void destroyObject(Object key, Object obj) throws Exception {
contextMap.remove(key);
}

@Override
public boolean validateObject(Object key, Object obj) {
return true;
}

@Override
public void activateObject(Object key, Object obj) throws Exception {
// System.out.println("user "+Thread.currentThread()+" borrow "+obj.hashCode());
}

@Override
public void passivateObject(Object key, Object obj) throws Exception {
// System.out.println("user "+Thread.currentThread()+" return "+obj.hashCode());
}
}
这里定义了两个对象池工厂,分别保存marshaller和unmarshaller,感觉应该可以合并到一个工厂中,后面的实践中再加以改进。
在使用时应该注意borrowObject并使用完成后一定要记得returnObject,这样才能达到对象池的效果
public static void toOutputStream(Object o,OutputStream os) throws JAXBException{
Marshaller mar;
try {
// 从线程池中借出
mar = (Marshaller)marPool.borrowObject(o.getClass());
// 转换对象到XML
mar.marshal(o, os);
// 归还到对象池
marPool.returnObject(o.getClass(), mar);
} catch (Exception e) {
logger.error(e);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐