【问题标题】:thread safety when using spring WebServiceTemplate and Jaxb2Marshaller使用 spring WebServiceTemplate 和 Jaxb2Marshaller 时的线程安全
【发布时间】:2010-08-13 15:49:01
【问题描述】:

我以编程方式使用 spring WebServiceTemplate 作为 Web 服务客户端,即没有实例化 spring 容器。我正在使用 Jaxb2Marshaller 进行编组/解组。在我的应用程序中,我创建了一个 SaajSoapMessageFactory 实例和一个 Jaxb2Marshaller 实例。我还创建了 WebServiceTemplate 的单个实例,并分配了之前创建的 SaajSoapMessageFactory 和 Jaxb2Marshaller 实例。

我创建的 WebServiceTemplate 以多线程方式使用,即多个线程可以同时调用 marshalSendAndReceive。我的问题是 - 我的配置线程安全吗?我担心 Jaxb2Marshaller。 javadoc 说 Jaxb2Marshallers 不一定是线程安全的。如何在不重新初始化 Jaxb 上下文的情况下以线程安全的方式使用 Jaxb2Marshaller?

顺便说一句:查看 spring reference 中的示例 spring-ws 配置让我相信 Jaxb2Marshaller 是线程安全的,但 Javadoc 似乎与此相矛盾。

【问题讨论】:

    标签: java spring jaxb spring-ws jaxb2


    【解决方案1】:

    Jaxb2Marshaller 的 javadoc 没有以某种方式提及线程安全,所以我不确定您为什么认为它不是。如果它不是线程安全的,javadoc 会说得很清楚。

    您对WebServiceTemplateSaajSoapMessageFactoryJaxb2Marshaller 单例的配置非常好,而且完全是线程安全的。

    【讨论】:

    • createMarshaller() [static.springsource.org/spring-ws/sites/1.5/apidocs/org/… 方法的定义在我心中播下了怀疑的种子。它说“JAXB 编组器不一定是线程安全的。”我知道他们在谈论 javax.xml.bind.Marshaller 但我不确定 Jaxb2Marshaller 如何使用此类(或此接口的实现)。
    • @neesh:啊,好的。这是真的,MarshallerUnmarshaller 对象不是线程安全的,这就是 Spring-WS 不在线程之间共享它们的原因。这是关于正确使用 JAXB API。
    • 前段时间我遇到过这个问题,我需要在多线程流中使用单例编组器手动解组,在我确保每个线程都有自己的实例之前,它肯定行为不端。
    • Spring Jaxb2Marshaller 包含一个单例 JAXBContext 并按照 JAXB 的建议为每个编组操作创建一个 JAXB2Marshaller。所以 Spring Jaxb2Marshaller 有正确的线程安全实现。
    • Jaxb2Marshaller 运行良好,但名称不佳;它可能应该被称为 Jaxb2MarshallerWrapper 以表明它不是真正的 JAXB Marshaller。
    【解决方案2】:

    org.springframework.oxm.jaxb.Jaxb2Marshaller 类是线程安全的,因为它为每个编组事件创建 javax.xml.bind.Marshaller 的新实例,请参阅 Spring 5.2.6 的原始源代码:

    @Override
    public void marshal(Object graph, Result result, @Nullable MimeContainer mimeContainer) throws XmlMappingException {
        try {
            Marshaller marshaller = createMarshaller();
            if (this.mtomEnabled && mimeContainer != null) {
                marshaller.setAttachmentMarshaller(new Jaxb2AttachmentMarshaller(mimeContainer));
            }
            if (StaxUtils.isStaxResult(result)) {
                marshalStaxResult(marshaller, graph, result);
            }
            else {
                marshaller.marshal(graph, result);
            }
        }
        catch (JAXBException ex) {
            throw convertJaxbException(ex);
        }
    }
    
    /**
     * Return a newly created JAXB marshaller.
     * <p>Note: JAXB marshallers are not necessarily thread-safe.
     * This method is public as of 5.2.
     * @since 5.2
     * @see #createUnmarshaller()
     */
    public Marshaller createMarshaller() {
        try {
            Marshaller marshaller = getJaxbContext().createMarshaller();
            initJaxbMarshaller(marshaller);
            return marshaller;
        }
        catch (JAXBException ex) {
            throw convertJaxbException(ex);
        }
    }
    

    Unmarshaller 也遇到了类似的情况。

    【讨论】:

      【解决方案3】:

      创建几个Jaxb2Marshaller(比如说五个),将它们放入一个池中(使用LinkedBlockingQueue)。创建线程时,将其传递给队列。

      当一个线程需要一个时,take() 来自队列/池一个。当池为空时,线程将阻塞此调用。

      当一个线程使用Jaxb2Marshallerput() 完成后,它会回到队列中,以便其他线程可以使用它。

      如果您发现线程在等待编组程序时过于频繁地阻塞,请向队列中添加更多线程(参见第一步)。这样,您可以轻松地调整池的大小(甚至使其可配置)。然后队列会自动分发它们。

      【讨论】:

      • 这会起作用,并且池的想法被广泛用于实例化繁重的对象,但是您必须管理对象的状态等等,这在连接池中完成。但是 Marshaller 并不是一个需要实例化的繁重对象。 JaxbContext 是。
      猜你喜欢
      • 2011-12-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-10-19
      • 2011-10-08
      • 2013-02-06
      • 1970-01-01
      相关资源
      最近更新 更多