Spring 3.0.5的HTTP消息转换器在并发请求时生成不一致的对象

时间:2013-05-08 05:51:26

标签: spring spring-mvc concurrency jaxb

我正在关注 spring 3.0.5 HTTP消息转换器,以便自动获取控制器级别的java对象。它适用于顺序请求。

但是在服务器上提供来自REST客户端的并发请求时,java对象会出现不一致。我通过REST客户端提供相同类型的请求,但数据存在一些差异。

某些内部实例对象值为null /空请求对象甚至存在于XML中。 (通过过滤级别和控制器级别的日志验证)

请注意,对象的map(键值对)中始终存在一个不一致的事情。不知道有什么理由。

我正在使用以下环境:

  1. Spring 3.0.5
  2. HTTP消息转换器
  3. Tomcat 1.6
  4. jaxb 2.2
  5. 以下是xml:

    的配置
        <bean id="messageAdapter"
        class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
        <property name="messageConverters">
            <list>
                <ref bean="marshallingHttpMessageConverter" />
            </list>
        </property>
    </bean>
    
        <bean id="marshallingHttpMessageConverter" class="org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter">
    </bean>
    

    控制器代码如下:

    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.http.HttpStatus;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.bind.annotation.ResponseStatus;
    
    import com.sfnt.saas.provisioning.service.ProvisioningInterfaceManager;
    import com.sfnt.saas.provisioning.util.messages.ContractData;
    import com.sfnt.saas.provisioning.util.messages.ContractInfo;
    
    @Controller
    public class ProvisioningController {
    
        private final Log log = LogFactory.getLog(this.getClass());
    
        @Autowired
        private ProvisioningInterfaceManager provisioningInterfaceManager;
    
        @RequestMapping(value = {"/1.0/Contract"}, method = RequestMethod.POST)
        @ResponseStatus(HttpStatus.CREATED)
        public void deployContract(HttpServletRequest request,
                HttpServletResponse response, @RequestBody ContractData contractData)
                throws RuntimeException {
    
            ContractInfo contractInfo = null;
            contractInfo = provisioningInterfaceManager
                    .deployContract(contractData);
            response.addHeader("Location", request.getRequestURL() + "/"
                    + contractInfo.getContract().getContract());
        }
    }
    

    请帮助!!!

2 个答案:

答案 0 :(得分:0)

我自己解决了这个问题,我需要做的是如下:

正如Spring提供的HttpMessageConverter org.springframework.oxm.jaxb.Jaxb2Marshaller正在为每个请求创建Marshaller / Unmarshaller而不是全局创建的JAXBContext对象。

所以我需要做的 - 创建我自己的自定义HttpMessageConverter并在每个请求上创建marshaller / unmarshaller以及JAXBContext,之后我的问题消失了。:)下面是示例代码:

<强> XML:

    <bean id="messageAdapter"
        class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
        <property name="messageConverters">
            <list>
                <ref bean="marshallingHttpMessageConverter" />
            </list>
        </property>
    </bean>

    <bean id="marshallingHttpMessageConverter"
        class="org.springframework.http.converter.xml.MarshallingHttpMessageConverter">
        <property name="marshaller" ref="jaxbConvertor" />
        <property name="unmarshaller" ref="jaxbConvertor" />
    </bean>

    <bean id="jaxbConvertor" class="com.sfnt.saas.provisioning.web.JAXBConvertor">
        <property name="classesToBeBound">
            <list>
                      <value>com.project.yourclass1</value>
                      <value>com.project.yourclass2</value>
            </list>
        </property>
    </bean>

<强> CustomConveter:

import java.io.IOException;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.transform.Result;
import javax.xml.transform.Source;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.oxm.Marshaller;
import org.springframework.oxm.UncategorizedMappingException;
import org.springframework.oxm.Unmarshaller;
import org.springframework.oxm.XmlMappingException;
import org.springframework.oxm.jaxb.Jaxb2Marshaller;

/**
 * This class will be responsible for providing auto conversion of
 * request/response body.
 * 
 * <h1>Note:</h1>
 * <p>
 * There is need to create custom converter as
 * <code>spring provided converter is
 * generating inconsistent data object in unmarshaling when concurrent request
 * came.</code>
 * <p>
 * In spring provided converter there is global JAXBContext is created and used
 * in all marshaling/unmarshaling that create object inconsistency when
 * concurrent request came.
 * 
 * @see Jaxb2Marshaller
 * @author Atul Kumar
 */
public class JAXBConvertor implements Marshaller, Unmarshaller {
    private Class<?>[] classesToBeBound;
    private final Log log = LogFactory.getLog(this.getClass());

    /**
     * This method will create JAXBContext in each request which is not done in
     * spring provided converter
     */
    @Override
    public Object unmarshal(Source source) throws IOException,
            XmlMappingException {
        try {
            javax.xml.bind.Unmarshaller jaxbUnmarshaller = JAXBContext
                    .newInstance(getClassesToBeBound()).createUnmarshaller();
            return jaxbUnmarshaller.unmarshal(source);
        } catch (JAXBException e) {
            log.error("Error in unmarshallering xml to object", e);
            throw new UncategorizedMappingException(e.getMessage(), e);
        }
    }

    @Override
    public boolean supports(Class<?> clazz) {
        return true;
    }

    /**
     * This method will create JAXBContext in each request which is not done in
     * spring provided converter
     */
    @Override
    public void marshal(Object graph, Result result) throws IOException,
            XmlMappingException {
        try {
            JAXBContext jaxbContext = JAXBContext.newInstance(graph.getClass());
            javax.xml.bind.Marshaller jaxbMarshaller = jaxbContext
                    .createMarshaller();
            jaxbMarshaller.marshal(graph, result);
        } catch (JAXBException e) {
            log.error("Error in marshallering object to xml", e);
            throw new UncategorizedMappingException(e.getMessage(), e);
        }
    }

    /**
     * Set the list of Java classes to be recognized by a newly created
     * JAXBContext. Setting this property or {@link #setContextPath
     * "contextPath"} is required.
     */
    public synchronized void setClassesToBeBound(Class<?>... classesToBeBound) {
        this.classesToBeBound = classesToBeBound;
    }

    /**
     * Return the list of Java classes to be recognized by a newly created
     * JAXBContext.
     */
    public Class<?>[] getClassesToBeBound() {
        return this.classesToBeBound;
    }
}

答案 1 :(得分:0)