복붙노트

[SPRING] JsonDeserializer에서의 Autowiring : SpringBeanAutowiringSupport 대 HandlerInstantiator

SPRING

JsonDeserializer에서의 Autowiring : SpringBeanAutowiringSupport 대 HandlerInstantiator

다음과 같이 autowired 서비스를 포함하는 사용자 정의 JsonDeserializer를 작성했습니다.

public class PersonDeserializer extends JsonDeserializer<Person> {

    @Autowired
    PersonService personService;

    @Override
    public Person deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {

        // deserialization occurs here which makes use of personService

        return person;
    }
}

처음이 디시리얼라이저를 사용했을 때 personService가 자동 실행되고 있지 않은 것처럼 NPE를 얻었습니다. 다른 SO 답변 (특히,이 하나)에서 볼 때 autowiring이 작동하도록하는 두 가지 방법이 나타납니다.

옵션 1은 커스텀 디시리얼라이저의 생성자 내에서 SpringBeanAutowiringSupport를 사용하는 것입니다.

public PersonDeserializer() { 

    SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this); 
}

옵션 2는 HandlerInstantiator를 사용하고 그것을 내 ObjectMapper 빈에 등록하는 것입니다 :

@Component
public class SpringBeanHandlerInstantiator extends HandlerInstantiator {

    @Autowired
    private ApplicationContext applicationContext;

    @Override
    public JsonDeserializer<?> deserializerInstance(DeserializationConfig config, Annotated annotated, Class<? extends JsonDeserializer<?>> deserClass) {

        try {

            return (JsonDeserializer<?>) applicationContext.getBean(deserClass);

        } catch (Exception e) {

            // Return null and let the default behavior happen
            return null;
        }
    }
}

@Configuration  
public class JacksonConfiguration {

    @Autowired
    SpringBeanHandlerInstantiator springBeanHandlerInstantiator;

    @Bean
    public ObjectMapper objectMapper() {

        Jackson2ObjectMapperFactoryBean jackson2ObjectMapperFactoryBean = new Jackson2ObjectMapperFactoryBean();
        jackson2ObjectMapperFactoryBean.afterPropertiesSet();

        ObjectMapper objectMapper = jackson2ObjectMapperFactoryBean.getObject();

        // add the custom handler instantiator
        objectMapper.setHandlerInstantiator(springBeanHandlerInstantiator);

        return objectMapper;
    }
}

나는 두 가지 옵션을 모두 시도했으며 똑같이 잘 작동합니다. SpringBeanAutowiringSupport를 HandlerInstantiator 접근 방식과 비교할 때 어떤 단점이 있습니까? 분명히 옵션 3은 코드가 3 줄이기 때문에 훨씬 쉽습니다. 차이가 나는 경우에는 분당 수백 개의 객체를 비 직렬화 할 것입니다.

모든 조언 / 의견을 보내 주시면 감사하겠습니다.

해결법

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

    1.Amir Jamak의 대답에 SpringHandlerInstantiator 인 Spring이 있으므로 사용자 정의 HandlerInstantiator를 만들 필요가 없습니다.

    Amir Jamak의 대답에 SpringHandlerInstantiator 인 Spring이 있으므로 사용자 정의 HandlerInstantiator를 만들 필요가 없습니다.

    스프링 구성에서 Jackson2ObjectMapperBuilder에 연결해야합니다.

    @Bean
    public HandlerInstantiator handlerInstantiator(ApplicationContext applicationContext) {
        return new SpringHandlerInstantiator(applicationContext.getAutowireCapableBeanFactory());
    }
    
    @Bean
    public Jackson2ObjectMapperBuilder objectMapperBuilder(HandlerInstantiator handlerInstantiator) {
        Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
        builder.handlerInstantiator(handlerInstantiator);
        return builder;
    }
    
  2. ==============================

    2.이 주석에서 제안하고이 링크에서 발견되는대로 사용자 정의 HandlerInstantiator를 만들어야합니다.

    이 주석에서 제안하고이 링크에서 발견되는대로 사용자 정의 HandlerInstantiator를 만들어야합니다.

    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.ApplicationContext;
    import org.springframework.stereotype.Component;
    
    import com.fasterxml.jackson.databind.DeserializationConfig;
    import com.fasterxml.jackson.databind.JsonDeserializer;
    import com.fasterxml.jackson.databind.JsonSerializer;
    import com.fasterxml.jackson.databind.KeyDeserializer;
    import com.fasterxml.jackson.databind.SerializationConfig;
    import com.fasterxml.jackson.databind.cfg.HandlerInstantiator;
    import com.fasterxml.jackson.databind.cfg.MapperConfig;
    import com.fasterxml.jackson.databind.introspect.Annotated;
    import com.fasterxml.jackson.databind.jsontype.TypeIdResolver;
    import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder;
    
    @Component
    public class SpringBeanHandlerInstantiator extends HandlerInstantiator {
    
        private ApplicationContext applicationContext;
    
        @Autowired
        public SpringBeanHandlerInstantiator(ApplicationContext applicationContext) {
            this.applicationContext = applicationContext;
        }
    
        @Override
        public JsonDeserializer<?> deserializerInstance(DeserializationConfig config,
                Annotated annotated,
                Class<?> deserClass) {
            try {
                return (JsonDeserializer<?>) applicationContext.getBean(deserClass);
            } catch (Exception e) {
                // Return null and let the default behavior happen
            }
            return null;
        }
    
        @Override
        public KeyDeserializer keyDeserializerInstance(DeserializationConfig config,
                Annotated annotated,
                Class<?> keyDeserClass) {
            try {
                return (KeyDeserializer) applicationContext.getBean(keyDeserClass);
            } catch (Exception e) {
                // Return null and let the default behavior happen
            }
            return null;
        }
    
        @Override
        public JsonSerializer<?> serializerInstance(SerializationConfig config, Annotated annotated, Class<?> serClass) {
            try {
                return (JsonSerializer<?>) applicationContext.getBean(serClass);
            } catch (Exception e) {
                // Return null and let the default behavior happen
            }
            return null;
        }
    
        @Override
        public TypeResolverBuilder<?> typeResolverBuilderInstance(MapperConfig<?> config, Annotated annotated,
                Class<?> builderClass) {
            try {
                return (TypeResolverBuilder<?>) applicationContext.getBean(builderClass);
            } catch (Exception e) {
                // Return null and let the default behavior happen
            }
            return null;
        }
    
        @Override
        public TypeIdResolver typeIdResolverInstance(MapperConfig<?> config, Annotated annotated, Class<?> resolverClass) {
            try {
                return (TypeIdResolver) applicationContext.getBean(resolverClass);
            } catch (Exception e) {
                // Return null and let the default behavior happen
            }
            return null;
        }
    }
    

    사용자 정의 ObjectMapper :

    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.ApplicationContext;
    
    import com.fasterxml.jackson.annotation.JsonInclude;
    import com.fasterxml.jackson.databind.DeserializationFeature;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.fasterxml.jackson.databind.SerializationFeature;
    import com.fasterxml.jackson.databind.cfg.HandlerInstantiator;
    
    
    public class CustomObjectMapper extends ObjectMapper {
        private static final long serialVersionUID = -8865944893878900100L;
    
        @Autowired
        ApplicationContext applicationContext;
    
        public JamaxObjectMapper() {
            // Problems serializing Hibernate lazily initialized collections?  Fix here.
    //        HibernateModule hm = new HibernateModule();
    //        hm.configure(com.fasterxml.jackson.module.hibernate.HibernateModule.Feature.FORCE_LAZY_LOADING, true);
    //        this.registerModule(hm);
    
            // Jackson confused by what to set or by extra properties?  Fix it.
            this.setSerializationInclusion(JsonInclude.Include.NON_NULL);
            this.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
            this.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
        }
    
        @Override
        @Autowired
        public Object setHandlerInstantiator(HandlerInstantiator hi) {
            return super.setHandlerInstantiator(hi);
        }
    }
    

    사용자 정의 ObjectMapper를 등록하십시오.

    <bean id="jacksonObjectMapper" class="com.acme.CustomObjectMapper" />
    <bean id="jacksonMessageConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
        <property name="prefixJson" value="false" />
        <property name="supportedMediaTypes" value="application/json" />
        <property name="objectMapper" ref="jacksonObjectMapper" />
    </bean>
    

    이 순간에 다음을 사용할 수 있습니다 :

    @JsonDeserialize(contentUsing=PersonDeserializer.class)
    public void setPerson(Person person) {
        ...
    }
    

    ... 및 personService는 null이 아닙니다.

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

    3.봄 부팅을 사용하여 위의 답변 정리,

    봄 부팅을 사용하여 위의 답변 정리,

    @Bean
    public HandlerInstantiator handlerInstantiator(ApplicationContext context) {
        return new SpringHandlerInstantiator(context.getAutowireCapableBeanFactory());
    }
    
    @Bean
    public ObjectMapper objectMapper(HandlerInstantiator handlerInstantiator) {
        Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
        builder.handlerInstantiator(handlerInstantiator);
        return builder.build();
    }
    
  4. from https://stackoverflow.com/questions/28393599/autowiring-in-jsondeserializer-springbeanautowiringsupport-vs-handlerinstantiat by cc-by-sa and MIT license