关于Spring返回json的问题
来源:互联网 发布:金庸x 知乎 编辑:程序博客网 时间:2024/06/14 05:37
今天测试了一下搭建一个新的SpringMVC项目
然后测试Controller返回String类型,加上了@ResponseBody,访问之后是可以正常访问到返回内容
然后我改成返回Map类型,访问之后直接报错。
在这之前首先我是:
1.没有配置StringHttpMessageConvertor
2.maven没有把json包引入
严重: Servlet.service() for servlet [dispatcher] in context with path [/ElecEmp] threw exception [Request processing failed; nested exception is java.lang.IllegalArgumentException: No converter found for return value of type: class java.util.HashMap] with root causejava.lang.IllegalArgumentException: No converter found for return value of type: class java.util.HashMap at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:187) at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.handleReturnValue(RequestResponseBodyMethodProcessor.java:174) at org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite.handleReturnValue(HandlerMethodReturnValueHandlerComposite.java:81) at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:132) at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:827) at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:738) at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85) at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:963) at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:897) at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970) at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861) at javax.servlet.http.HttpServlet.service(HttpServlet.java:622)
错误信息里看到没有converter用来处理HashMap
/** * Writes the given return type to the given output message. * @param value the value to write to the output message * @param returnType the type of the value * @param inputMessage the input messages. Used to inspect the {@code Accept} header. * @param outputMessage the output message to write to * @throws IOException thrown in case of I/O errors * @throws HttpMediaTypeNotAcceptableException thrown when the conditions indicated by {@code Accept} header on * the request cannot be met by the message converters */ @SuppressWarnings("unchecked") protected <T> void writeWithMessageConverters(T value, MethodParameter returnType, ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage) throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException { Object outputValue; Class<?> valueType; Type declaredType; if (value instanceof CharSequence) { outputValue = value.toString(); valueType = String.class; declaredType = String.class; } else { outputValue = value; valueType = getReturnValueType(outputValue, returnType); declaredType = getGenericType(returnType); } HttpServletRequest request = inputMessage.getServletRequest(); List<MediaType> requestedMediaTypes = getAcceptableMediaTypes(request); List<MediaType> producibleMediaTypes = getProducibleMediaTypes(request, valueType, declaredType); if (outputValue != null && producibleMediaTypes.isEmpty()) { throw new IllegalArgumentException("No converter found for return value of type: " + valueType); }..............看到报错的地方是因为producibleMediaTypes的值为空。getProducibleMediaTypes是获取produces的,看下源码: /** * Returns the media types that can be produced: * <ul> * <li>The producible media types specified in the request mappings, or * <li>Media types of configured converters that can write the specific return value, or * <li>{@link MediaType#ALL} * </ul> * @since 4.2 */ @SuppressWarnings("unchecked") protected List<MediaType> getProducibleMediaTypes(HttpServletRequest request, Class<?> valueClass, Type declaredType) { Set<MediaType> mediaTypes = (Set<MediaType>) request.getAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE); if (!CollectionUtils.isEmpty(mediaTypes)) { return new ArrayList<MediaType>(mediaTypes); } else if (!this.allSupportedMediaTypes.isEmpty()) { List<MediaType> result = new ArrayList<MediaType>(); for (HttpMessageConverter<?> converter : this.messageConverters) { if (converter instanceof GenericHttpMessageConverter && declaredType != null) { if (((GenericHttpMessageConverter<?>) converter).canWrite(declaredType, valueClass, null)) { result.addAll(converter.getSupportedMediaTypes()); } } else if (converter.canWrite(valueClass, null)) { result.addAll(converter.getSupportedMediaTypes()); } } return result; } else { return Collections.singletonList(MediaType.ALL); } }
请求的mediaType是从request中获取的,用google浏览器查看请求,看到请求头默认是:
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
因此mediaTypes的值不为空,到了下面的逻辑,是先获取全部支持的mediaType,我这边获取到
[application/octet-stream, text/plain, application/xml, text/xml, application/x-www-form-urlencoded, application/*+xml, multipart/form-data, */*]
而且messageConverter的值如下:
这些值如何获取呢,看源码:
/** * Return the media types supported by all provided message converters sorted * by specificity via {@link MediaType#sortBySpecificity(List)}. */ private static List<MediaType> getAllSupportedMediaTypes(List<HttpMessageConverter<?>> messageConverters) { Set<MediaType> allSupportedMediaTypes = new LinkedHashSet<MediaType>(); for (HttpMessageConverter<?> messageConverter : messageConverters) { allSupportedMediaTypes.addAll(messageConverter.getSupportedMediaTypes()); } List<MediaType> result = new ArrayList<MediaType>(allSupportedMediaTypes); MediaType.sortBySpecificity(result); return Collections.unmodifiableList(result); }
很明显,意思是获取messageConverters支持的mediatype,关键是看messageConverters是什么值,我看了一下源码,调用了此方法的往上追溯是Resolver,Adapter方法
也就是适配器,用来查询适配的controller,再往上到顶,是WebMvcConfigurationSupport(带有@Bean),调用了
/** * Provides access to the shared {@link HttpMessageConverter}s used by the * {@link RequestMappingHandlerAdapter} and the * {@link ExceptionHandlerExceptionResolver}. * This method cannot be overridden. * Use {@link #configureMessageConverters(List)} instead. * Also see {@link #addDefaultHttpMessageConverters(List)} that can be * used to add default message converters. */ protected final List<HttpMessageConverter<?>> getMessageConverters() { if (this.messageConverters == null) { this.messageConverters = new ArrayList<HttpMessageConverter<?>>(); configureMessageConverters(this.messageConverters); if (this.messageConverters.isEmpty()) { addDefaultHttpMessageConverters(this.messageConverters); } extendMessageConverters(this.messageConverters); } return this.messageConverters; }
configureMessageConverters是抽象方法,我觉得应该是实现了此方法的类可以对messageConverters做新的配置。
假如没有做配置,那么就还是空,因为开头我说了我没有配置messageConverters,所以这里拿到的messageConverters是空的。所以会调用addDefaultHttpMessageConverters
源码:
/** * Adds a set of default HttpMessageConverter instances to the given list. * Subclasses can call this method from {@link #configureMessageConverters(List)}. * @param messageConverters the list to add the default message converters to */ protected final void addDefaultHttpMessageConverters(List<HttpMessageConverter<?>> messageConverters) { StringHttpMessageConverter stringConverter = new StringHttpMessageConverter(); stringConverter.setWriteAcceptCharset(false); messageConverters.add(new ByteArrayHttpMessageConverter()); messageConverters.add(stringConverter); messageConverters.add(new ResourceHttpMessageConverter()); messageConverters.add(new SourceHttpMessageConverter<Source>()); messageConverters.add(new AllEncompassingFormHttpMessageConverter()); if (romePresent) { messageConverters.add(new AtomFeedHttpMessageConverter()); messageConverters.add(new RssChannelHttpMessageConverter()); } if (jackson2XmlPresent) { ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.xml().applicationContext(this.applicationContext).build(); messageConverters.add(new MappingJackson2XmlHttpMessageConverter(objectMapper)); } else if (jaxb2Present) { messageConverters.add(new Jaxb2RootElementHttpMessageConverter()); } if (jackson2Present) { ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json().applicationContext(this.applicationContext).build(); messageConverters.add(new MappingJackson2HttpMessageConverter(objectMapper)); } else if (gsonPresent) { messageConverters.add(new GsonHttpMessageConverter()); } }
所以默认的话MessageConverter有5个ByteArrayHttpMessageConverter,stringConverter,ResourceHttpMessageConverter,SourceHttpMessageConverter,AllEncompassingFormHttpMessageConverter
源码里也看到了:
private static boolean romePresent = ClassUtils.isPresent("com.rometools.rome.feed.WireFeed", WebMvcConfigurationSupport.class.getClassLoader()); private static final boolean jaxb2Present = ClassUtils.isPresent("javax.xml.bind.Binder", WebMvcConfigurationSupport.class.getClassLoader()); private static final boolean jackson2Present = ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", WebMvcConfigurationSupport.class.getClassLoader()) && ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", WebMvcConfigurationSupport.class.getClassLoader()); private static final boolean jackson2XmlPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.xml.XmlMapper", WebMvcConfigurationSupport.class.getClassLoader()); private static final boolean gsonPresent = ClassUtils.isPresent("com.google.gson.Gson", WebMvcConfigurationSupport.class.getClassLoader());..................... /** * Determine whether the {@link Class} identified by the supplied name is present * and can be loaded. Will return {@code false} if either the class or * one of its dependencies is not present or cannot be loaded. * @param className the name of the class to check * @param classLoader the class loader to use * (may be {@code null}, which indicates the default class loader) * @return whether the specified class is present */ public static boolean isPresent(String className, ClassLoader classLoader) { try { forName(className, classLoader); return true; } catch (Throwable ex) { // Class or one of its dependencies is not present... return false; } }
这些值的定义,所以假如maven没有配json相关的jar包,MessageConverter也就没有相应的处理json的converter。
所以回到开始那里,因为没有一个converter可以写hashmap到响应体里,所以就会报错。
所以结论就是,只要配置了json相关的jar包,就可以返回json类型的数据。比如com.fasterxml.jackson
阅读全文
0 0
- 关于Spring返回json的问题
- 关于spring json view返回json内容的问题
- 关于返回json数据格式的问题
- 关于jquery-ajax返回json的问题
- 关于Json返回null的问题
- 关于spring MVC 返回集合或对象自动转成json串的问题
- 关于application/json返回问题
- Spring-webmvc关于json的一些问题
- spring mvc解决返回json乱码的问题
- 关于EXT用json返回结果分页的问题
- 关于angularjs返回的json不转义问题
- 关于Http请求后返回json乱码的问题
- 关于Http请求后返回json乱码的问题
- 关于Http请求后返回json乱码的问题
- 关于springmvc 返回json数据null字段的显示问题
- 关于Http请求后返回json乱码的问题
- 关于Http请求后返回json乱码的问题
- 关于Http请求后返回json乱码的问题
- 【Java多线程】ThreadLocal实现原理
- Unable to locate package错误解决办法以及jdk的切换
- Huffman编码——文件压缩项目
- C语言:生产者-消费者问题、读者-写者问题
- C 语言DLL跨平台调用时,支持多线程TLS方法的使用
- 关于Spring返回json的问题
- java 树的应用
- [Unity] A* pathfinding project integrated with influence map
- 【JAVA】Spring 自动注入类注释详解
- TCP/UDP通信协议基础全集(区别,三次握手四次挥手)
- 联网请求操作okhttputils
- Python第三方库h5py——读取mat文件并显示值
- modern c++ design
- 给动态添加的按钮添加动态事件