解決feign接口返回泛型設(shè)置屬性為null的問(wèn)題
簡(jiǎn)介
feign是一種聲明式http請(qǐng)求調(diào)用方式,工作原理就是根據(jù)FeignClient注解生成新的接口(也就是傳說(shuō)中的動(dòng)態(tài)代理),常見(jiàn)使用方式如下所示:
@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)用場(chǎng)景
1、序列化以及反序列化采用jackson
2、調(diào)用第三方采用feign注解式接口
問(wèn)題分析
APIResultTO是一個(gè)api通用接口返回泛型類(lèi),TenantOrg為傳入的具體泛型類(lèi),咱們來(lái)看下出問(wèn)題的類(lèi):
@Getter @Setter @NoArgsConstructor public class TenantOrg { /** */ @JsonProperty("Id") private String Id; /** * 父級(jí)Id */ @JsonProperty("PId") private String PId; /** * 租戶(hù)代碼 */ @JsonProperty("Tenant") private String tenant; /** * 組織架構(gòu)名字 */ @JsonProperty("Name") private String name; }
必須要用@JsonProperty("Id")或者@JsonSetter("Id")注解來(lái)顯示聲明屬性名字,尤其是首字母為大寫(xiě)的情況,否則反序列化后的數(shù)據(jù)就為空值。
為什么TenantOrg類(lèi)中的Id等其他屬性跟第三方服務(wù)返回的json數(shù)據(jù)字段完全一致,卻沒(méi)有成功設(shè)置對(duì)應(yīng)的屬性呢,這個(gè)就要看下BeanDeserializer類(lèi)的deserializeFromObject方法,從其名字上我們可以看出這是將請(qǐng)求返回的數(shù)據(jù)反序列化成對(duì)應(yīng)的類(lèi)對(duì)象:
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(); //如果要跟蹤測(cè)試的話(huà),直接定位到該位置就可以,你就會(huì)發(fā)現(xiàn)如果沒(méi)有 //JSONProperty之類(lèi)的注解定義屬性名字的話(huà),Id、PId屬性在_beanProperties都成了小寫(xiě)的屬性 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注解配置的屬性,在反序列化時(shí)就按照@JsonProperty注解定義的屬性名相同,至于為什么在TenantOrg中定義的PId屬性在使用時(shí)怎么變成了pid,
具體可以看下POJOPropertiesCollector類(lèi)的_removeUnwantedProperties方法以及_renameProperties方法:
protected void _removeUnwantedProperties(Map<String, POJOPropertyBuilder> props) { Iterator<POJOPropertyBuilder> it = props.values().iterator(); while (it.hasNext()) { POJOPropertyBuilder prop = it.next(); // 去除private屬性,PId屬性會(huì)在這里移除 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注解的屬性會(huì)找到對(duì)應(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注解后,我們就無(wú)需關(guān)注類(lèi)里面屬性的大小寫(xiě),設(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請(qǐng)求:數(shù)據(jù)返回null
問(wèn)題描述
調(diào)用方調(diào)用服務(wù),DEBUG被調(diào)用方服務(wù)得到正確數(shù)據(jù),但調(diào)用方返回的數(shù)據(jù)對(duì)象屬性全為null
原因及解決方法:
在feign調(diào)用接口中與被調(diào)用方接口返回類(lèi)型不一致。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
java面試題解LeetCode27二叉樹(shù)的鏡像實(shí)例
這篇文章主要為大家介紹了java面試題解LeetCode27二叉樹(shù)的鏡像實(shí)例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01Java-ElementUi中的row-class-name使用
這篇文章主要介紹了Java-ElementUi中的row-class-name使用方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-08-08超簡(jiǎn)單的java獲取鼠標(biāo)點(diǎn)擊位置坐標(biāo)的實(shí)例(鼠標(biāo)在Jframe上的坐標(biāo))
在Java窗體Jframe上獲取鼠標(biāo)點(diǎn)擊的坐標(biāo),其中使用了匿名內(nèi)部類(lèi),實(shí)例代碼非常簡(jiǎn)單易懂,大家可以學(xué)習(xí)一下2018-03-03Javafx利用fxml變換場(chǎng)景的實(shí)現(xiàn)示例
本文主要介紹了Javafx利用fxml變換場(chǎng)景的實(shí)現(xiàn)示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2024-07-07Mybatis實(shí)現(xiàn)關(guān)聯(lián)關(guān)系映射的方法示例
本文主要介紹了Mybatis實(shí)現(xiàn)關(guān)聯(lián)關(guān)系映射的方法示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-07-07Java日期時(shí)間格式化操作DateUtils 的整理
這篇文章主要介紹了Java日期時(shí)間格式化操作DateUtils 的整理的相關(guān)資料,這里總結(jié)了java日期格式化的操作,需要的朋友可以參考下2017-07-07Java實(shí)現(xiàn)UDP通信過(guò)程實(shí)例分析【服務(wù)器端與客戶(hù)端】
這篇文章主要介紹了Java實(shí)現(xiàn)UDP通信過(guò)程,結(jié)合實(shí)例形式分析了java實(shí)現(xiàn)UDP服務(wù)器端與客戶(hù)端相關(guān)操作技巧與注意事項(xiàng),需要的朋友可以參考下2020-05-05如何使用IntelliJ IDEA搭建MyBatis-Plus框架并連接MySQL數(shù)據(jù)庫(kù)
這篇文章主要介紹了如何使用IntelliJ IDEA搭建MyBatis-Plus框架并連接MySQL數(shù)據(jù)庫(kù),本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2023-11-11