问题有点理论化,创建JAXB上下文,marshaller和unmarshaller的成本是多少?
我发现我的代码可以从保持相同的JAXB上下文和可能与所有编组操作相同的编组器中受益,而不是在每次编组时创建上下文和编组器。
那么创建JAXB上下文和marshaller / unmarshaller的成本是多少?是否可以为每个编组操作创建context + marshaller,或者最好避免它?
答案 0 :(得分:213)
注意:我是EclipseLink JAXB (MOXy)主管,是JAXB 2(JSR-222)专家组的成员。
JAXBContext
是线程安全的,只应创建一次并重复使用,以避免多次初始化元数据的成本。 Marshaller
和Unmarshaller
不是线程安全的,但是可以轻量级创建,并且可以按操作创建。
答案 1 :(得分:35)
理想情况下,您应该拥有单身JAXBContext
以及Marshaller
和Unmarshaller
的本地实例。
JAXBContext
个实例是线程安全的,而Marshaller
和Unmarshaller
个实例不是线程安全的,不应该跨线程共享。
答案 2 :(得分:13)
遗憾的是,这在javadoc中没有具体描述。我能说的是Spring使用了一个在线程之间共享的全局JAXBContext,而它为每个编组操作创建了一个新的编组器,代码中有javadoc comment表示JAXB marshallers不一定是线程安全的。
我猜想创建一个JAXBContext是一项代价高昂的操作,因为它涉及扫描类和注释的包。但衡量它是了解最佳方式。
答案 3 :(得分:3)
我使用共享线程安全 JAXBContext 解决了这个问题,并解决了本地 un / marschallers 的问题 (理论上,会有多个 un / marshaller 实例,因为有访问它们的线程) 仅在 un / marshaller 的初始化时同步。
private final ThreadLocal<Unmarshaller> unmarshallerThreadLocal = new ThreadLocal<Unmarshaller>() {
protected synchronized Unmarshaller initialValue() {
try {
return jaxbContext.createUnmarshaller();
} catch (JAXBException e) {
throw new IllegalStateException("Unable to create unmarshaller");
}
}
};
private final ThreadLocal<Marshaller> marshallerThreadLocal = new ThreadLocal<Marshaller>() {
protected synchronized Marshaller initialValue() {
try {
return jaxbContext.createMarshaller();
} catch (JAXBException e) {
throw new IllegalStateException("Unable to create marshaller");
}
}
};
private final JAXBContext jaxbContext;
private MyClassConstructor(){
try {
jaxbContext = JAXBContext.newInstance(Entity.class);
} catch (JAXBException e) {
throw new IllegalStateException("Unable to initialize");
}
}
答案 4 :(得分:3)
JAXB 2.2(JSR-222)在“4.2 JAXBContext”一节中有这样的说法:
为了避免创建
JAXBContext
实例所涉及的开销,a 鼓励重用JAXB应用程序以重用JAXBContext实例。一个 需要实现抽象类JAXBContext 线程安全,因此,应用程序中的多个线程可以共享 相同的JAXBContext实例。[..]
JAXBContext类被设计为不可变的,因此线程安全。 考虑到可能采取的动态处理量 在创建JAXBContxt的新实例时,建议使用它 一个JAXBContext实例在线程之间共享并重用为 尽可能提高应用程序性能。
不幸的是,该规范没有对Unmarshaller
和Marshaller
的线程安全做出任何声明。所以最好假设它们不是。
答案 5 :(得分:1)
更好!!根据上面帖子中的好解决方案,在构造函数中创建上下文 just-once ,并保存它而不是类。
替换行:
private Class clazz;
这一个:
private JAXBContext jc;
这个主要构造函数:
private Jaxb(Class clazz)
{
this.jc = JAXBContext.newInstance(clazz);
}
所以在getMarshaller / getUnmarshaller中你可以删除这一行:
JAXBContext jc = JAXBContext.newInstance(clazz);
在我的情况下,这种改进使处理时间从60~70ms下降到5~10ms
答案 6 :(得分:1)
在枚举中创建JAXBContext并在应用程序线程中访问它以创建Marshaller / Unmarshaller就足够了。
public enum MyApplicationJAXBContext {
REQ(Request.class), RESP(Response.class);
private final JAXBContext jaxbContext;
MyApplicationJAXBContext(Class contextClass) {
try {
jaxbContext = JAXBContext.newInstance(contextClass);
} catch (JAXBException e)
throw new RunTimeException(e);
// Lets caller decide what to do ?
}
}
public JAXBContext getJaxbContext() {
return jaxbContext;
}
}
public class MyAppCallable implements Callable<Response> {
private final Request req;
public MyAppCallable(Request req) {
this.req = req;
}
public Response call() {
Marshaller marshaller = MyApplicationJAXBContext.REQ.getJaxbContext().createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
// Anything else you want to configure properties
Unmarshaller unmarshaller = MyApplicationJAXBContext.RESP.getJaxbContext().createUnmarshaller();
/**
All other logic you want to do after req/rsp usage and return Response
**/
}
}
答案 7 :(得分:0)
我通常使用ThreadLocal
类模式来解决这样的问题。
鉴于您需要为每个类使用不同的编组器,您可以将其与singleton
- 地图模式结合使用。
为你节省15分钟的工作时间。以下是我对Jaxb Marshallers和Unmarshallers的线程安全工厂的实现。
它允许您按如下方式访问实例...
Marshaller m = Jaxb.get(SomeClass.class).getMarshaller();
Unmarshaller um = Jaxb.get(SomeClass.class).getUnmarshaller();
您需要的代码是一个小的Jaxb类,如下所示:
public class Jaxb
{
// singleton pattern: one instance per class.
private static Map<Class,Jaxb> singletonMap = new HashMap<>();
private Class clazz;
// thread-local pattern: one marshaller/unmarshaller instance per thread
private ThreadLocal<Marshaller> marshallerThreadLocal = new ThreadLocal<>();
private ThreadLocal<Unmarshaller> unmarshallerThreadLocal = new ThreadLocal<>();
// The static singleton getter needs to be thread-safe too,
// so this method is marked as synchronized.
public static synchronized Jaxb get(Class clazz)
{
Jaxb jaxb = singletonMap.get(clazz);
if (jaxb == null)
{
jaxb = new Jaxb(clazz);
singletonMap.put(clazz, jaxb);
}
return jaxb;
}
// the constructor needs to be private,
// because all instances need to be created with the get method.
private Jaxb(Class clazz)
{
this.clazz = clazz;
}
/**
* Gets/Creates a marshaller (thread-safe)
* @throws JAXBException
*/
public Marshaller getMarshaller() throws JAXBException
{
Marshaller m = marshallerThreadLocal.get();
if (m == null)
{
JAXBContext jc = JAXBContext.newInstance(clazz);
m = jc.createMarshaller();
marshallerThreadLocal.set(m);
}
return m;
}
/**
* Gets/Creates an unmarshaller (thread-safe)
* @throws JAXBException
*/
public Unmarshaller getUnmarshaller() throws JAXBException
{
Unmarshaller um = unmarshallerThreadLocal.get();
if (um == null)
{
JAXBContext jc = JAXBContext.newInstance(clazz);
um = jc.createUnmarshaller();
unmarshallerThreadLocal.set(um);
}
return um;
}
}