如何将ObjectIdResolver注册到Spring / Jackson对象映射器,以便ObjectIdResolver类被Spring实例化?我想在ObjectIdResolver类中使用依赖注入。
ObjectIdResolver.java
@Component
public class UserIdResolver implements ObjectIdResolver {
@Autowired
UserConverter userConverter;
@Override
public void bindItem(ObjectIdGenerator.IdKey id, Object ob) { }
@Override
public Object resolveId(ObjectIdGenerator.IdKey id) {
return userConverter.convert((Integer)id.key);
}
@Override
public boolean canUseFor(ObjectIdResolver resolverType) {
return getClass().isAssignableFrom(resolverType.getClass());
}
@Override
public ObjectIdResolver newForDeserialization(Object c) {
return this;
}
}
Order.java
public class Order {
@JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id",
resolver = UserIdResolver.class,
scope = User.class
)
@JsonIdentityReference(alwaysAsId=true)
private User user;
..
}
弹簧servlet.xml中
<bean id="objectMapper" class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean"
p:indentOutput="true" p:simpleDateFormat="yyyy-MM-dd'T'HH:mm:ss.SSSZ" />
<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean"
p:targetObject-ref="objectMapper" p:targetMethod="registerModule">
<property name="arguments">
<list>
<bean class="com.fasterxml.jackson.datatype.joda.JodaModule" />
</list>
</property>
</bean>
<mvc:annotation-driven conversion-service="conversionService">
<mvc:message-converters>
<bean class="org.springframework.http.converter.StringHttpMessageConverter"/>
<bean class="org.springframework.http.converter.ResourceHttpMessageConverter" />
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper" ref="objectMapper" />
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
答案 0 :(得分:1)
实际上,现在只要在构造函数中添加@Autowired,就可以将@Autowired 用于ObjectIdResolver:
public class CategoryTaskIdResolver implements ObjectIdResolver {
CategoryTaskRepository categoryTaskRepository;
public CategoryTaskIdResolver(@Autowired CategoryTaskRepository categoryTaskRepo) {
this.categoryTaskRepository = categoryTaskRepo;
}
@Override
public void bindItem(IdKey id, Object pojo) {
}
@Override
public Object resolveId(IdKey id) {
return categoryTaskRepository.findById((Long) id.key).orElse(null);
}
@Override
public ObjectIdResolver newForDeserialization(Object context) {
return this;
}
@Override
public boolean canUseFor(ObjectIdResolver resolverType) {
return getClass().isAssignableFrom(resolverType.getClass());;
}
}
我使用的是 Spring Boot 2.3.4 和 Jackson Databind 2.11.2。
答案 1 :(得分:0)
因为com.fasterxml.jackson.annotation.ObjectIdResolver
不是由spring构造和管理的(jackson在构造函数上使用反射),所以没有直接的方法来实现它。当我想在jpa EntityListener中自动装配某些依赖项时,我遇到了类似的问题,并在以下位置找到了巧妙的技巧:
https://guylabs.ch/2014/02/22/autowiring-pring-beans-in-hibernate-jpa-entity-listeners/
/**
* Helper class which is able to autowire a specified class. It holds a static reference to the {@link org
* .springframework.context.ApplicationContext}.
*/
public final class AutowireHelper implements ApplicationContextAware {
private static final AutowireHelper INSTANCE = new AutowireHelper();
private static ApplicationContext applicationContext;
private AutowireHelper() {
}
/**
* Tries to autowire the specified instance of the class if one of the specified beans which need to be autowired
* are null.
*
* @param classToAutowire the instance of the class which holds @Autowire annotations
* @param beansToAutowireInClass the beans which have the @Autowire annotation in the specified {#classToAutowire}
*/
public static void autowire(Object classToAutowire, Object... beansToAutowireInClass) {
for (Object bean : beansToAutowireInClass) {
if (bean == null) {
applicationContext.getAutowireCapableBeanFactory().autowireBean(classToAutowire);
return;
}
}
}
@Override
public void setApplicationContext(final ApplicationContext applicationContext) {
AutowireHelper.applicationContext = applicationContext;
}
/**
* @return the singleton instance.
*/
public static AutowireHelper getInstance() {
return INSTANCE;
}
}
所以你必须添加这个AutowireHelper
类并改变你的方法:
@Override
public ObjectIdResolver newForDeserialization(Object c) {
AutowireHelper.autowire(this, userConverter);
return this;
}
答案 2 :(得分:0)
您可以尝试另一种解决方案,稍微复杂一点,但您可以完全控制反序列化。
第1步:创建JpaEntityResolver
public class JpaEntityResolver extends SimpleObjectIdResolver {
private EntityManager em;
private Class<? extends EntityType> entityClass;
public JpaEntityResolver() {
}
public JpaEntityResolver(EntityManager em, Class<? extends EntityType> entityClass) {
this.em = em;
this.entityClass = entityClass;
}
@Override
public Object resolveId(IdKey id) {
Object resolved = super.resolveId(id);
if (resolved == null) {
resolved = findEntityById(id);
bindItem(id, resolved);
}
return resolved;
}
private Object findEntityById(IdKey idKey) {
Long id = (Long) idKey.key;
return em.find(entityClass, id);
}
@Override
public ObjectIdResolver newForDeserialization(Object context) {
return new JpaEntityResolver(this.em, this.entityClass);
}
}
第2步:创建一个自定义的SpringHandlerInstantiator,帮助jackson动态定义哪个
@Component
public class TsqBeanHandlerInstantiator extends SpringHandlerInstantiator {
/** Logger. */
protected static final Logger LOGGER = LoggerFactory.getLogger(TsqBeanHandlerInstantiator.class.getName());
private final AutowireCapableBeanFactory beanFactory;
private final EntityManager em;
private List<Class<? extends EntityType>> tsqEntities = new ArrayList<>();
/**
* Create a new SpringHandlerInstantiator for the given BeanFactory.
* @param beanFactory the target BeanFactory
*/
public TsqBeanHandlerInstantiator(AutowireCapableBeanFactory beanFactory, EntityManager em) {
super(beanFactory);
this.beanFactory = beanFactory;
this.em = em;
}
@PostConstruct
public void init()
{
//We record all available entity for futur use.
for (EntityType<?> entity : em.getMetamodel().getEntities()) {
Class<? extends EntityType> entityClass = (Class<? extends EntityType>) entity.getJavaType();
String className = entityClass.getName();
tsqEntities.add(entityClass);
}
}
@Override
public JsonDeserializer<?> deserializerInstance(DeserializationConfig config, Annotated annotated, Class<?> implClass) {
Class entityClass = annotated.getRawType();
LOGGER.debug("deserializerInstance : " + entityClass);
return (JsonDeserializer<?>) this.beanFactory.createBean(implClass);
}
/** @since 4.3 */
@Override
public ObjectIdGenerator<?> objectIdGeneratorInstance(MapperConfig<?> config, Annotated annotated, Class<?> implClass) {
Class entityClass = annotated.getRawType();
LOGGER.debug("objectIdGeneratorInstance : " + entityClass);
return (ObjectIdGenerator<?>) this.beanFactory.createBean(implClass);
}
/** @since 4.3 */
@Override
public ObjectIdResolver resolverIdGeneratorInstance(MapperConfig<?> config, Annotated annotated, Class<?> implClass) {
Class entityClass = annotated.getRawType();
if(!tsqEntities.contains(entityClass))
{
return super.resolverIdGeneratorInstance(config, annotated, implClass);
}
LOGGER.debug("resolverIdGeneratorInstance : " + entityClass);
return new JpaEntityResolver(em, entityClass);
}
}
第3步:将处理程序注册到jacksonHttpMessageConverter的ObjectMapper
@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {
private @Autowired MappingJackson2HttpMessageConverter jacksonHttpMessageConverter;
private @Autowired TsqBeanHandlerInstantiator tsqBeanHandlerInstantiator;
@PostConstruct
public void init(){
ObjectMapper objectMapper = jacksonHttpMessageConverter.getObjectMapper();
objectMapper.setHandlerInstantiator(tsqBeanHandlerInstantiator);
}
}
第4步:欢迎在控制器上使用
@RequestMapping(method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Bean> create(HttpServletRequest request) throws IOException {
// getting the posted value
String body = CharStreams.toString(request.getReader());
Bean bean = jacksonHttpMessageConverter.getObjectMapper().readValue(body, service.getBeanClass());
Bean saved = service.save(bean);
URI location = ServletUriComponentsBuilder.fromCurrentRequest().path("/{id}").buildAndExpand(saved.getId()).toUri();
return ResponseEntity.created(location).body(saved);
}