在 Jackson VirtualBeanPropertyWriter 中注入 Spring Bean

Posted

技术标签:

【中文标题】在 Jackson VirtualBeanPropertyWriter 中注入 Spring Bean【英文标题】:Inject Spring Bean in Jackson VirtualBeanPropertyWriter 【发布时间】:2018-12-26 07:51:06 【问题描述】:

tldr; 我想在使用 Jackson @JsonAppend 将 JPA 实体序列化为 JSON 时添加虚拟字段。虚拟字段的值必须通过 Spring 管理的服务来确定。如何在 Jackson 类中注入我的 Spring 管理服务?

技术:Spring Boot 1.5.10、Spring Data Rest、JPA 2.1、Jackson 2.8.10

详情:

我有一个 Spring Data 托管的 JPA 实体:

@Entity
public class Stream 
   ...

我使用 Mixin 创建了一个自定义 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\"])"

我知道 Spring Data Rest 的 HalHandlerInstantiator 包含 AutowireCapableBeanFactory,但我不确定这如何/是否可以提供帮助。参考DATAREST-840

【问题讨论】:

【参考方案1】:

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

【讨论】:

你的ABACInspector初始化不是因为没有这样的构造函数而无效吗?构造函数注入也不起作用,因为还有另一个没有任何 PermissionEvaluator 参数的构造函数。 @xsreality 感谢您的评论。我修好了。

以上是关于在 Jackson VirtualBeanPropertyWriter 中注入 Spring Bean的主要内容,如果未能解决你的问题,请参考以下文章

jackson学习之二:jackson-core

如何在Github下载jackson的jar包

Jackson忽略空字段

jackson中文是啥意思

Maven 构建错误 org.codehaus.jackson:jackson-core-asl:jar:[1.8,1.9) 在指定范围内没有可用的版本

如何在 Spring Boot 1.4 中自定义 Jackson