Autowiring in JsonDeserializer: SpringBeanAutowiringSupport vs HandlerInstantiator

前端 未结 3 1577
佛祖请我去吃肉
佛祖请我去吃肉 2020-12-03 11:04

I have written a custom JsonDeserializer which contains an autowired service, as follows:

public class PersonDeserializer extends JsonDeserializ         


        
相关标签:
3条回答
  • 2020-12-03 11:44

    Adding to the Amir Jamak's answer, you don't have to create custom HandlerInstantiator as Spring has it already which is SpringHandlerInstantiator.

    What you need to do is to hook it up to Jackson2ObjectMapperBuilder in Spring configuration.

    @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;
    }
    
    0 讨论(0)
  • 2020-12-03 11:44

    cleanup to the above answer using spring boot,

    @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();
    }
    
    0 讨论(0)
  • 2020-12-03 12:06

    As suggested in this comment and found on this link you need to create custom 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;
        }
    }
    

    Custom 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);
        }
    }
    

    and register your custom 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>
    

    At this moment you can use:

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

    ... and personService will not be null.

    0 讨论(0)
提交回复
热议问题