微信号:imooc-com

介绍:慕课网是一个超酷的互联网、IT技术免费学习平台,创新的网络一站式学习、实践体验;服务及时贴心,内容专业、有趣易学。专注服务互联网工程师快速成为技术高手!

Spring Boot(一):使用Spring Boot实现天气预报服务(附源码)

2018-02-27 19:50 老卫

本文,我们将基于 Spring Boot 技术来实现一个微服务天气预报服务接口——micro-weather-basic。micro-weather-basic 的作用是实现简单的天气预报功能,可以根据不同的城市,查询该城市的实时天气情况。



开发环境


  • Gradle 4.0

  • Spring Boot 1.5.6

  • Apache HttpClient 1.5.3



数据来源


理论上,天气的数据是天气预报的实现基础。本应用与实际的天气数据无关,理论上,可以兼容多种数据来源。但为求简单,我们在网上找了一个免费、可用的天气数据接口。


天气数据来源为中华万年历。例如:

  • 通过城市名字获得天气数据 :http://wthrcdn.etouch.cn/weather_mini?city=深圳

  • 通过城市id获得天气数据:http://wthrcdn.etouch.cn/weather_mini?citykey=101280601


城市ID列表。每个城市都有一个唯一的ID作为标识。见 http://cj.weather.com.cn/support/Detail.aspx?id=51837fba1b35fe0f8411b6df 或者 http://mobile.weather.com.cn/js/citylist.xml。


调用天气服务接口示例,我们以“深圳”城市为例,可用看到如下天气数据返回。


 
           
  1. {

  2.  "data": {

  3.  "yesterday": {

  4.  "date""1日星期五",

  5.  "high""高温 33℃",

  6.  "fx""无持续风向",

  7.  "low""低温 26℃",

  8.  "fl""<![CDATA[<3级]]>",

  9.  "type""多云"

  10.  },

  11.  "city""深圳",

  12.  "aqi""72",

  13.  "forecast": [

  14.  {

  15.  "date""2日星期六",

  16.  "high""高温 32℃",

  17.  "fengli""<![CDATA[<3级]]>",

  18.  "low""低温 26℃",

  19.  "fengxiang""无持续风向",

  20.  "type""阵雨"

  21.  },

  22.  {

  23.  "date""3日星期天",

  24.  "high""高温 29℃",

  25.  "fengli""<![CDATA[5-6级]]>",

  26.  "low""低温 26℃",

  27.  "fengxiang""无持续风向",

  28.  "type""大雨"

  29.  },

  30.  {

  31.  "date""4日星期一",

  32.  "high""高温 29℃",

  33.  "fengli""<![CDATA[3-4级]]>",

  34.  "low""低温 26℃",

  35.  "fengxiang""西南风",

  36.  "type""暴雨"

  37.  },

  38.  {

  39.  "date""5日星期二",

  40.  "high""高温 31℃",

  41.  "fengli""<![CDATA[<3级]]>",

  42.  "low""低温 27℃",

  43.  "fengxiang""无持续风向",

  44.  "type""阵雨"

  45.  },

  46.  {

  47.  "date""6日星期三",

  48.  "high""高温 32℃",

  49.  "fengli""<![CDATA[<3级]]>",

  50.  "low""低温 27℃",

  51.  "fengxiang""无持续风向",

  52.  "type""阵雨"

  53.  }

  54.  ],

  55.  "ganmao""风较大,阴冷潮湿,较易发生感冒,体质较弱的朋友请注意适当防护。",

  56.  "wendu""29"

  57.  },

  58.  "status"1000,

  59.  "desc""OK"

  60. }

我们通过观察数据,来了解每个返回字段的含义。

  • "city": 城市名称

  • "aqi": 空气指数,

  • "wendu": 实时温度

  • "date": 日期,包含未来5天

  • "high":最高温度

  • "low": 最低温度

  • "fengli": 风力

  • "fengxiang": 风向

  • "type": 天气类型


以上数据,是我们需要的天气数据的核心数据,但是,同时也要关注下面两个字段:


  • "status": 接口调用的返回状态,返回值“1000”,意味着数据是接口正常

  • "desc": 接口状态的描述,“OK”代表接口正常


重点关注返回值不是“1000”的情况,说明,这个接口调用异常了。


初始化一个 Spring Boot 项目



初始化一个 Spring Boot 项目 micro-weather-basic,该项目可以直接在我们之前章节课程中的 basic-gradle 项目基础进行修改。同时,为了优化项目的构建速度,我们对Maven中央仓库地址和 Gradle Wrapper 地址做了调整。其中细节暂且不表,读者可以自行参阅源码,或者学习笔者所著的《Spring Boot 教程》(https://github.com/waylau/spring-boot-tutorial)。其原理,我也整理到我的博客中了:



项目配置


添加 Apache HttpClient 的依赖,来作为我们Web请求的客户端。

 
           
  1. // 依赖关系

  2. dependencies {

  3.  //...

  4.  // 添加 Apache HttpClient 依赖

  5.  compile('org.apache.httpcomponents:httpclient:4.5.3')

  6.  //...

  7. }


创建天气信息相关的值对象


创建 com.waylau.spring.cloud.vo包,用于相关值对象。创建天气信息类 Weather


 
           
  1. public class Weather implements Serializable {

  2.  private static final long serialVersionUID = 1L;

  3.  private String city;

  4.  private String aqi;

  5.  private String wendu;

  6.  private String ganmao;

  7.  private Yesterday yesterday;

  8.  private List<Forecast> forecast;

  9.  public String getCity() {

  10.  return city;

  11.  }

  12.  public void setCity(String city) {

  13.  this.city = city;

  14.  }

  15.  public String getAqi() {

  16.  return aqi;

  17.  }

  18.  public void setAqi(String aqi) {

  19.  this.aqi = aqi;

  20.  }

  21.  public String getWendu() {

  22.  return wendu;

  23.  }

  24.  public void setWendu(String wendu) {

  25.  this.wendu = wendu;

  26.  }

  27.  public String getGanmao() {

  28.  return ganmao;

  29.  }

  30.  public void setGanmao(String ganmao) {

  31.  this.ganmao = ganmao;

  32.  }

  33.  public Yesterday getYesterday() {

  34.  return yesterday;

  35.  }

  36.  public void setYesterday(Yesterday yesterday) {

  37.  this.yesterday = yesterday;

  38.  }

  39.  public List<Forecast> getForecast() {

  40.  return forecast;

  41.  }

  42.  public void setForecast(List<Forecast> forecast) {

  43.  this.forecast = forecast;

  44.  }

  45. }


昨日天气信息:

 
           
  1. public class Yesterday implements Serializable {

  2.  private static final long serialVersionUID = 1L;

  3.  private String date;

  4.  private String high;

  5.  private String fx;

  6.  private String low;

  7.  private String fl;

  8.  private String type;

  9.  public Yesterday() {

  10.  }

  11.  public String getDate() {

  12.  return date;

  13.  }

  14.  public void setDate(String date) {

  15.  this.date = date;

  16.  }

  17.  public String getHigh() {

  18.  return high;

  19.  }

  20.  public void setHigh(String high) {

  21.  this.high = high;

  22.  }

  23.  public String getFx() {

  24.  return fx;

  25.  }

  26.  public void setFx(String fx) {

  27.  this.fx = fx;

  28.  }

  29.  public String getLow() {

  30.  return low;

  31.  }

  32.  public void setLow(String low) {

  33.  this.low = low;

  34.  }

  35.  public String getFl() {

  36.  return fl;

  37.  }

  38.  public void setFl(String fl) {

  39.  this.fl = fl;

  40.  }

  41.  public String getType() {

  42.  return type;

  43.  }

  44.  public void setType(String type) {

  45.  this.type = type;

  46.  }

  47. }


未来天气信息:

 
           
  1. public class Forecast implements Serializable {

  2.  private static final long serialVersionUID = 1L;

  3.  private String date;

  4.  private String high;

  5.  private String fengxiang;

  6.  private String low;

  7.  private String fengli;

  8.  private String type;

  9.  public String getDate() {

  10.  return date;

  11.  }

  12.  public void setDate(String date) {

  13.  this.date = date;

  14.  }

  15.  public String getHigh() {

  16.  return high;

  17.  }

  18.  public void setHigh(String high) {

  19.  this.high = high;

  20.  }

  21.  public String getFengxiang() {

  22.  return fengxiang;

  23.  }

  24.  public void setFengxiang(String fengxiang) {

  25.  this.fengxiang = fengxiang;

  26.  }

  27.  public String getLow() {

  28.  return low;

  29.  }

  30.  public void setLow(String low) {

  31.  this.low = low;

  32.  }

  33.  public String getFengli() {

  34.  return fengli;

  35.  }

  36.  public void setFengli(String fengli) {

  37.  this.fengli = fengli;

  38.  }

  39.  public String getType() {

  40.  return type;

  41.  }

  42.  public void setType(String type) {

  43.  this.type = type;

  44.  }

  45.  public Forecast() {

  46.  }

  47. }


WeatherResponse 作为整个消息的返回对象

 
           
  1. public class WeatherResponse implements Serializable {

  2.  private static final long serialVersionUID = 1L;

  3.  private Weather data; // 消息数据

  4.  private String status; // 消息状态

  5.  private String desc; // 消息描述

  6.  public Weather getData() {

  7.  return data;

  8.  }

  9.  public void setData(Weather data) {

  10.  this.data = data;

  11.  }

  12.  public String getStatus() {

  13.  return status;

  14.  }

  15.  public void setStatus(String status) {

  16.  this.status = status;

  17.  }

  18.  public String getDesc() {

  19.  return desc;

  20.  }

  21.  public void setDesc(String desc) {

  22.  this.desc = desc;

  23.  }

  24. }


服务接口及实现


定义了获取服务的两个接口方法


 
           
  1. public interface WeatherDataService {

  2.  /**

  3.  * 根据城市ID查询天气数据

  4.  * @param cityId

  5.  * @return

  6.  */

  7.  WeatherResponse getDataByCityId(String cityId);

  8.  /**

  9.  * 根据城市名称查询天气数据

  10.  * @param cityId

  11.  * @return

  12.  */

  13.  WeatherResponse getDataByCityName(String cityName);

  14. }


其实现为:

 
           
  1. @Service

  2. public class WeatherDataServiceImpl implements WeatherDataService {

  3.  @Autowired

  4.  private RestTemplate restTemplate;

  5.  private final String WEATHER_API = "http://wthrcdn.etouch.cn/weather_mini";

  6.  @Override

  7.  public WeatherResponse getDataByCityId(String cityId) {

  8.  String uri = WEATHER_API + "?citykey=" + cityId;

  9.  return this.doGetWeatherData(uri);

  10.  }

  11.  @Override

  12.  public WeatherResponse getDataByCityName(String cityName) {

  13.  String uri = WEATHER_API + "?city=" + cityName;

  14.  return this.doGetWeatherData(uri);

  15.  }

  16.  private WeatherResponse doGetWeatherData(String uri) {

  17.  ResponseEntity<String> response = restTemplate.getForEntity(uri, String.class);

  18.  String strBody = null;

  19.  if (response.getStatusCodeValue() == 200) {

  20.  strBody = response.getBody();

  21.  }

  22.  ObjectMapper mapper = new ObjectMapper();

  23.  WeatherResponse weather = null;

  24.  try {

  25.  weather = mapper.readValue(strBody, WeatherResponse.class);

  26.  } catch (IOException e) {

  27.  e.printStackTrace();

  28.  }

  29.  return weather;

  30.  }

  31. }

返回的天气信息采用了 Jackson 来进行反序列化成为 WeatherResponse 对象。


控制器层

控制器层暴露了RESTful API 地址。

 
           
  1. @RestController

  2. @RequestMapping("/weather")

  3. public class WeatherController {

  4.  @Autowired

  5.  private WeatherDataService weatherDataService;

  6.  @GetMapping("/cityId/{cityId}")

  7.  public WeatherResponse getReportByCityId(@PathVariable("cityId"String cityId) {

  8.  return weatherDataService.getDataByCityId(cityId);

  9.  }

  10.  @GetMapping("/cityName/{cityName}")

  11.  public WeatherResponse getReportByCityName(@PathVariable("cityName"String cityName) {

  12.  return weatherDataService.getDataByCityName(cityName);

  13.  }

  14. }

@RestController自动会将返回的数据,序列化成 JSON数据格式。


配置类

RestConfiguration 是 RestTemplate 的配置类。

 
           
  1. @Configuration

  2. public class RestConfiguration {

  3.  @Autowired

  4.  private RestTemplateBuilder builder;

  5.  @Bean

  6.  public RestTemplate restTemplate() {

  7.  return builder.build();

  8.  }

  9. }



访问API

运行项目之后,访问项目的 API :

  • http://localhost:8080/weather/cityId/101280601

  • http://localhost:8080/weather/cityName/惠州


能看到如下的数据返回


源码

本章节的源码,见 https://github.com/waylau/spring-cloud-tutorial/ samples目录下的 micro-weather-basic


推荐教程学习








    长按二维码
    即可关注我们

    慕课网

     程序员的梦工厂 

    更多技术干货

       等你来解锁




     
    慕课网 更多文章 图说架构系列 - 架构师的坎坷成长路
    猜您喜欢 中国移动+MBH,这次要搞个大事情!!! 一个程序猿的幸福历程 携程MTP和MCD平台 - 如何支撑一年10W+次无线集成和发布 沙龙报名| 2017的小目标,从《云上开发与运维最佳实践》开始 [译]Quora是如何进行答案排名的