场景
由于想在接口文档上显示属性的描述,于是使用了如下代码
@JsonProperty(value = "aaBB")
@ApiModelProperty(value = "描述内容")
private Integer cc;
可是打开接口文档一看,原来的cc属性不见了,只剩下aaBB。
小白我只知道,@JsonProperty适用于序列化时转换的,@ApiModelProperty是为了接口文档展示使用的。两者这么说下来好像没什么关系啊。没想明白,心里有点堵。上网搜寻了好久,只发现别人有使用@JsonProperty来强制改变名字的需求,但是没有说明为啥可以这样。只能自己慢慢看看源码了…
swagger接口文档的内容总归来源于你的代码,所有的信息其实就是在AbstractApplicationContext 的refresh方法,有了雏形。
AbstractApplicationContext.refresh ->
EmbeddedWebApplicationContext.finishRefresh ->
AbstractApplicationContext.finishRefresh ->
DefaultLifecycleProcessor.onRefresh -> startBeans -> start -> doStart
->DocumentationPluginsBootstrapper.start -> scanDocumentation
ApiDocumentationScanner.scan ->
ApiListingScanner.scan ->
ApiModelReader.read ->
… ->
BasicBeanDescription.findProperties ->
… ->
POJOPropertiesCollector.collectAll -> _addFields
上面记录的流程有点多,不用慌,只是为了更加清楚经过了哪些流程,不仅可以知道其来龙去脉,还能方便以后深入学习。今天就简单解决下遇到的问题,了解一下到底是哪一步导致的问题。
上述流程可以看出来,主要经历在onRefresh 里面触发了Api相关的读取操作。在收集model(实体类)的属性时,就已经和JsonProperty纠缠不清了。
collectAll
protected void collectAll()
{
LinkedHashMap<String, POJOPropertyBuilder> props = new LinkedHashMap<String, POJOPropertyBuilder>();
// 获取基本的属性,并且这里会记录每个属性可能有的别名
_addFields(props);
_addMethods(props);
// 25-Jan-2016, tatu: Avoid introspecting (constructor-)creators for non-static
// inner classes, see [databind#1502]
if (!_classDef.isNonStaticInnerClass()) {
_addCreators(props);
}
_addInjectables(props);
......
// 重命名属性,就是这里将结合基本属性信息与_addFields收集的别名信息
_renameProperties(props);
......
}
_addFields
protected void _addFields(Map<String, POJOPropertyBuilder> props)
{
......
// 遍历所有属性
for (AnnotatedField f : _classDef.fields()) {
// 获取字段基础名字(就是开头例子的 cc)
String implName = ai.findImplicitPropertyName(f);
// 记录有@JsonValue的字段
if (Boolean.TRUE.equals(ai.hasAsValue(f))) {
if (_jsonValueAccessors == null) {
_jsonValueAccessors = new LinkedList<>();
}
_jsonValueAccessors.add(f);
continue;
}
// 记录有@JsonAnySetter的字段
if (Boolean.TRUE.equals(ai.hasAnySetter(f))) {
if (_anySetterField == null) {
_anySetterField = new LinkedList<AnnotatedMember>();
}
_anySetterField.add(f);
continue;
}
if (implName == null) {
implName = f.getName();
}
PropertyName pn;
// 寻找序列化相关的名字
if (_forSerialization) {
/* 18-Aug-2011, tatu: As per existing unit tests, we should only
* use serialization annotation (@JsonSerialize) when serializing
* fields, and similarly for deserialize-only annotations... so
* no fallbacks in this particular case.
*/
pn = ai.findNameForSerialization(f);
} else {
pn = ai.findNameForDeserialization(f);
}
boolean hasName = (pn != null);
boolean nameExplicit = hasName;
// 记录定义的别名
if (nameExplicit && pn.isEmpty()) { // empty String meaning "use default name", here just means "same as field name"
pn = _propNameFromSimple(implName);
nameExplicit = false;
}
......
_property(props, implName).addField(f, pn, nameExplicit, visible, ignored);
}
}
findNameForDeserialization
public PropertyName findNameForDeserialization(Annotated a)
{
// @JsonSetter 优先级高于 @JsonProperty
boolean useDefault = false;
JsonSetter js = _findAnnotation(a, JsonSetter.class);
if (js != null) {
String s = js.value();
// 04-May-2018, tatu: Need to allow for "nameless" `@JsonSetter` too
if (s.isEmpty()) {
useDefault = true;
} else {
return PropertyName.construct(s);
}
}
JsonProperty pann = _findAnnotation(a, JsonProperty.class);
// 找到JsonProperty的value作为逻辑名字
if (pann != null) {
return PropertyName.construct(pann.value());
}
if (useDefault || _hasOneOf(a, ANNOTATIONS_TO_INFER_DESER)) {
return PropertyName.USE_DEFAULT;
}
return null;
}
总结
没错,就是在findNameForDeserialization里面找到了JsonProperty定义的value,之后会在_renameProperties里面替换掉我们的 “cc"为 ”aaBB"。