背景
在springboot-web项目中,绝大多时候都是使用json格式来传输数据,springboot默认使用jackson来转换,一般情况下,默认的转化设置已经可以满足要求。特殊一点的使用Jackson相关的注解辅助也能完成。
但是在一次开发过程中,要求对某些字段进行加解密,但是由于是spring微服务的项目,所以只在其中一个服务中需要加密,所以没有办法使用@JsonSerialize 和 @JsonDeserialize着两种jackson的注解。
解决思路
一开始想的是,jackson既然实现了自己的注解,那它为了满足用户的需求,肯定做了兼容让用户根据自己的需要做数据的转换。经过查询资料和源码,发现OjectMapper有一个注入内省注解处理器的方法:
java
public ObjectMapper setAnnotationIntrospector(AnnotationIntrospector ai) {
_serializationConfig = _serializationConfig.with(ai);
_deserializationConfig = _deserializationConfig.with(ai);
return this;
}
public ObjectMapper setAnnotationIntrospectors(AnnotationIntrospector serializerAI,
AnnotationIntrospector deserializerAI) {
_serializationConfig = _serializationConfig.with(serializerAI);
_deserializationConfig = _deserializationConfig.with(deserializerAI);
return this;
}
序列化
ObjectMapper在对象序列化时,会使用到 _serializationConfig这个配置。
java
protected final void _configAndWriteValue(JsonGenerator g, Object value)
throws IOException
{
SerializationConfig cfg = getSerializationConfig(); //这个方法会返回 包含了 自定义注解解释类的的 _serializationConfig
cfg.initialize(g); // since 2.5
if (cfg.isEnabled(SerializationFeature.CLOSE_CLOSEABLE) && (value instanceof Closeable)) {
_configAndWriteCloseable(g, value, cfg);
return;
}
try {
// _serializerProvider(cfg) 会构建一个默认的 DefaultSerializerProvider序列化对象
_serializerProvider(cfg).serializeValue(g, value);
} catch (Exception e) {
ClassUtil.closeOnFailAndThrowAsIOE(g, e);
return;
}
g.close();
}
DefaultSerializerProvider继承了SerializerProvider
java
public JsonSerializer<Object> findTypedValueSerializer(Class<?> valueType,
boolean cache, BeanProperty property)
throws JsonMappingException
{
// Two-phase lookups; local non-shared cache, then shared:
JsonSerializer<Object> ser = _knownSerializers.typedValueSerializer(valueType);
if (ser != null) {
return ser;
}
// If not, maybe shared map already has it?
ser = _serializerCache.typedValueSerializer(valueType);
if (ser != null) {
return ser;
}
// Well, let's just compose from pieces:
ser = findValueSerializer(valueType, property); //这里获取对应的序列化实现
TypeSerializer typeSer = _serializerFactory.createTypeSerializer(_config,
_config.constructType(valueType));
if (typeSer != null) {
typeSer = typeSer.forProperty(property);
ser = new TypeWrappedSerializer(typeSer, ser);
}
if (cache) {
_serializerCache.addTypedSerializer(valueType, ser);
}
return ser;
}
一直走到方法:
java
protected JsonSerializer<Object> _createAndCacheUntypedSerializer(Class<?> rawType)
throws JsonMappingException
{
JavaType fullType = _config.constructType(rawType);
JsonSerializer<Object> ser;
try {
ser = _createUntypedSerializer(fullType); //创建序列化器
} catch (IllegalArgumentException iae) {
// We better only expose checked exceptions, since those
// are what caller is expected to handle
ser = null; // doesn't matter but compiler whines otherwise
reportMappingProblem(iae, ClassUtil.exceptionMessage(iae));
}
if (ser != null) {
// 21-Dec-2015, tatu: Best to cache for both raw and full-type key
_serializerCache.addAndResolveNonTypedSerializer(rawType, fullType, ser, this);
}
return ser;
}
java
@Override
@SuppressWarnings("unchecked")
public JsonSerializer<Object> createSerializer(SerializerProvider prov,
JavaType origType)
throws JsonMappingException
{
// Very first thing, let's check if there is explicit serializer annotation:
final SerializationConfig config = prov.getConfig();
BeanDescription beanDesc = config.introspect(origType);
//通过注解来创建序列化器
JsonSerializer<?> ser = findSerializerFromAnnotation(prov, beanDesc.getClassInfo());
if (ser != null) {
return (JsonSerializer<Object>) ser;
}
... 忽略其他
}
最终:
java
protected JsonSerializer<Object> findSerializerFromAnnotation(SerializerProvider prov,
Annotated a)
throws JsonMappingException
{
/**
prov使用的是之前构建的 SerializationConfig 类,其中包含了 我们自定义的 AnnotationIntrospector
getAnnotationIntrospector()可以获取到这个自定义的AnnotationIntrospector并调用其方法返回我们自定义的序列化器
*/
Object serDef = prov.getAnnotationIntrospector().findSerializer(a);
if (serDef == null) {
return null;
}
JsonSerializer<Object> ser = prov.serializerInstance(a, serDef);
// One more thing however: may need to also apply a converter:
return (JsonSerializer<Object>) findConvertingSerializer(prov, a, ser);
}
这样我们就能使用自定义注解+自定义的序列化器来实现字段的序列化。
反序列化
反序列化的过程和序列化的过程相似。
java
protected Object _readMapAndClose(JsonParser p0, JavaType valueType)
throws IOException
{
try (JsonParser p = p0) {
Object result;
JsonToken t = _initForReading(p, valueType);
/**这里构建了 使用 _deserializationConfig的 DeserializationConfig
而_deserializationConfig中包含了我们自定义的注解解释器
*/
final DeserializationConfig cfg = getDeserializationConfig();
final DeserializationContext ctxt = createDeserializationContext(p, cfg);
if (t == JsonToken.VALUE_NULL) {
// Ask JsonDeserializer what 'null value' to use:
result = _findRootDeserializer(ctxt, valueType).getNullValue(ctxt);
} else if (t == JsonToken.END_ARRAY || t == JsonToken.END_OBJECT) {
result = null;
} else {
//这里开始反序列化数据
JsonDeserializer<Object> deser = _findRootDeserializer(ctxt, valueType);
if (cfg.useRootWrapping()) {
result = _unwrapAndDeserialize(p, ctxt, cfg, valueType, deser);
} else {
result = deser.deserialize(p, ctxt);
}
ctxt.checkUnresolvedObjectId();
}
if (cfg.isEnabled(DeserializationFeature.FAIL_ON_TRAILING_TOKENS)) {
_verifyNoTrailingTokens(p, ctxt, valueType);
}
return result;
}
}
最终也会执行到创建反序列化器的方法(DeserializationContext)中
java
protected JsonDeserializer<Object> _createDeserializer(DeserializationContext ctxt,
DeserializerFactory factory, JavaType type)
throws JsonMappingException
{
final DeserializationConfig config = ctxt.getConfig();
// First things first: do we need to use abstract type mapping?
if (type.isAbstract() || type.isMapLikeType() || type.isCollectionLikeType()) {
type = factory.mapAbstractType(config, type);
}
BeanDescription beanDesc = config.introspect(type);
// Then: does type define explicit deserializer to use, with annotation(s)?
/**
这里通过获取注解序列化器
*/
JsonDeserializer<Object> deser = findDeserializerFromAnnotation(ctxt,
beanDesc.getClassInfo());
if (deser != null) {
return deser;
}
//... 忽略其他
}
创建注解序列化器的方法:
java
protected JsonDeserializer<Object> findDeserializerFromAnnotation(DeserializationContext ctxt,
Annotated ann)
throws JsonMappingException
{
/**
ctxt中包含了我们自定的注解解释器 - _deserializationConfig。
*/
Object deserDef = ctxt.getAnnotationIntrospector().findDeserializer(ann);
if (deserDef == null) {
return null;
}
JsonDeserializer<Object> deser = ctxt.deserializerInstance(ann, deserDef);
// One more thing however: may need to also apply a converter:
return findConvertingDeserializer(ctxt, ann, deser);
}
这样就能得到我们自定义的注解解释器,并在注解解释器中返回我们自定义的字段序列化和反序列实现。
具体实现
首先我们需要自定义一个注解,以及一个包含了注解的实体类。
java
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnnotation {
}
java
@Data
public class TestBean {
private String name;
private Integer age;
@TestAnnotation
private String serise;
}
然后实现自定义的注解解释器、序列化和反序列化实现:
自定义注解解释器实现 NopAnnotationIntrospector 类,并重写findSerializer()和findDeserializer(),在方法中判断是否包含注解,如果包含则返回我们自定义的序列化和饭序列化实现
java
public class TestDataAnnotationIntrospector extends NopAnnotationIntrospector {
@Override
public Object findSerializer(Annotated am) {
TestAnnotation annotation = am.getAnnotation(TestAnnotation.class);
if (annotation != null) {
return TestDataSerializer.class;
}
return null;
}
@Override
public Object findDeserializer(Annotated am) {
TestAnnotation annotation = am.getAnnotation(TestAnnotation.class);
if (annotation != null) {
return TestDataDeserializer.class;
}
return null;
}
}
序列化和反序列化实现
java
public class TestDataDeserializer extends StdDeserializer<String> {
private static final long serialVersionUID = 1L;
public TestDataDeserializer() {
super(String.class);
}
@Override
public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
// string
String s = p.getValueAsString();
return s + "------我加了结尾";
}
}
public class TestDataSerializer extends StdSerializer<String> {
private static final long serialVersionUID = 1L;
public TestDataSerializer() {
super(String.class);
}
@Override
public void serialize(String value, JsonGenerator gen, SerializerProvider provider) throws IOException {
gen.writeString("我加了开头---------" + value);
}
}
测试代码:
java
public static void main(String[] args){
TestBean testBean = new TestBean();
testBean.setName("这是名称");
testBean.setAge(100);
testBean.setSerise("这是被注解的数据啊啊啊啊");
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setAnnotationIntrospector(new TestDataAnnotationIntrospector());
String json = null;
try {
json = objectMapper.writeValueAsString(testBean);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
System.out.println("json化结果:" + json);
try {
TestBean testBean1 = objectMapper.readValue(json, TestBean.class);
} catch (IOException e) {
e.printStackTrace();
}
}
输出结果:
序列化:
json化结果:{"name":"这是名称","age":100,"serise":"我加了开头---------这是被注解的数据啊啊啊啊"}
反序列化:
springMVC使用自定义的ObjectMapper
方式1:
通过覆盖:springMvc自带的MappingJackson2HttpMessageConverter类
java
@Bean
MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter(){
MappingJackson2HttpMessageConverter converter=new MappingJackson2HttpMessageConverter();
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
objectMapper.setAnnotationIntrospector(new DataAnnotationIntrospector());
objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
converter.setObjectMapper(objectMapper);
return converter;
}
方式2:
通过覆盖:springMvc自带的ObjectMapper类
java
@Bean
ObjectMapper objectMapper(){
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
objectMapper.setAnnotationIntrospector(new DataAnnotationIntrospector());
objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
return objectMapper;
}
方式3:
java
@Configuration
public class ExtWebMvcConfig implements WebMvcConfigurer {
@Bean
ObjectMapper objectMapper(){
Object[] objects = {
SerializationFeature.WRITE_DATES_AS_TIMESTAMPS
, DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS
, DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE
, DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES
, DeserializationFeature.FAIL_ON_INVALID_SUBTYPE
, SerializationFeature.FAIL_ON_EMPTY_BEANS
, DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES
};
Jackson2ObjectMapperFactoryBean objectMapper = new Jackson2ObjectMapperFactoryBean();
objectMapper.setFeaturesToDisable(objects);
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
objectMapper.setFindModulesViaServiceLoader(true);
objectMapper.setAnnotationIntrospector(new DataAnnotationIntrospector());
objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
objectMapper.afterPropertiesSet();
return objectMapper.getObject();
}
问题
1.@JsonIgnore失效
以上按照自定实现内省器的方式自定义注解序列化,在一般情况都没有问题,但是,如果是存在@JsonIgnore注解属性或方法,会导致@JsonIgnore失效甚至会报错。
原因:
NopAnnotationIntrospector中的hasIgnoreMarker 方法(是否有忽略Json的标记)和hasRequiredMarker(是否有必须序列化或反序列化的标记)方法没有做任何的实现,这是不符合实际的。
java
/**
* Method called to check whether given property is marked to
* be ignored. This is used to determine whether to ignore
* properties, on per-property basis, usually combining
* annotations from multiple accessors (getters, setters, fields,
* constructor parameters).
*/
public boolean hasIgnoreMarker(AnnotatedMember m) { return false; }
/**
* Method that can be called to check whether this member has
* an annotation that suggests whether value for matching property
* is required or not.
*/
public Boolean hasRequiredMarker(AnnotatedMember m) { return null; }
解决办法:
1.我们可以重写这hasIgnoreMarker方法和hasRequiredMarker方法。
2.我们自定义的内省器不要继承NopAnnotationIntrospector,改为继承 JacksonAnnotationIntrospector。
java
public class TestDataAnnotationIntrospector extends JacksonAnnotationIntrospector{
@Override
public Object findSerializer(Annotated am) {
TestAnnotation annotation = am.getAnnotation(TestAnnotation.class);
if (annotation != null) {
return TestDataSerializer.class;
}
return null;
}
@Override
public Object findDeserializer(Annotated am) {
TestAnnotation annotation = am.getAnnotation(TestAnnotation.class);
if (annotation != null) {
return TestDataDeserializer.class;
}
return null;
}
}
2.@JsonProperty和@JsonIgnore同时存在导致@JsonIgnore失效
重写findNameForSerialization
方法,先执行父类原来的逻辑,如果PropertyName
为null或者空,则返回null,这样后面的逻辑就不会给PropertyName
赋值了,最后也就不会取消Ignore了。
java
public class TestDataAnnotationIntrospector extends JacksonAnnotationIntrospector {
@Override
public PropertyName findNameForSerialization(Annotated a)
{
PropertyName propertyName = super.findNameForSerialization(a);
return propertyName == null || StringUtils.isEmpty(propertyName.getSimpleName()) ? null : propertyName;
}
@Override
public Object findSerializer(Annotated am) {
TestAnnotation annotation = am.getAnnotation(TestAnnotation.class);
if (annotation != null) {
return TestDataSerializer.class;
}
return null;
}
@Override
public Object findDeserializer(Annotated am) {
TestAnnotation annotation = am.getAnnotation(TestAnnotation.class);
if (annotation != null) {
return TestDataDeserializer.class;
}
return null;
}
}