新年目標Spring Cloud
開始實施,打開慕課網。html
剛學了一章,大致就是調用中國天氣網的api
,使用Spring Boot
構建本身的天氣預報系統,而後使用Spring Cloud
,一步一步使用微服務的思想來演進架構。java
小目標spring
昨天去百度搶了新年紅包,感嘆百度的高併發作的也是如此優秀。apache
阿里的雙十一,百度的新年。(發現了兩者的共同點,可能也是解決併發的一種思路,併發的時候只容許增長數據。)json
感嘆歸感嘆,指望着學習完Spring Cloud
也能設計出優秀的架構,解決併發的一些問題。api
學習時也跟着課程進行編碼,講師講的很是好,可是本課程的重點是後面的微服務架構,因此前面的功能有一些瑕疵,特此提出本身的實現,供你們學習交流。架構
最初的功能很簡單,由於後臺是沒有任何數據的,因此前臺有請求,就直接去天氣網要數據,而後再返回去。併發
這是天氣網api
返回來的數據格式,乍一看沒啥毛病。mvc
{ "data": { "yesterday": { "date": "4日星期一", "high": "高溫 26℃", "fx": "無持續風向", "low": "低溫 18℃", "fl": "<![CDATA[<3級]]>", "type": "多雲" }, "city": "深圳", "forecast": [ { "date": "5日星期二", "high": "高溫 25℃", "fengli": "<![CDATA[<3級]]>", "low": "低溫 18℃", "fengxiang": "無持續風向", "type": "多雲" }, { "date": "6日星期三", "high": "高溫 26℃", "fengli": "<![CDATA[<3級]]>", "low": "低溫 17℃", "fengxiang": "無持續風向", "type": "多雲" }, { "date": "7日星期四", "high": "高溫 27℃", "fengli": "<![CDATA[<3級]]>", "low": "低溫 18℃", "fengxiang": "無持續風向", "type": "多雲" }, { "date": "8日星期五", "high": "高溫 26℃", "fengli": "<![CDATA[<3級]]>", "low": "低溫 17℃", "fengxiang": "無持續風向", "type": "多雲" }, { "date": "9日星期六", "high": "高溫 24℃", "fengli": "<![CDATA[<3級]]>", "low": "低溫 14℃", "fengxiang": "無持續風向", "type": "小雨" } ], "ganmao": "相對今天出現了較大幅度降溫,較易發生感冒,體質較弱的朋友請注意適當防禦。", "wendu": "23" }, "status": 1000, "desc": "OK" }
缺點1:有拼音;ganmao
、wendu
。app
缺點2:名稱不一致;理論上來講yesterday
與forecast
應該是同一個實體,都表示一天的天氣狀況,只是名稱不一樣。可是在yesterday
中,風向和風力是fx
和fl
,在forecast
中,名稱倒是fengli
、fengxiang
。
解決此問題,想到的思路就是使用jackson
進行序列化與反序列化時進行配置的一些註解。
最初使用此種方法實現:
@JsonProperty("wendu") private Float temperature;
一個對象中的名字,一個json
數據中的名字。
能夠實現,可是很差。
舉個例子,天氣api
返回給我wendu
,添加了@JsonProperty
,而後wendu
就綁定到了temperature
上,可是若是我前臺再返回該對象,序列化後生成的名稱仍是wendu
。很差!
目標是實現,反序列化時:從wendu
能綁定到個人temperature
,序列化時直接使用個人字段名。
get、set嘗試
猜想是否是和get
、set
方法有關。
就把@JsonProperty("wendu")
添加到set
方法上,發現並無用。
JsonAlias
後來通過查詢,原來是註解用錯了,此種狀況應使用別名。
關於JsonProperty
和JsonAlias
的詳細講解,請參考Jackson @JsonProperty and @JsonAlias Example。
@JsonAlias("wendu") private Float temperature;
同時,能夠應用多個別名:
@JsonAlias({"fengli", "fl"}) private String windForce;
發起請求的示例代碼,供之後參考。
@Autowired private RestTemplate restTemplate; @Override public Weather getWeatherByCityName(String cityName) { return this.getWeatherByUrl(BASE_URL + "?" + CITY_NAME + "=" + cityName) .getData(); } private Response getWeatherByUrl(String url) { // 發起Get請求 ResponseEntity<String> response = restTemplate.getForEntity(url, String.class); // 若是狀態碼非200, 拋異常 if (response.getStatusCodeValue() != 200) { throw new YunzhiNetworkException("數據請求失敗"); } // 實例化對象映射對象 ObjectMapper mapper = new ObjectMapper(); // 初始化響應數據 Response data; // 從字符串轉換爲Response對象 try { data = mapper.readValue(response.getBody(), Response.class); } catch (IOException e) { throw new YunzhiIOException("json數據轉換失敗"); } // 返回 return data; }
RestTemplate配置
這裏與正常的RestTemplate
構建有些不一樣,一般的RestTemplate
是使用Spring
工具類構造的,此處使用Apache
的Http
組件構造,以支持更多的數據格式。
implementation 'org.apache.httpcomponents:httpclient'
同時去除了默認的對String
的Http
消息轉換器,默認的轉換器使用的不是UTF-8
編碼。
講師原文章:Spring RestTemplate 調用天氣預報接口亂碼的解決
@Configuration public class BeanConfiguration { @Bean public RestTemplate restTemplate() { // 使用Apache HttpClient構建RestTemplate, 支持的比Spring自帶的更多 RestTemplate restTemplate = new RestTemplate(new HttpComponentsClientHttpRequestFactory()); // 去除默認的String轉換器 restTemplate.getMessageConverters().removeIf(converter -> converter instanceof StringHttpMessageConverter); // 添加自定義的String轉換器, 支持UTF-8 restTemplate.getMessageConverters() .add(new StringHttpMessageConverter(StandardCharsets.UTF_8)); return restTemplate; } }
同時,在編寫單元測試的時候,看了一篇關於AssertJ
的文章。Testing with AssertJ assertions - Tutorial
以前學Junit5
的時候,以爲這個東西挺好使的啊?爲何被開源社區拋棄而使用AssertJ
呢?
原來以前用的斷言都太簡單,其實AssertJ
遠比咱們使用的更強大。
@Test public void getWeatherByCityName() throws Exception { final String cityName = "深圳"; MvcResult mvcResult = this.mockMvc .perform(MockMvcRequestBuilders.get(BASE_URL + "/cityName/" + cityName)) .andExpect(MockMvcResultMatchers.status().isOk()) .andReturn(); String json = mvcResult.getResponse().getContentAsString(); Assertions.assertThat(json) .contains("cityName", "cold", "temperature", "windDirection", "windForce") .doesNotContain("ganmao", "wendu", "fx", "fl", "fengxiang", "fengli"); }
多看英文文章,
Tutorial
寫得都特別好。