Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SpringMVC注解之@ResponseBody #3

Open
kuitos opened this issue Aug 23, 2015 · 1 comment
Open

SpringMVC注解之@ResponseBody #3

kuitos opened this issue Aug 23, 2015 · 1 comment

Comments

@kuitos
Copy link
Owner

kuitos commented Aug 23, 2015

SpringMVC注解之@responsebody

原文写于 2013-04-18

web项目中会大量用到ajax请求实现前后台交互,以前处理后台返回给前台的集合数据的方式是这样的:

@RequestMapping("loadConfigUsers")
public void loadConfigUsers(String domain, HttpServletResponse response) {
    response.setCharacterEncoding("UTF-8");
    response.setContentType("text/html;charset=utf-8");
    List<Map<String, Object>> list = userConfigService.loadConfigUsers(domain);
    PrintWriter out = null;
    try {
        out = response.getWriter();
        out.print(JackSonMapper.toJsonString(list));
    } catch (IOException e) {
        logger.error("I/O出错", e);
    } finally {
        try {
            out.close();
        } catch (Exception e) {
            logger.error("关闭流出错", e);
        }
    }
}

也就是使用jackson将List<Map<String,Object>>对象转换为json格式的数组,如[{"a":"b"},{"c","d"}]。
有了@responsebody之后我们的代码就简单多了

@ResponseBody
@RequestMapping("loadConfigUsers")
public List<Map<String,String>> loadConfigUsers(String domain, HttpServletResponse response) {
    return userConfigService.loadConfigUsers(domain);
}

前台接收到的即为json格式数组,如[{"a":"b"},{"c","d"}]。SpringMVC底层会使用jackson将带有@responsebody的方法体的返回值转成标准的json格式。
想返回Map<String,String>格式的也一样

@ResponseBody
@RequestMapping("loadConfigUsers")
public Map<String,String> loadConfigUsers(String domain, HttpServletResponse response) {
    return userConfigService.loadConfigUsers(domain);
}

返回的json格式为 {"a":"b","c":"d"}。
也可以直接向前台返回String

@ResponseBody
@RequestMapping("loadConfigUsers")
public String loadConfigUsers(String domain, HttpServletResponse response) {
    return "success";
}

前台接收到的为 "success"。
但是在实际开发中碰到一个问题,返回List,Map,即前台接收到的为json格式字符串的时候中文字符都正常,但是直接返回String却会出现中文乱码问题。google一下发现SpringMVC是这样实现的。
SpringMVC对于注有@responsebody注解的方法返回值有自己的一系列转换器,当发现返回值为List,Map等集合类型时SpringMVC使用的是MappingJacksonHttpMessageConverter转换器,改转换器字符集设置的为UTF-8,附部分代码

public class MappingJacksonHttpMessageConverter extends AbstractHttpMessageConverter<Object> {
    // 设置默认字符集
    public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
    private ObjectMapper objectMapper = new ObjectMapper();
    private boolean prefixJson = false;
    /**
     * Construct a new {@code BindingJacksonHttpMessageConverter}.
     */
    public MappingJacksonHttpMessageConverter() {
        super(new MediaType("application", "json", DEFAULT_CHARSET));
    }
}

而对于返回值为String时使用的转换器则为StringHttpMessageConverter

public class StringHttpMessageConverter extends AbstractHttpMessageConverter<String> {
    // 设置默认字符集
    public static final Charset DEFAULT_CHARSET = Charset.forName("ISO-8859-1");
    private final List<Charset> availableCharsets;
    private boolean writeAcceptCharset = true;
    public StringHttpMessageConverter() {
        super(new MediaType("text", "plain", DEFAULT_CHARSET), MediaType.ALL);
        this.availableCharsets = new ArrayList<Charset>(Charset.availableCharsets().values());
    }
}

可以发现,两个转换器用的字符集竟然不一样,这个实在是难以理解,为毛用于处理同一个注解的两个转换器要用两种字符集??
经过一番google及测试,发现了有一种方式是可以解决StringHttpMessageConverter字符集的问题,即修改我们的springmvc-servlet.xml,在<mvc:annotation-driven />前加上这样一段配置

<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping" />

<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter" />

<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
    <property name="messageConverters">
        <list>
            <bean class="org.springframework.http.converter.StringHttpMessageConverter">
                <property name="supportedMediaTypes">
                    <list>
                        <value>text/plain;charset=UTF-8</value>
                    </list>
                </property>
            </bean>
            <bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter" />
        </list>
    </property>
</bean>

<mvc:annotation-driven />

即设置StringHttpMessageConverter可支持的媒体类型仅只有"text/plain;charset=UTF-8"一种。
另外还有一种是配置AnnotationMethodHandlerAdapter的messageConverters,即

<bean class="org.springframework.web.servlet.mvc.method.annotation.AnnotationMethodHandlerAdapter">       
    <property name="messageConverters">
            <list>
                <bean class="org.springframework.http.converter.StringHttpMessageConverter">
                    <property name="supportedMediaTypes">
                        <list>
                            <value>text/plain;charset=UTF-8</value>
                        </list>
                    </property>
                </bean>
                <bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter" />
            </list>
     </property>
</bean>

这种方式是将spring所有的messageConverters改为两种StringHttpMessageConverter、MappingJacksonHttpMessageConverter
使用这种配置就不能再用mvc:annotation-driven了,官方文档是这样写的。也就是说它会覆盖之前的配置

The above registers a RequestMappingHandlerMapping, a RequestMappingHandlerAdapter, and an ExceptionHandlerExceptionResolver (among others) in support of processing requests with annotated controller methods using annotations such as @RequestMapping , @ExceptionHandler, and others
This is the complete list of HttpMessageConverters set up by mvc:annotation-driven:
ByteArrayHttpMessageConverter converts byte arrays.
StringHttpMessageConverter converts strings.
ResourceHttpMessageConverter converts to/from org.springframework.core.io.Resource for all media types.
SourceHttpMessageConverter converts to/from a javax.xml.transform.Source.
FormHttpMessageConverter converts form data to/from a MultiValueMap<String, String>.
Jaxb2RootElementHttpMessageConverter converts Java objects to/from XML — added if JAXB2 is present on the classpath.
MappingJackson2HttpMessageConverter (or MappingJacksonHttpMessageConverter) converts to/from JSON — added if Jackson 2 (or Jackson) is present on the classpath.
AtomFeedHttpMessageConverter converts Atom feeds — added if Rome is present on the classpath.
RssChannelHttpMessageConverter converts RSS feeds — added if Rome is present on the classpath.

至于spring是如何选择可用的converter的,这里有一篇文章,有兴趣可以看下:这里

@cheetahlou
Copy link

写的很详细

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants