복붙노트

[SPRING] Json 일을 할 @ResponseBody가있는 사용자 정의 HttpMessageConverter

SPRING

Json 일을 할 @ResponseBody가있는 사용자 정의 HttpMessageConverter

나는 잭슨이 싫어.

나는 아약스를 사용하지만 Google Gson을 사용하고 싶다.

그래서 @ResponseBody 주석과 함께 사용하기 위해 자신의 HttpMessageConverter를 구현하는 방법을 찾으려고합니다. 누군가 내가 가야 할 길을 보여줄 시간을 가질 수 있습니까? 어떤 구성을 켜야합니까? 또한이 작업을 수행 할 수 여전히 사용할 수 있는지 궁금하네요?

미리 감사드립니다.

나는 이미 3 일 전에 답변이 없으므로 Spring Community Forums에서 이미 질문했습니다. 그래서 더 나은 기회가 있는지 여기에서 질문하고 있습니다. 내 질문에 대한 춘계 커뮤니티 포럼 링크

또한 웹에서 철저한 검색을 수행하여이 주제에 대해 흥미로운 것을 발견했으나 스프링 3.1에 넣으려는 것으로 보이고 여전히 3.0.5를 사용하고 있습니다. Jira 's Spring Improvement가 묻습니다.

글쎄 ... 지금 스프링 코드를 디버깅하려고 시도하고 있는데, 어떻게해야 할지를 알기 위해 Spring 코드를 디버깅하려하지만 여기에서 말한 것과 같은 몇 가지 문제가 있습니다. 스프링 프레임 워크 빌드 오류

이 작업을 수행 할 수있는 다른 방법이 있는데 누락 된 경우 알려 주시기 바랍니다.

해결법

  1. ==============================

    1.글쎄 ... 대답을 찾기가 너무 어려웠고 불완전한 정보에 대한 많은 단서를 따라야 만 여기에서 완전한 답을 게시하는 것이 좋을 것이라고 생각합니다. 따라서 다음에 검색하는 것이 더 쉬울 것입니다.

    글쎄 ... 대답을 찾기가 너무 어려웠고 불완전한 정보에 대한 많은 단서를 따라야 만 여기에서 완전한 답을 게시하는 것이 좋을 것이라고 생각합니다. 따라서 다음에 검색하는 것이 더 쉬울 것입니다.

    먼저 맞춤 HttpMessageConverter를 구현해야했습니다.

    
    package net.iogui.web.spring.converter;
    
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.io.Reader;
    import java.io.StringWriter;
    import java.io.Writer;
    import java.nio.charset.Charset;
    
    import org.springframework.http.HttpInputMessage;
    import org.springframework.http.HttpOutputMessage;
    import org.springframework.http.MediaType;
    import org.springframework.http.converter.AbstractHttpMessageConverter;
    import org.springframework.http.converter.HttpMessageNotReadableException;
    import org.springframework.http.converter.HttpMessageNotWritableException;
    
    import com.google.gson.Gson;
    import com.google.gson.JsonSyntaxException;
    
    public class GsonHttpMessageConverter extends AbstractHttpMessageConverter<Object> {
    
        private Gson gson = new Gson();
    
        public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
    
        public GsonHttpMessageConverter(){
            super(new MediaType("application", "json", DEFAULT_CHARSET));
        }
    
        @Override
        protected Object readInternal(Class<? extends Object> clazz,
                                      HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
    
            try{
                return gson.fromJson(convertStreamToString(inputMessage.getBody()), clazz);
            }catch(JsonSyntaxException e){
                throw new HttpMessageNotReadableException("Could not read JSON: " + e.getMessage(), e);
            }
    
        }
    
        @Override
        protected boolean supports(Class<?> clazz) {
            return true;
        }
    
        @Override
        protected void writeInternal(Object t, 
                                     HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
    
            //TODO: adapt this to be able to receive a list of json objects too
    
            String json = gson.toJson(t);
    
            outputMessage.getBody().write(json.getBytes());
        }
    
        //TODO: move this to a more appropriated utils class
        public String convertStreamToString(InputStream is) throws IOException {
            /*
             * To convert the InputStream to String we use the Reader.read(char[]
             * buffer) method. We iterate until the Reader return -1 which means
             * there's no more data to read. We use the StringWriter class to
             * produce the string.
             */
            if (is != null) {
                Writer writer = new StringWriter();
    
                char[] buffer = new char[1024];
                try {
                    Reader reader = new BufferedReader(new InputStreamReader(is, "UTF-8"));
                    int n;
                    while ((n = reader.read(buffer)) != -1) {
                        writer.write(buffer, 0, n);
                    }
                } finally {
                    is.close();
                }
                return writer.toString();
            } else {
                return "";
            }
        }
    
    }
    
    

    그런 다음 annnotaion 기반 태그를 제거하고 spring-mvc 구성 파일에서 직접 모든 것을 구성해야했습니다.

    
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:mvc="http://www.springframework.org/schema/mvc"
        xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
            http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
    
        <!-- Configures the @Controller programming model -->
    
        <!-- To use just with a JSR-303 provider in the classpath 
        <bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean" />
        -->
    
        <bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean" />
    
        <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
            <property name="webBindingInitializer">
                <bean class="net.iogui.web.spring.util.CommonWebBindingInitializer" />
            </property>
            <property name="messageConverters">
                <list>
                    <bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter" />
                    <bean class="org.springframework.http.converter.StringHttpMessageConverter" />
                    <bean class="org.springframework.http.converter.ResourceHttpMessageConverter" />
                    <bean class="net.iogui.web.spring.converter.GsonHttpMessageConverter" />
                    <bean class="org.springframework.http.converter.xml.SourceHttpMessageConverter" />
                    <bean class="org.springframework.http.converter.xml.XmlAwareFormHttpMessageConverter" />
                    <!-- bean class="org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter" /-->
                </list>
            </property>
        </bean>
        <bean id="handlerMapping" class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping" />
    
    
        <context:component-scan base-package="net.iogui.teste.web.controller"/>
    
        <!-- Forwards requests to the "/" resource to the "login" view -->
        <mvc:view-controller path="/" view-name="home"/>
    
        <!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources/ directory -->
        <mvc:resources mapping="/resources/**" location="/resources/" />
    
        <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
            <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
            <property name="prefix" value="/WEB-INF/view/"/>
            <property name="suffix" value=".jsp"/>
        </bean>
    
    </beans>
    
    

    Format과 Validator가 작동하도록하려면 사용자 정의 webBindingInitializer도 빌드해야합니다.

    
    package net.iogui.web.spring.util;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.core.convert.ConversionService;
    import org.springframework.validation.Validator;
    import org.springframework.web.bind.WebDataBinder;
    import org.springframework.web.bind.support.WebBindingInitializer;
    import org.springframework.web.context.request.WebRequest;
    
    public class CommonWebBindingInitializer implements WebBindingInitializer {
    
        @Autowired(required=false)
        private Validator validator;
    
        @Autowired
        private ConversionService conversionService;
    
        @Override
        public void initBinder(WebDataBinder binder, WebRequest request) {
            binder.setValidator(validator);
            binder.setConversionService(conversionService);
        }
    
    }
    
    

    Annotation-driven 태그없이 구성 작업을 수행하려면 AnnotationMethodHandlerAdapter 및 DefaultAnnotationHandlerMapping을 수동으로 구성해야합니다. 그리고 AnnotationMethodHandlerAdapter가 형식화와 유효성 검사를 처리 할 수 ​​있도록하기 위해 우리는 유효성 검사기와 conversionService를 구성하고 사용자 정의 webBindingInitializer를 빌드해야했습니다.

    이 모든 것이 나 외에 다른 사람을 돕기를 바랍니다.

    필사적 인 검색에서이 @Bozho 게시물은 매우 유용했습니다. 나는 @GaryF에 감사 드리며 그의 대답은 나를 @Bozho 지위에 데려 갔다. Spring 3.1에서이 작업을하려고하는 여러분, @Robby Pond answer ..를 참조하십시오. 훨씬 쉽지 않습니까?

  2. ==============================

    2.AbstractHttpMessageConverter를 확장하고 mvc-message-converters 태그를 사용하여 메시지 변환기를 등록하는 GsonMessageConverter를 만들어야합니다. 이 태그를 사용하면 변환기가 Jackson보다 우선 적용됩니다.

    AbstractHttpMessageConverter를 확장하고 mvc-message-converters 태그를 사용하여 메시지 변환기를 등록하는 GsonMessageConverter를 만들어야합니다. 이 태그를 사용하면 변환기가 Jackson보다 우선 적용됩니다.

  3. ==============================

    3.Jackson의 사용으로 다른 그룹의 (동일한 회사에서) 코드를 변경해야하는 상황이있었습니다. 그걸 좋아하지 않았어. 그래서 저는 Gson을 사용하고 필요에 따라 TypeAdapters를 등록하기로했습니다.

    Jackson의 사용으로 다른 그룹의 (동일한 회사에서) 코드를 변경해야하는 상황이있었습니다. 그걸 좋아하지 않았어. 그래서 저는 Gson을 사용하고 필요에 따라 TypeAdapters를 등록하기로했습니다.

    변환기를 연결하고 spring-test (spring-mvc-test로 사용됨)를 사용하여 몇 가지 통합 테스트를 작성했습니다. mvc : annotation-driven 또는 bean의 수동 정의를 사용하여 어떤 변형을 시도해도 상관 없습니다. 그들 중 누구도 일하지 않았습니다. 이들 중 어떤 조합도 항상 실패한 Jackson Converter를 사용했습니다.

    Answer> MockMvcBuilders의 standaloneSetup 메소드는 메시지 변환기를 기본 버전으로 코딩하고 위의 모든 변경 사항을 무시했다. 다음은 효과가있는 것입니다.

    @Autowired
    private RequestMappingHandlerAdapter adapter;
    
    public void someOperation() {
      StandaloneMockMvcBuilder smmb = MockMvcBuilders.standaloneSetup(controllerToTest);
      List<HttpMessageConverter<?>> converters = adapter.getMessageConverters();
      HttpMessageConverter<?> ary[] = new HttpMessageConverter[converters.size()];
      smmb.setMessageConverters(conveters.toArray(ary));
      mockMvc = smmb.build();
       .
       .
    }
    

    희망이 사람을 돕는, 결국 나는 안드로이드 - 안드로이드 변환기를 다시 사용하여 주석 구동 및 사용

  4. ==============================

    4.여기에 xml을 사용하지 않고 메시지 변환기를 추가하려는 경우 간단한 예제가 있습니다.

    여기에 xml을 사용하지 않고 메시지 변환기를 추가하려는 경우 간단한 예제가 있습니다.

    @Autowired
    private RequestMappingHandlerAdapter adapter;
    
    @PostConstruct
    public void initStuff() {
        List<HttpMessageConverter<?>> messageConverters = adapter.getMessageConverters();
        BufferedImageHttpMessageConverter imageConverter = new BufferedImageHttpMessageConverter();;
        messageConverters.add(0,imageConverter);
    }
    
  5. ==============================

    5.Robby Pond는 기본적으로 정확하지만 mvc : message-converters 태그를 사용하기 위해서는 3.1을 사용해야한다고 제안합니다. 3.1은 현재 유일한 이정표 (M1)이므로, 생성 한 후에는이 방법으로 등록하는 것이 좋습니다.

    Robby Pond는 기본적으로 정확하지만 mvc : message-converters 태그를 사용하기 위해서는 3.1을 사용해야한다고 제안합니다. 3.1은 현재 유일한 이정표 (M1)이므로, 생성 한 후에는이 방법으로 등록하는 것이 좋습니다.

    <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
        <property name="messageConverters">
          <util:list id="beanList">
            <ref bean="someMessageConverter"/>
            <ref bean="someOtherMessageConverter"/>
          </util:list>
        </property>
    </bean>
    
  6. ==============================

    6.Jira 's Spring Improvement에서 언급했듯이 HttpMessageConvertor를 AnnotationMethodHandlerAdapter에 추가하는 BeanPostProcessor를 작성하십시오.

    Jira 's Spring Improvement에서 언급했듯이 HttpMessageConvertor를 AnnotationMethodHandlerAdapter에 추가하는 BeanPostProcessor를 작성하십시오.

  7. ==============================

    7.최근 GsonHttpMessageConverter가 Spring (4.1)에 추가되었습니다.

    최근 GsonHttpMessageConverter가 Spring (4.1)에 추가되었습니다.

  8. ==============================

    8.WebConfig 파일을 Java 파일로 작성하여이를 수행 할 수 있습니다. WebMvcConfigurerAdapter를 사용하여 구성 파일을 확장하고 extendMessageConverters 메소드를 재정 의하여 원하는 메시지 변환기를 추가하십시오. 이 메소드는 Spring에서 추가 한 기본 변환기를 보유하고 끝에 변환기를 추가합니다. 분명히 당신은 목록을 완벽하게 통제 할 수 있으며 목록에서 원하는 곳을 추가 할 수 있습니다.

    WebConfig 파일을 Java 파일로 작성하여이를 수행 할 수 있습니다. WebMvcConfigurerAdapter를 사용하여 구성 파일을 확장하고 extendMessageConverters 메소드를 재정 의하여 원하는 메시지 변환기를 추가하십시오. 이 메소드는 Spring에서 추가 한 기본 변환기를 보유하고 끝에 변환기를 추가합니다. 분명히 당신은 목록을 완벽하게 통제 할 수 있으며 목록에서 원하는 곳을 추가 할 수 있습니다.

    @Configuration
    @EnableWebMvc
    @ComponentScan(basePackageClasses={WebConfig.class})
    public class WebConfig extends WebMvcConfigurerAdapter {
        @Override
        public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
          converters.add(new GsonHttpMessageConverter());
       }
    }
    
    package net.iogui.web.spring.converter;
    
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.io.Reader;
    import java.io.StringWriter;
    import java.io.Writer;
    import java.nio.charset.Charset;
    
    import org.springframework.http.HttpInputMessage;
    import org.springframework.http.HttpOutputMessage;
    import org.springframework.http.MediaType;
    import org.springframework.http.converter.AbstractHttpMessageConverter;
    import org.springframework.http.converter.HttpMessageNotReadableException;
    import org.springframework.http.converter.HttpMessageNotWritableException;
    
    import com.google.gson.Gson;
    import com.google.gson.JsonSyntaxException;
    
    public class GsonHttpMessageConverter extends AbstractHttpMessageConverter<Object> {
    
    private Gson gson = new Gson();
    
    public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
    
    public GsonHttpMessageConverter(){
        super(new MediaType("application", "json", DEFAULT_CHARSET));
    }
    
    @Override
    protected Object readInternal(Class<? extends Object> clazz,
                                  HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
    
        try{
            return gson.fromJson(convertStreamToString(inputMessage.getBody()), clazz);
        }catch(JsonSyntaxException e){
            throw new HttpMessageNotReadableException("Could not read JSON: " + e.getMessage(), e);
        }
    
    }
    
    @Override
    protected boolean supports(Class<?> clazz) {
        return true;
    }
    
    @Override
    protected void writeInternal(Object t, 
                                 HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
    
        //TODO: adapt this to be able to receive a list of json objects too
    
        String json = gson.toJson(t);
    
        outputMessage.getBody().write(json.getBytes());
    }
    
    //TODO: move this to a more appropriated utils class
    public String convertStreamToString(InputStream is) throws IOException {
        /*
         * To convert the InputStream to String we use the Reader.read(char[]
         * buffer) method. We iterate until the Reader return -1 which means
         * there's no more data to read. We use the StringWriter class to
         * produce the string.
         */
        if (is != null) {
            Writer writer = new StringWriter();
    
            char[] buffer = new char[1024];
            try {
                Reader reader = new BufferedReader(new InputStreamReader(is, "UTF-8"));
                int n;
                while ((n = reader.read(buffer)) != -1) {
                    writer.write(buffer, 0, n);
                }
            } finally {
                is.close();
            }
            return writer.toString();
        } else {
            return "";
        }
    }
    
  9. from https://stackoverflow.com/questions/5019162/custom-httpmessageconverter-with-responsebody-to-do-json-things by cc-by-sa and MIT license