2015-02-08 3 views
5

ich eine benutzerdefinierte JsonDeserializer geschrieben haben, die einen autowired Dienst enthält, wie folgt:autowiring in 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; 
    } 
} 

Als ich zum ersten Verwendung dieses Deserializer machte ich wurde NPE als personService immer nicht autowired werden . Aus der Betrachtung anderer SO-Antworten (insbesondere this one) ergibt sich, dass es zwei Möglichkeiten gibt, das Autowiren zum Laufen zu bringen.

Option 1 ist SpringBeanAutowiringSupport im Konstruktor des benutzerdefinierten Deserializer zu verwenden:

public PersonDeserializer() { 

    SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this); 
} 

Option 2 ist ein HandlerInstantiator zu verwenden und registriert es mit meiner ObjectMapper Bohne:

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

ich versucht habe, beide Optionen und sie funktionieren gleich gut. Natürlich ist Option 1 viel einfacher, da es nur drei Zeilen Code ist, aber meine Frage ist: Gibt es Nachteile bei der Verwendung von SpringBeanAutowiringSupport im Vergleich zu HandlerInstantiator Ansatz? Meine Anwendung wird Hunderte von Objekten pro Minute deserialisieren, wenn das irgendeinen Unterschied macht.

Jede Beratung/Feedback ist willkommen.

+0

Haben Sie es geschafft, jeden Nachteil gegen SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext (diese) zu finden; ? –

Antwort

0

Wie in dieser comment vorgeschlagen und auf dieser link Sie müssen gefunden benutzerdefinierte HandlerInstantiator erstellen:

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

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

und registrieren Sie Ihre individuelle 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> 

In diesem Moment können Sie verwenden:

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

... und personService wird nicht null sein.

2

Wenn Sie die Antwort von Amir Jamak hinzufügen, müssen Sie keinen benutzerdefinierten HandlerInstantiator erstellen, da Spring bereits über SpringHandlerInstantiator verfügt.

Was Sie tun müssen, ist es an Jackson2ObjectMapperBuilder in Spring-Konfiguration anschließen.

@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

Bereinigung auf die obige Antwort mit Federverschluß,

@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(); 
} 
Verwandte Themen