解決feign接口返回泛型設(shè)置屬性為null的問題
簡介
feign是一種聲明式http請求調(diào)用方式,工作原理就是根據(jù)FeignClient注解生成新的接口(也就是傳說中的動態(tài)代理),常見使用方式如下所示:
@FeignClient(name="UserFeignService",url="${auth.url}", fallbackFactory = OrgFeignServiceFallback.class, configuration = FeignErrorDecoderConfiguration.class) public interface OrgFeignService { /** * * @param org * @return */ @PostMapping(value="Tenant/AddTenantOrg", consumes="application/json; charset=UTF-8") APIResultTO<TenantOrg> addOrg(OrgDto org, @RequestHeader("token")String token); }
應(yīng)用場景
1、序列化以及反序列化采用jackson
2、調(diào)用第三方采用feign注解式接口
問題分析
APIResultTO是一個api通用接口返回泛型類,TenantOrg為傳入的具體泛型類,咱們來看下出問題的類:
@Getter @Setter @NoArgsConstructor public class TenantOrg { /** */ @JsonProperty("Id") private String Id; /** * 父級Id */ @JsonProperty("PId") private String PId; /** * 租戶代碼 */ @JsonProperty("Tenant") private String tenant; /** * 組織架構(gòu)名字 */ @JsonProperty("Name") private String name; }
必須要用@JsonProperty("Id")或者@JsonSetter("Id")注解來顯示聲明屬性名字,尤其是首字母為大寫的情況,否則反序列化后的數(shù)據(jù)就為空值。
為什么TenantOrg類中的Id等其他屬性跟第三方服務(wù)返回的json數(shù)據(jù)字段完全一致,卻沒有成功設(shè)置對應(yīng)的屬性呢,這個就要看下BeanDeserializer類的deserializeFromObject方法,從其名字上我們可以看出這是將請求返回的數(shù)據(jù)反序列化成對應(yīng)的類對象:
public Object deserializeFromObject(JsonParser p, DeserializationContext ctxt) throws IOException { /* 09-Dec-2014, tatu: As per [databind#622], we need to allow Object Id references * to come in as JSON Objects as well; but for now assume they will * be simple, single-property references, which means that we can * recognize them without having to buffer anything. * Once again, if we must, we can do more complex handling with buffering, * but let's only do that if and when that becomes necessary. */ if ((_objectIdReader != null) && _objectIdReader.maySerializeAsObject()) { if (p.hasTokenId(JsonTokenId.ID_FIELD_NAME) && _objectIdReader.isValidReferencePropertyName(p.getCurrentName(), p)) { return deserializeFromObjectId(p, ctxt); } } if (_nonStandardCreation) { if (_unwrappedPropertyHandler != null) { return deserializeWithUnwrapped(p, ctxt); } if (_externalTypeIdHandler != null) { return deserializeWithExternalTypeId(p, ctxt); } Object bean = deserializeFromObjectUsingNonDefault(p, ctxt); if (_injectables != null) { injectValues(ctxt, bean); } /* 27-May-2014, tatu: I don't think view processing would work * at this point, so commenting it out; but leaving in place * just in case I forgot something fundamental... */ /* if (_needViewProcesing) { Class<?> view = ctxt.getActiveView(); if (view != null) { return deserializeWithView(p, ctxt, bean, view); } } */ return bean; } final Object bean = _valueInstantiator.createUsingDefault(ctxt); // [databind#631]: Assign current value, to be accessible by custom deserializers p.setCurrentValue(bean); if (p.canReadObjectId()) { Object id = p.getObjectId(); if (id != null) { _handleTypedObjectId(p, ctxt, bean, id); } } if (_injectables != null) { injectValues(ctxt, bean); } if (_needViewProcesing) { Class<?> view = ctxt.getActiveView(); if (view != null) { return deserializeWithView(p, ctxt, bean, view); } } if (p.hasTokenId(JsonTokenId.ID_FIELD_NAME)) { String propName = p.getCurrentName(); do { p.nextToken(); //如果要跟蹤測試的話,直接定位到該位置就可以,你就會發(fā)現(xiàn)如果沒有 //JSONProperty之類的注解定義屬性名字的話,Id、PId屬性在_beanProperties都成了小寫的屬性 SettableBeanProperty prop = _beanProperties.find(propName); if (prop != null) { // normal case try { prop.deserializeAndSet(p, ctxt, bean); } catch (Exception e) { wrapAndThrow(e, bean, propName, ctxt); } continue; } handleUnknownVanilla(p, ctxt, bean, propName); } while ((propName = p.nextFieldName()) != null); } return bean; }
具體如下圖所示:
正如上面所示,用@JsonProperty注解配置的屬性,在反序列化時就按照@JsonProperty注解定義的屬性名相同,至于為什么在TenantOrg中定義的PId屬性在使用時怎么變成了pid,
具體可以看下POJOPropertiesCollector類的_removeUnwantedProperties方法以及_renameProperties方法:
protected void _removeUnwantedProperties(Map<String, POJOPropertyBuilder> props) { Iterator<POJOPropertyBuilder> it = props.values().iterator(); while (it.hasNext()) { POJOPropertyBuilder prop = it.next(); // 去除private屬性,PId屬性會在這里移除 if (!prop.anyVisible()) { it.remove(); continue; } // Otherwise, check ignorals if (prop.anyIgnorals()) { // first: if one or more ignorals, and no explicit markers, remove the whole thing if (!prop.isExplicitlyIncluded()) { it.remove(); _collectIgnorals(prop.getName()); continue; } // otherwise just remove ones marked to be ignored prop.removeIgnored(); if (!prop.couldDeserialize()) { _collectIgnorals(prop.getName()); } } } }
protected void _renameProperties(Map<String, POJOPropertyBuilder> props) { // With renaming need to do in phases: first, find properties to rename Iterator<Map.Entry<String,POJOPropertyBuilder>> it = props.entrySet().iterator(); LinkedList<POJOPropertyBuilder> renamed = null; while (it.hasNext()) { Map.Entry<String, POJOPropertyBuilder> entry = it.next(); POJOPropertyBuilder prop = entry.getValue(); //被@JsonProperty注解的屬性會找到對應(yīng)的屬性名 Collection<PropertyName> l = prop.findExplicitNames(); // no explicit names? Implicit one is fine as is if (l.isEmpty()) { continue; } it.remove(); // need to replace with one or more renamed if (renamed == null) { renamed = new LinkedList<POJOPropertyBuilder>(); } // simple renaming? Just do it //在這里使用@JsonProperty注解里面定義的屬性名,比如PId、Id等 //所以使用了@JsonProperty注解后,我們就無需關(guān)注類里面屬性的大小寫,設(shè)置不用關(guān)注屬性名 if (l.size() == 1) { PropertyName n = l.iterator().next(); renamed.add(prop.withName(n)); continue; } // but this may be problematic... renamed.addAll(prop.explode(l)); /* String newName = prop.findNewName(); if (newName != null) { if (renamed == null) { renamed = new LinkedList<POJOPropertyBuilder>(); } prop = prop.withSimpleName(newName); renamed.add(prop); it.remove(); } */ } // and if any were renamed, merge back in... if (renamed != null) { for (POJOPropertyBuilder prop : renamed) { String name = prop.getName(); POJOPropertyBuilder old = props.get(name); if (old == null) { props.put(name, prop); } else { old.addAll(prop); } // replace the creatorProperty too, if there is one _updateCreatorProperty(prop, _creatorProperties); // [databind#2001]: New name of property was ignored previously? Remove from ignored // 01-May-2018, tatu: I have a feeling this will need to be revisited at some point, // to avoid removing some types of removals, possibly. But will do for now. if (_ignoredPropertyNames != null) { _ignoredPropertyNames.remove(name); } } } }
springcloud feign請求:數(shù)據(jù)返回null
問題描述
調(diào)用方調(diào)用服務(wù),DEBUG被調(diào)用方服務(wù)得到正確數(shù)據(jù),但調(diào)用方返回的數(shù)據(jù)對象屬性全為null
原因及解決方法:
在feign調(diào)用接口中與被調(diào)用方接口返回類型不一致。
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java-ElementUi中的row-class-name使用
這篇文章主要介紹了Java-ElementUi中的row-class-name使用方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-08-08超簡單的java獲取鼠標(biāo)點擊位置坐標(biāo)的實例(鼠標(biāo)在Jframe上的坐標(biāo))
在Java窗體Jframe上獲取鼠標(biāo)點擊的坐標(biāo),其中使用了匿名內(nèi)部類,實例代碼非常簡單易懂,大家可以學(xué)習(xí)一下2018-03-03Mybatis實現(xiàn)關(guān)聯(lián)關(guān)系映射的方法示例
本文主要介紹了Mybatis實現(xiàn)關(guān)聯(lián)關(guān)系映射的方法示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-07-07Java實現(xiàn)UDP通信過程實例分析【服務(wù)器端與客戶端】
這篇文章主要介紹了Java實現(xiàn)UDP通信過程,結(jié)合實例形式分析了java實現(xiàn)UDP服務(wù)器端與客戶端相關(guān)操作技巧與注意事項,需要的朋友可以參考下2020-05-05如何使用IntelliJ IDEA搭建MyBatis-Plus框架并連接MySQL數(shù)據(jù)庫
這篇文章主要介紹了如何使用IntelliJ IDEA搭建MyBatis-Plus框架并連接MySQL數(shù)據(jù)庫,本文通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧2023-11-11