我有如下代码:
- Job job = getRestTemplate().postForObject(url,postData,Job.class);
我的applicationContext.xml看起来像:
- <bean id="restTemplate" class="org.springframework.web.client.RestTemplate">
- <constructor-arg ref="httpClientFactory"/>
- <property name="messageConverters">
- <list>
- <bean class="org.springframework.http.converter.xml.MarshallingHttpMessageConverter">
- <property name="marshaller" ref="jaxbMarshaller"/>
- <property name="unmarshaller" ref="jaxbMarshaller"/>
- </bean>
- <bean class="org.springframework.http.converter.FormHttpMessageConverter"/>
- <bean class="org.springframework.http.converter.StringHttpMessageConverter"/>
- </list>
- </property>
- </bean>
- <bean id="jaxbMarshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
- <property name="classesToBeBound">
- <list>
- <value>domain.fullspec.Job</value>
- <value>domain.fullspec.Exception</value>
- </list>
- </property>
- </bean>
当我尝试使这个电话和服务失败,我得到:
在postForObject()调用中,我要求一个Job.class,而不是得到一个,它正在变得不安.
我想我需要能够做的事情是:
- Object o = getRestTemplate().postForObject(url,Object.class);
- if (o instanceof Job.class) {
- ...
- else if (o instanceof Exception.class) {
- }
但是这不行,因为JAXB抱怨说它不知道如何组织到Object.class – 并不奇怪.
我试图创建MarshallingHttpMessageConverter的子类并重写readFromSource()
protected Object readFromSource(类clazz,HttpHeaders头,源代码){
- Object o = null;
- try {
- o = super.readFromSource(clazz,headers,source);
- } catch (Exception e) {
- try {
- o = super.readFromSource(MyCustomException.class,source);
- } catch (IOException e1) {
- log.info("Failed readFromSource "+e);
- }
- }
- return o;
- }
不幸的是,这不工作,因为在我重试之前,源代码中的底层输入流已被关闭.
有意见的建议,
汤姆
更新:我已经通过获取一个inputStream的副本来工作
- protected Object readFromSource(Class<?> clazz,HttpHeaders headers,Source source) {
- InputStream is = ((StreamSource) source).getInputStream();
- // Take a copy of the input stream so we can use it for initial JAXB conversion
- // and if that fails,we can try to convert to Exception
- CopyInputStream copyInputStream = new CopyInputStream(is);
- // input stream in source is empty now,so reset using copy
- ((StreamSource) source).setInputStream(copyInputStream.getCopy());
- Object o = null;
- try {
- o = super.readFromSource(clazz,source);
- // we have Failed to unmarshal to 'clazz' - assume it is <exception> and unmarshal to MyCustomException
- } catch (Exception e) {
- try {
- // reset input stream using copy
- ((StreamSource) source).setInputStream(copyInputStream.getCopy());
- o = super.readFromSource(MyCustomException.class,source);
- } catch (IOException e1) {
- e1.printStackTrace();
- }
- e.printStackTrace();
- }
- return o;
- }
CopyInputStream取自http://www.velocityreviews.com/forums/t143479-how-to-make-a-copy-of-inputstream-object.html,我将其粘贴到这里.
- import java.io.ByteArrayInputStream;
- import java.io.ByteArrayOutputStream;
- import java.io.IOException;
- import java.io.InputStream;
- public class CopyInputStream
- {
- private InputStream _is;
- private ByteArrayOutputStream _copy = new ByteArrayOutputStream();
- /**
- *
- */
- public CopyInputStream(InputStream is)
- {
- _is = is;
- try
- {
- copy();
- }
- catch(IOException ex)
- {
- // do nothing
- }
- }
- private int copy() throws IOException
- {
- int read = 0;
- int chunk = 0;
- byte[] data = new byte[256];
- while(-1 != (chunk = _is.read(data)))
- {
- read += data.length;
- _copy.write(data,chunk);
- }
- return read;
- }
- public InputStream getCopy()
- {
- return (InputStream)new ByteArrayInputStream(_copy.toByteArray());
- }
- }
解决方法
我读了RestTemplate source code,你目前正在调用这个API:
- public <T> T postForObject(URI url,Object request,Class<T> responseType) throws RestClientException {
- HttpEntityRequestCallback requestCallback = new HttpEntityRequestCallback(request,responseType);
- HttpMessageConverterExtractor<T> responseExtractor =
- new HttpMessageConverterExtractor<T>(responseType,getMessageConverters());
- return execute(url,HttpMethod.POST,requestCallback,responseExtractor);
- }
您可以看到,它会根据您的响应类型返回类型T.您可能需要做的是对RestTemplate进行子类化,并添加一个返回Object而不是类型T的新的PostForObject()API,以便您可以对返回的对象执行instanceof检查.
UPDATE
我一直在想这个问题的解决方案,而不是使用内置的RestTemplate,为什么不自己写?我认为比子类RestTemplate更好的添加一个新的方法.
这是我的例子…授予,我没有测试这个代码,但它应该给你一个想法:
- // reuse the same marshaller wired in RestTemplate
- @Autowired
- private Jaxb2Marshaller jaxb2Marshaller;
- public Object genericPost(String url) {
- // using Commons HttpClient
- HttpClient client = new HttpClient();
- PostMethod method = new PostMethod(url);
- // add your data here
- method.addParameter("data","your-data");
- try {
- int returnCode = client.executeMethod(method);
- // status code is 200
- if (returnCode == HttpStatus.SC_OK) {
- // using Commons IO to convert inputstream to string
- String xml = IoUtil.toString(method.getResponseBodyAsStream());
- return jaxb2Marshaller.unmarshal(new StreamSource(new ByteArrayInputStream(xml.getBytes("UTF-8"))));
- }
- else {
- // handle error
- }
- }
- catch (Exception e) {
- throw new RuntimeException(e);
- }
- finally {
- method.releaseConnection();
- }
- return null;
- }
如果有些情况需要重新使用RestTemplate中的一些API,那么您可以构建一个适配器来封装您的自定义实现并重用RestTemplate API,而不会在您的代码中实际暴露RestTemplate API.
例如,您可以创建一个适配器接口,如下所示:
- public interface MyRestTemplateAdapter {
- Object genericPost(String url);
- // same signature from RestTemplate that you want to reuse
- <T> T postForObject(String url,Class<T> responseType,Object... uriVariables);
- }
具体的定制休息模板看起来像这样:
- public class MyRestTemplateAdapterImpl implements MyRestTemplateAdapter {
- @Autowired
- private RestTemplate restTemplate;
- @Autowired
- private Jaxb2Marshaller jaxb2Marshaller;
- public Object genericPost(String url) {
- // code from above
- }
- public <T> T postForObject(String url,Object... uriVariables) {
- return restTemplate.postForObject(url,request,responseType);
- }
- }