章節目錄
1. 前言
先用SpringBoot搭建一個簡單的單體項目,後期,再用SpringCluod把它改造一個微服務架構的項目。其實,寫這篇博客的真實原因也就學習一下SpringCloud。從單體架構過渡到微服務架構,可以深刻地體會到這兩者架構的區別。而且,從今後的發展來看,確實很有必要接觸微服務架構
2. 數據來源
獲取預報天氣的數據來源:調用第三方接口進行獲取。網上有很多免費的天氣預報接口,隨便找找就有了,也可推薦一個高德地圖的天氣預報接口,它只能根據城市ID獲取天氣信息,但它還有個接口“行政區域查詢”----可以將城市名稱轉換爲城市ID,我覺得比較麻煩,就放棄了。這裏是使用了一個不知名的接口:
- 根據城市名獲取天氣信息:http://wthrcdn.etouch.cn/weather_mini?city=深圳
- 根據城市ID取天氣信息:http://wthrcdn.etouch.cn/weather_mini?citykey=101280601
3. 實戰
因爲此項目是以SpringBoot爲基礎進行搭建的,所以,建議有SpringBoot基礎的讀者再看。
項目結構
項目依賴
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- httpclient -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
3.1 開發環境
- IDEA:2018.2(安裝lombok插件)
- JDK:8
- MAVEN:3.6.0
- SpringBoot:2.3.0
3.2 功能需求
- 通過城市ID獲取天氣信息
- 通過城市名稱獲取天氣信息
這兩個功能可以通過調用第三方接口進行實現
3.3 手動編碼
將接口返回的Json數據進行抽象、封裝成Java對象,方便開發人員操作
3.3.1 vo層
WeatherResponseVO:
@Data
public class WeatherResponseVO implements Serializable {
private static final long serialVersionUID = -8483256225271502962L;
private WeatherVO data;
private Integer status;
private String desc;
}
- @Data註解:可以省略對象的set()/get()方法。@Data註解 與 lombok
- 實現Serializable 接口:因爲這些Java對象要通過網絡傳輸,所以,要序列化
WeatherVO:
@Data
public class WeatherVO implements Serializable {
private static final long serialVersionUID = 4089597935549696545L;
private String city;
private String ganmao;
private String wendu;
private YesterdayVO yesterday;
private List<ForecastVO> forecast;
}
YesterdayVO:
@Data
public class YesterdayVO implements Serializable {
private static final long serialVersionUID = -806309024676977591L;
private String date;
private String high;
private String fx;
private String low;
private String fl;
private String type;
}
ForecastVO:
@Data
public class ForecastVO implements Serializable {
private static final long serialVersionUID = 1686655601208573654L;
private String date;
private String high;
private String fengli;
private String low;
private String fengxiang;
private String type;
}
3.3.2 service層
此層是面向接口開發
WeatherDataService:
public interface WeatherDataService {
// 根據城市id查詢天氣數據
WeatherResponseVO getDataByCityId(String cityId);
// 根據城市名稱查詢天氣數據
WeatherResponseVO getDataByCityName(String cityName);
}
WeatherDataServiceImpl:
@Service
public class WeatherDataServiceImpl implements WeatherDataService {
private static final String WEATHER_URL = "http://wthrcdn.etouch.cn/weather_mini?";
@Autowired
private RestTemplate restTemplate;
@Override
public WeatherResponseVO getDataByCityId(String cityId) {
String uri = WEATHER_URL + "citykey=" + cityId;
return doGetWeather(uri, WeatherResponseVO.class);
}
@Override
public WeatherResponseVO getDataByCityName(String cityName) {
String uri = WEATHER_URL + "city=" + cityName;
return doGetWeather(uri, WeatherResponseVO.class);
}
}
- 由於兩個方法中都使用到了部分相同的uri,所以可以提取相同的uri作爲靜態常量
- RestTemplate:用於HTTP通信。使用HttpClien進行http通信,代碼複雜,還得手動資源回收等。Springboot — 用更優雅的方式發HTTP請求(RestTemplate詳解)
- 由於兩個方法中都是通過RestTemplate進行http通信,並將返回的json數據轉換爲java對象,所以,便可將相同的代碼進行抽取爲一個共同的方法(重構)
doGetWeather():
private <T> T doGetWeather(String uri, Class<T> type) {
String key = uri;
String strBody = null;
ResponseEntity<String> responseEntity = restTemplate.getForEntity(uri, String.class);
if (StatusCodeConstant.OK == responseEntity.getStatusCodeValue()) {
strBody = responseEntity.getBody();
}
ObjectMapper objectMapper = new ObjectMapper();
T t = null;
try {
t = objectMapper.readValue(strBody, type);
} catch (Exception e) {
log.error("Error!", e);
}
return t;
}
- 使用了 objectMapper.readValue()方法將json字符串轉換爲java對象(json字符串中的key要與對象的屬性名及類型相對應)
- 返回值使用了泛型
3.3.3 controller層
WeatherController:
@RestController
@RequestMapping("/weather")
public class WeatherController {
@Autowired
private WeatherDataService weatherDataService;
@GetMapping("/cityId/{cityId}")
public WeatherResponseVO getWeatherByCityId(@PathVariable("cityId") String cityId) {
return weatherDataService.getDataByCityId(cityId);
}
@GetMapping("/cityName/{cityName}")
public WeatherResponseVO getWeatherByCityName(@PathVariable("cityName") String cityName) {
return weatherDataService.getDataByCityName(cityName);
}
}
3.3.4 配置類
@Configuration
public class RestConfig {
@Autowired
private RestTemplateBuilder builder;
@Bean
public RestTemplate restTemplate() {
return builder.build();
}
}
配置RestTemplate:在啓動時,RestTemplate會在classpath查找中有哪些依賴,因爲已添加HttpClient依賴,所以,RestTemplate會默認把它作爲默認的實現
3.3.5 測試
啓動main()方法,在瀏覽器中訪問controller層的接口
1)、根據城市ID獲取天氣信息
2)、根據城市名稱獲取天氣信息
好了,這兩個功能已經實現了。接下來就對它進行簡單地優化了。