tldr; 我想在使用Jackson @JsonAppend
将JPA实体序列化为JSON的同时添加虚拟字段。虚拟字段的值必须通过Spring管理的服务来确定。如何将我的Spring托管服务注入Jackson类中?
技术:Spring Boot 1.5.10,Spring Data Rest,JPA 2.1,Jackson 2.8.10
详细信息:
我有一个由Spring Data管理的JPA实体:
@Entity
public class Stream {
...
}
我创建了一个带有Mixin的Custom Jackson模块,以添加@JsonAppend
虚拟字段,如下所示:
@Bean
public Module customModule() {
return new CustomModule();
}
@Component
class CustomModule extends SimpleModule {
CustomModule() {
setMixInAnnotation(Stream.class, StreamMixin.class);
}
@JsonAppend(
props = {
@JsonAppend.Prop(name = "canEdit", value = ABACInspector.class)
}
)
abstract class StreamMixin {}
}
ABACInspector
类扩展了Jackson的VirtualBeanPropertyWriter
,以确定虚拟字段canEdit
的值。如果此类不使用Spring服务(例如,设置硬编码值),则它将正常工作,并且该字段将显示在REST API JSON响应中。但是自动装配Spring bean不起作用,该对象仍为null
。
@Component
class ABACInspector extends VirtualBeanPropertyWriter {
@Autowired
private PermissionEvaluator permissionEvaluator;
public ABACInspector() {
}
public ABACInspector(BeanPropertyDefinition propDef, Annotations contextAnnotations, JavaType declaredType) {
super(propDef, contextAnnotations, declaredType);
}
@Override
protected Object value(Object bean, JsonGenerator gen, SerializerProvider prov) throws Exception {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
boolean permission = permissionEvaluator.hasPermission(authentication, bean, Action.STREAM_VIEW);
System.out.println("evaluated permission is " + permission);
return permission;
}
@Override
public VirtualBeanPropertyWriter withConfig(MapperConfig<?> config, AnnotatedClass declaringClass, BeanPropertyDefinition propDef, JavaType type) {
return new ABACInspector(propDef, null, type);
}
}
以下是NPE错误(因为从未注入permissionEvaluator
):
{"status":"INTERNAL_SERVER_ERROR","message":"Could not write JSON:
(was java.lang.NullPointerException); nested exception is com.fasterxml.jackson.databind.JsonMappingException:
(was java.lang.NullPointerException) (through reference chain: org.springframework.data.rest.webmvc.json.PersistentEntityJackson2Module$PersistentEntityResourceSerializer$1[\"content\"]->com.example.streammanagement.Stream[\"canView\"])"
我知道包含HalHandlerInstantiator
的Spring Data Rest的AutowireCapableBeanFactory
,但不确定如何/是否有帮助。请参阅DATAREST-840
答案 0 :(得分:0)
Jackson内部调用组件的withConfig
函数以构建VirtualBeanPropertyWriter
。
因此,如果使用断点,则可以看到首先创建了一个带有注入bean的组件,然后调用了withConfig函数,并且创建了新的VirtualBeanPropertyWriter对象,该对象由jackson使用,当然也没有注入的bin(因为您调用了构造函数)手动)。
因此您可以通过以下方式进行更改:
@Component
class ABACInspector extends VirtualBeanPropertyWriter {
private PermissionEvaluator permissionEvaluator;
@Autowired
public ABACInspector(PermissionEvaluator permissionEvaluator) {
this.permissionEvaluator = permissionEvaluator;
}
public ABACInspector(BeanPropertyDefinition propDef, Annotations contextAnnotations, JavaType declaredType, PermissionEvaluator permissionEvaluator) {
super(propDef, contextAnnotations, declaredType);
this.permissionEvaluator = permissionEvaluator;
}
@Override
protected Object value(Object bean, JsonGenerator gen, SerializerProvider prov) throws Exception {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
boolean permission = permissionEvaluator.hasPermission(authentication, bean, Action.STREAM_VIEW);
System.out.println("evaluated permission is " + permission);
return permission;
}
@Override
public VirtualBeanPropertyWriter withConfig(MapperConfig<?> config, AnnotatedClass declaringClass, BeanPropertyDefinition propDef, JavaType type) {
return new ABACInspector(propDef, null, type, permissionEvaluator);
}
}