緣起
目前的項目中有個需求是在附件對象轉換成 json 時增加個 url 屬性,以前的方式是在返回附件對象或列表時候做一次統一處理,這次想看看 spring 或者 jackson fasterxml 是否自帶類似功能,結果一查,還真有。
當前業務系統的處理附件的方式是上傳附件之後將其基本信息統一存儲到附件表中,然後通過相應的 url 拼接附件 id 的方式來讀取相應附件(以便更方便控制授權),也就是說將附件對象返回前端的時候需要將其 url 也放置到 json 中,但是這個 url 屬性並不在基本信息中存在。
實現
查了一下,發現 @JsonAppend
完全可以滿足這個需要。
而@JsonAppend
在 jackson fasterxml 中是通過 VirtualBeanPropertyWriter
來處理的。
首先構造一個 mixin 接口,在其上配置 @JsonAppend
:
@JsonAppend(
props = {
@JsonAppend.Prop(name = "url", value = AttachmentUrl.class),
}
)
public interface AttachmentMixin {
}
之後就是實現 VirtualBeanPropertyWriter
的子類,在這兒可以通過 @Autowired
注入相應的處理方法:
public class AttachmentUrl extends VirtualBeanPropertyWriter {
private static final long serialVersionUID = 1028128817195205673L;
@Autowired UrlService urlService;
public AttachmentUrl() {
}
public AttachmentUrl(BeanPropertyDefinition propDef,
Annotations contextAnnotations,
JavaType declaredType, UrlService urlService) {
super(propDef, contextAnnotations, declaredType);
this.urlService = urlService;
}
@Override
protected Object value(Object bean, JsonGenerator gen, SerializerProvider prov) {
if (urlService != null) {
return urlService.getAttachUrl((Attachment) bean);
}
return null;
}
@Override
public VirtualBeanPropertyWriter withConfig(MapperConfig<?> config,
AnnotatedClass declaringClass, BeanPropertyDefinition propDef, JavaType type) {
return new AttachmentUrl(propDef, null, type, urlService);
}
}
注意:在 withConfig
方法中會創建新的對象,這個對象 return new AttachmentUrl(propDef, null, type, urlService);
,這兒的 urlService
不能使用 this.urlService
, 因爲urlService
是 spring 通過默認構造函數注入的,如果使用 this,則會成爲 null。
最後配置 Jackson2ObjectMapperBuilderCustomizer
:
@Configuration
public class JacksonConfiguration {
/**
* 自定義 {@link Jackson2ObjectMapperBuilder}.
*
* @return 略
*/
@Bean
public Jackson2ObjectMapperBuilderCustomizer objectMapperBuilderCustomizer() {
return builder -> builder.mixIn(Attachment.class, AttachmentMixin.class);
}
}
原理
spring 通過 org.springframework.http.converter.json.SpringHandlerInstantiator
實現對於 jackson fasterxml 的配置,也就是說在此處注入相應的 bean,對於 @JsonAppend
的處理方法爲:
@Override
public VirtualBeanPropertyWriter virtualPropertyWriterInstance(MapperConfig<?> config, Class<?> implClass) {
return (VirtualBeanPropertyWriter) this.beanFactory.createBean(implClass);
}
Over