2015-02-08 6 views
5

Pisałem zwyczaj JsonDeserializer który zawiera autowired usługi, jak następuje:autowiring w JsonDeserializer: SpringBeanAutowiringSupport vs HandlerInstantiator

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

Kiedy po raz pierwszy skorzystał z tej Deserializatora byłem coraz NPEs jak personService nie był autowired . Patrząc na inne odpowiedzi SO (w szczególności this one), wydaje się, że istnieją dwa sposoby uzyskania pracy autowirowania.

Wariant 1 jest użycie SpringBeanAutowiringSupport wewnątrz konstruktora niestandardowej Deserializatora:

public PersonDeserializer() { 

    SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this); 
} 

opcja 2 jest użycie HandlerInstantiator i zarejestrować go z moim ObjectMapper Fasola:

@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; 
    } 
} 

próbowałem obie opcje i działają równie dobrze. Oczywiście opcja 1 jest znacznie łatwiejsza, ponieważ jest tylko trzema liniami kodu, ale moje pytanie brzmi: czy są jakieś wady korzystania z SpringBeanAutowiringSupport w porównaniu z podejściem HandlerInstantiator? Moja aplikacja będzie deserializować setki obiektów na minutę, jeśli to robi jakąkolwiek różnicę.

Każda rada/opinia jest doceniana.

+0

Czy udało się znaleźć żadnego wadę przed SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext (this); ? –

Odpowiedz

0

Jak sugerowano w tym comment i znaleźć na tej link trzeba tworzyć niestandardowe 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; 
    } 
} 

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

i zarejestrować swój własny 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> 

W tej chwili możesz użyć:

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

... i personService nie będą miały wartości NULL.

2

Dodając do odpowiedzi Amira Jamaka, nie trzeba tworzyć niestandardowego HandlerInstantiator, ponieważ Spring ma już to, co jest SpringHandlerInstantiator.

Co musisz zrobić, to podłączyć go do Jackson2ObjectMapperBuilder w konfiguracji Spring.

@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

porządki z powyższą odpowiedź za pomocą buta sprężyny

@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(); 
} 
Powiązane problemy