版权声明:此博客为个人博客,不涉及商业用途,仅提供学习参考,内容均来自个人原创以及互联网转载和摘录。 --------------------- 本文来自 路西法Lucifer 的CSDN 博客 ,全文地址请点击: https://blog.csdn.net/qq_37495786/article/details/82832414
ps:
这是对SpringBoot 调用天气预报api接口获取天气数据(1)的代码改造,加入了redis,Quartz,thymeleaf,bootstrap技术。
建议先浏览SpringBoot 调用天气预报api接口获取天气数据(1)
项目结构:
实体类city:
package com.lucifer.demo.pojo;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
/**
* @author: Lucifer
* @create: 2018-09-24 18:38
* @description:城市
**/
@XmlRootElement(name = "d")
@XmlAccessorType(XmlAccessType.FIELD)
public class City {
@XmlAttribute(name = "d1")
private String cityId;
@XmlAttribute(name = "d2")
private String cityName;
@XmlAttribute(name = "d3")
private String cityCode;
@XmlAttribute(name = "d4")
private String province;
public String getCityId() {
return cityId;
}
public void setCityId(String cityId) {
this.cityId = cityId;
}
public String getCityName() {
return cityName;
}
public void setCityName(String cityName) {
this.cityName = cityName;
}
public String getCityCode() {
return cityCode;
}
public void setCityCode(String cityCode) {
this.cityCode = cityCode;
}
public String getProvince() {
return province;
}
public void setProvince(String province) {
this.province = province;
}
}
CityList:
package com.lucifer.demo.pojo;
import javax.xml.bind.annotation.*;
import java.util.List;
/**
* @author: Lucifer
* @create: 2018-09-24 18:42
* @description: 城市集合
**/
@XmlRootElement(name = "c")
@XmlAccessorType(XmlAccessType.FIELD)
public class CityList {
@XmlElement(name = "d")
private List<City> cityList;
public List<City> getCityList() {
return cityList;
}
public void setCityList(List<City> cityList) {
this.cityList = cityList;
}
}
WeatherResponse:响应数据
package com.lucifer.demo.pojo;
import java.io.Serializable;
/**
* @author: Lucifer
* @create: 2018-09-23 23:18
* @description:响应数据
**/
public class WeatherResponse implements Serializable {
private Weather data;
private Integer status;
private String desc;
public Weather getData() {
return data;
}
public void setData(Weather data) {
this.data = data;
}
public Integer getStatus() {
return status;
}
public void setStatus(Integer status) {
this.status = status;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
}
QuartzConfiguration:定时器配置类
package com.lucifer.demo.config;
import com.lucifer.demo.job.WeatherDataSyncJob;
import org.quartz.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author: Lucifer
* @create: 2018-09-24 18:11
* @description:
**/
@Configuration
public class QuartzConfiguration {
private static final int TIME =1800 ;
@Bean
public JobDetail weathDataSyncJobJobDetail(){
return JobBuilder.newJob(WeatherDataSyncJob.class).withIdentity("weatherDataSyncJob").storeDurably().build();
}
@Bean
public Trigger weathDataSyncTrigger(){
SimpleScheduleBuilder scheduleBuilder=SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(TIME).repeatForever();
return TriggerBuilder.newTrigger().forJob(weathDataSyncJobJobDetail()).withIdentity("weathDataSyncTrigger").withSchedule(scheduleBuilder).build();
}
}
WeatherReportController:
package com.lucifer.demo.Controller;
import com.lucifer.demo.Service.CityDataService;
import com.lucifer.demo.Service.WeatherReportService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;
/**
* @author: Lucifer
* @create: 2018-09-24 20:06
* @description:
**/
@RestController
@RequestMapping(value = "/report")
public class WeatherReportController {
@Autowired
private CityDataService cityDataService;
@Autowired
private WeatherReportService weatherReportService;
@GetMapping(value = "/cityId/{cityId}")
public ModelAndView getReportByCityId(@PathVariable("cityId") String cityId, Model model) throws Exception{
model.addAttribute("title","Lucifer的天气预报");
model.addAttribute("cityId",cityId);
model.addAttribute("cityList",cityDataService.listCity());
model.addAttribute("report",weatherReportService.getDataByCityId(cityId));
return new ModelAndView("/weather/report","reportModel",model);
}
}
WeatherDataSyncJob:
package com.lucifer.demo.job;
import com.lucifer.demo.Service.CityDataService;
import com.lucifer.demo.Service.WeatherDataService;
import com.lucifer.demo.pojo.City;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.QuartzJobBean;
import java.util.List;
/**
* @author: Lucifer
* @create: 2018-09-24 18:08
* @description:
**/
public class WeatherDataSyncJob extends QuartzJobBean {
private final static Logger logger = LoggerFactory.getLogger(WeatherDataSyncJob.class);
@Autowired
private CityDataService cityDataService;
@Autowired
private WeatherDataService weatherDataService;
@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
logger.info("Weather Data Sync Job,Start!");
List<City> cityList = null;
try {
cityList = cityDataService.listCity();
} catch (Exception e) {
e.printStackTrace();
}
for (City city : cityList) {
String cityId=city.getCityId();
logger.info("weather Data Sync Job,cityId:"+cityId);
weatherDataService.syncDataByCityId(cityId);
}
logger.info("Weather Data Sync Job,End!");
}
}
CityDataService:
package com.lucifer.demo.Service;
import com.lucifer.demo.pojo.City;
import java.util.List;
/**
* @author: Lucifer
* @create: 2018-09-24 18:51
* @description:
**/
public interface CityDataService {
/**
* 获取City
* @return
* @throws Exception
*/
List<City> listCity() throws Exception;
}
CityDataServiceImpl:
package com.lucifer.demo.Service;
import com.lucifer.demo.pojo.City;
import com.lucifer.demo.pojo.CityList;
import com.lucifer.demo.uitl.XmlBuilder;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Service;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.List;
/**
* @author: Lucifer
* @create: 2018-09-24 18:52
* @description:
**/
@Service
public class CityDataServiceImpl implements CityDataService {
@Override
public List<City> listCity() throws Exception {
//读取XML文件
Resource resource = new ClassPathResource("citylist.xml");
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(resource.getInputStream(), "utf-8"));
StringBuffer stringBuffer = new StringBuffer();
String line = "";
while ((line = bufferedReader.readLine()) != null) {
stringBuffer.append(line);
}
bufferedReader.close();
//XML转为Java对象
CityList cityList = (CityList) XmlBuilder.xmlStrToObject(CityList.class, stringBuffer.toString());
return cityList.getCityList();
}
}
WeatherDataService:
package com.lucifer.demo.Service;
import com.lucifer.demo.pojo.WeatherResponse;
/**
* 天气数据接口
*/
public interface WeatherDataService {
/**
* 根据城市ID查询天气数据
* @param CityId
* @return
*/
WeatherResponse getDataByCityId(String CityId);
/**
* 根据城市名称查询天气数据
* @param cityName
* @return
*/
WeatherResponse getDataByCityName(String cityName);
/**
* 根据城市id同步数据
*/
void syncDataByCityId(String cityId);
}
WeatherDataServiceImpl:
package com.lucifer.demo.Service;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.lucifer.demo.pojo.WeatherResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import java.util.concurrent.TimeUnit;
/**
* @author: Lucifer
* @create: 2018-09-23 23:24
* @description: 实现类
**/
@Service
public class WeatherDataServiceImpl implements WeatherDataService {
protected static final Logger logger = LoggerFactory.getLogger(WeatherDataService.class);
private static final String WEATHER_URI = "http://wthrcdn.etouch.cn/weather_mini?";
@Autowired
private RestTemplate restTemplate;
/**
* StringRedisTemplate 实际是对redis进行了封装
*/
@Autowired
private StringRedisTemplate stringRedisTemplate;
private static final long TIME_OUT = 10L;
@Override
public WeatherResponse getDataByCityId(String cityId) {
String uri = WEATHER_URI + "citykey=" + cityId;
return this.getWeatherResponse(uri);
}
@Override
public WeatherResponse getDataByCityName(String cityName) {
String uri = WEATHER_URI + "city=" + cityName;
return this.getWeatherResponse(uri);
}
private WeatherResponse getWeatherResponse(String uri) {
String key = uri;
String strBody = null;
WeatherResponse resp = null;
ObjectMapper objectMapper = new ObjectMapper();
ValueOperations<String, String> ops = stringRedisTemplate.opsForValue();
//先查缓存,如果有缓存有的,取缓存数据
if (stringRedisTemplate.hasKey(key)) {
logger.info("Redis has data");
strBody = ops.get(key);
} else {
logger.info("Redis don't has data");
//缓存没有,再调服务接口获取
ResponseEntity<String> respString = restTemplate.getForEntity(uri, String.class);
if (respString.getStatusCodeValue() == 200) {
strBody = respString.getBody();
}
//数据写入缓存
ops.set(key, strBody, TIME_OUT, TimeUnit.SECONDS);
}
try {
resp = objectMapper.readValue(strBody, WeatherResponse.class);
} catch (Exception e) {
e.printStackTrace();
}
return resp;
}
@Override
public void syncDataByCityId(String cityId) {
String uri=WEATHER_URI + "citykey=" + cityId;;
this.saveWeatherData(uri);
}
/**
* 把天气数据放在缓存当中
*
* @param uri
*/
private void saveWeatherData(String uri) {
String key = uri;
String strBody = null;
ValueOperations<String, String> ops = stringRedisTemplate.opsForValue();
ResponseEntity<String> respString = restTemplate.getForEntity(uri, String.class);
if (respString.getStatusCodeValue() == 200) {
strBody = respString.getBody();
}
ops.set(key, strBody, TIME_OUT, TimeUnit.SECONDS);
}
}
WeatherReportService:
package com.lucifer.demo.Service;
import com.lucifer.demo.pojo.Weather;
public interface WeatherReportService {
Weather getDataByCityId(String cityId);
}
WeatherReportServiceImpl:
package com.lucifer.demo.Service;
import com.lucifer.demo.pojo.Weather;
import com.lucifer.demo.pojo.WeatherResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @author: Lucifer
* @create: 2018-09-24 20:03
* @description:
**/
@Service
public class WeatherReportServiceImpl implements WeatherReportService {
@Autowired
private WeatherDataService weatherDataService;
@Override
public Weather getDataByCityId(String cityId) {
WeatherResponse response = weatherDataService.getDataByCityId(cityId);
return response.getData();
}
}
XmlBuilder:工具类
package com.lucifer.demo.uitl;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;
import java.io.Reader;
import java.io.StringReader;
/**
* @author: Lucifer
* @create: 2018-09-24 18:45
* @description: XML构建工具
**/
public class XmlBuilder {
/**
* 将XML转为指定的POJO
*
* @param clazz
* @param xmlStr
* @return
*/
public static Object xmlStrToObject(Class<?> clazz, String xmlStr) throws Exception {
Object xmlObject = null;
Reader reader = null;
JAXBContext context = JAXBContext.newInstance(clazz);
//XML转为对象的接口
Unmarshaller unmarshaller = context.createUnmarshaller();
reader = new StringReader(xmlStr);
xmlObject = unmarshaller.unmarshal(reader);
if (null != reader) {
reader.close();
}
return xmlObject;
}
}
citylist.xml:此处只列举了湖北的,当然可以添加全国城市的,但是数据太多,获取的会很慢,测试的话,少量城市即可。
附城市地址:http://mobile.weather.com.cn/js/citylist.xml
<?xml version="1.0" encoding="utf-8"?>
<c c1="0">
<d d1="101200101" d2="武汉" d3="wuhan" d4="湖北"/>
<d d1="101200102" d2="蔡甸" d3="caidian" d4="湖北"/>
<d d1="101200103" d2="黄陂" d3="huangpi" d4="湖北"/>
<d d1="101200104" d2="新洲" d3="xinzhou" d4="湖北"/>
<d d1="101200105" d2="江夏" d3="jiangxia" d4="湖北"/>
<d d1="101200106" d2="东西湖" d3="dongxihu" d4="湖北"/>
<d d1="101200201" d2="襄阳" d3="xiangyang" d4="湖北"/>
<d d1="101200202" d2="襄州" d3="xiangzhou" d4="湖北"/>
<d d1="101200203" d2="保康" d3="baokang" d4="湖北"/>
<d d1="101200204" d2="南漳" d3="nanzhang" d4="湖北"/>
<d d1="101200205" d2="宜城" d3="yicheng" d4="湖北"/>
<d d1="101200206" d2="老河口" d3="laohekou" d4="湖北"/>
<d d1="101200207" d2="谷城" d3="gucheng" d4="湖北"/>
<d d1="101200208" d2="枣阳" d3="zaoyang" d4="湖北"/>
<d d1="101200301" d2="鄂州" d3="ezhou" d4="湖北"/>
<d d1="101200302" d2="梁子湖" d3="liangzihu" d4="湖北"/>
<d d1="101200401" d2="孝感" d3="xiaogan" d4="湖北"/>
<d d1="101200402" d2="安陆" d3="anlu" d4="湖北"/>
<d d1="101200403" d2="云梦" d3="yunmeng" d4="湖北"/>
<d d1="101200404" d2="大悟" d3="dawu" d4="湖北"/>
<d d1="101200405" d2="应城" d3="yingcheng" d4="湖北"/>
<d d1="101200406" d2="汉川" d3="hanchuan" d4="湖北"/>
<d d1="101200407" d2="孝昌" d3="xiaochang" d4="湖北"/>
<d d1="101200501" d2="黄冈" d3="huanggang" d4="湖北"/>
<d d1="101200502" d2="红安" d3="hongan" d4="湖北"/>
<d d1="101200503" d2="麻城" d3="macheng" d4="湖北"/>
<d d1="101200504" d2="罗田" d3="luotian" d4="湖北"/>
<d d1="101200505" d2="英山" d3="yingshan" d4="湖北"/>
<d d1="101200506" d2="浠水" d3="xishui" d4="湖北"/>
<d d1="101200507" d2="蕲春" d3="qichun" d4="湖北"/>
<d d1="101200508" d2="黄梅" d3="huangmei" d4="湖北"/>
<d d1="101200509" d2="武穴" d3="wuxue" d4="湖北"/>
<d d1="101200510" d2="团风" d3="tuanfeng" d4="湖北"/>
<d d1="101200601" d2="黄石" d3="huangshi" d4="湖北"/>
<d d1="101200602" d2="大冶" d3="daye" d4="湖北"/>
<d d1="101200603" d2="阳新" d3="yangxin" d4="湖北"/>
<d d1="101200604" d2="铁山" d3="tieshan" d4="湖北"/>
<d d1="101200605" d2="下陆" d3="xialu" d4="湖北"/>
<d d1="101200606" d2="西塞山" d3="xisaishan" d4="湖北"/>
<d d1="101200701" d2="咸宁" d3="xianning" d4="湖北"/>
<d d1="101200702" d2="赤壁" d3="chibi" d4="湖北"/>
<d d1="101200703" d2="嘉鱼" d3="jiayu" d4="湖北"/>
<d d1="101200704" d2="崇阳" d3="chongyang" d4="湖北"/>
<d d1="101200705" d2="通城" d3="tongcheng" d4="湖北"/>
<d d1="101200706" d2="通山" d3="tongshan" d4="湖北"/>
<d d1="101200801" d2="荆州" d3="jingzhou" d4="湖北"/>
<d d1="101200802" d2="江陵" d3="jiangling" d4="湖北"/>
<d d1="101200803" d2="公安" d3="gongan" d4="湖北"/>
<d d1="101200804" d2="石首" d3="shishou" d4="湖北"/>
<d d1="101200805" d2="监利" d3="jianli" d4="湖北"/>
<d d1="101200806" d2="洪湖" d3="honghu" d4="湖北"/>
<d d1="101200807" d2="松滋" d3="songzi" d4="湖北"/>
<d d1="101200901" d2="宜昌" d3="yichang" d4="湖北"/>
<d d1="101200902" d2="远安" d3="yuanan" d4="湖北"/>
<d d1="101200903" d2="秭归" d3="zigui" d4="湖北"/>
<d d1="101200904" d2="兴山" d3="xingshan" d4="湖北"/>
<d d1="101200906" d2="五峰" d3="wufeng" d4="湖北"/>
<d d1="101200907" d2="当阳" d3="dangyang" d4="湖北"/>
<d d1="101200908" d2="长阳" d3="changyang" d4="湖北"/>
<d d1="101200909" d2="宜都" d3="yidu" d4="湖北"/>
<d d1="101200910" d2="枝江" d3="zhijiang" d4="湖北"/>
<d d1="101200911" d2="三峡" d3="sanxia" d4="湖北"/>
<d d1="101200912" d2="夷陵" d3="yiling" d4="湖北"/>
<d d1="101201001" d2="恩施" d3="enshi" d4="湖北"/>
<d d1="101201002" d2="利川" d3="lichuan" d4="湖北"/>
<d d1="101201003" d2="建始" d3="jianshi" d4="湖北"/>
<d d1="101201004" d2="咸丰" d3="xianfeng" d4="湖北"/>
<d d1="101201005" d2="宣恩" d3="xuanen" d4="湖北"/>
<d d1="101201006" d2="鹤峰" d3="hefeng" d4="湖北"/>
<d d1="101201007" d2="来凤" d3="laifeng" d4="湖北"/>
<d d1="101201008" d2="巴东" d3="badong" d4="湖北"/>
<d d1="101201101" d2="十堰" d3="shiyan" d4="湖北"/>
<d d1="101201102" d2="竹溪" d3="zhuxi" d4="湖北"/>
<d d1="101201103" d2="郧西" d3="yunxi" d4="湖北"/>
<d d1="101201104" d2="郧县" d3="yunxian" d4="湖北"/>
<d d1="101201105" d2="竹山" d3="zhushan" d4="湖北"/>
<d d1="101201106" d2="房县" d3="fangxian" d4="湖北"/>
<d d1="101201107" d2="丹江口" d3="danjiangkou" d4="湖北"/>
<d d1="101201108" d2="茅箭" d3="maojian" d4="湖北"/>
<d d1="101201109" d2="张湾" d3="zhangwan" d4="湖北"/>
<d d1="101201201" d2="神农架" d3="shennongjia" d4="湖北"/>
<d d1="101201301" d2="随州" d3="suizhou" d4="湖北"/>
<d d1="101201302" d2="广水" d3="guangshui" d4="湖北"/>
<d d1="101201401" d2="荆门" d3="jingmen" d4="湖北"/>
<d d1="101201402" d2="钟祥" d3="zhongxiang" d4="湖北"/>
<d d1="101201403" d2="京山" d3="jingshan" d4="湖北"/>
<d d1="101201404" d2="掇刀" d3="duodao" d4="湖北"/>
<d d1="101201405" d2="沙洋" d3="shayang" d4="湖北"/>
<d d1="101201406" d2="沙市" d3="shashi" d4="湖北"/>
<d d1="101201501" d2="天门" d3="tianmen" d4="湖北"/>
<d d1="101201601" d2="仙桃" d3="xiantao" d4="湖北"/>
<d d1="101201701" d2="潜江" d3="qianjiang" d4="湖北"/>
</c>
report.html: 前端展示页面
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css"
integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
<title>Lucifer的天气预报</title>
</head>
<body>
<div class="container">
<div class="row">
<h3 th:text="${reportModel.title}"></h3>
<select clas="custom-select" id="selectCityId">
<option th:each="city : ${reportModel.cityList}"
th:value="${city.cityId}"
th:text="${city.cityName}"
th:selected="${city.cityId eq reportModel.cityId}">
</option>
</select>
</div>
<div class="row">
<h1 class="text-success" th:text="${reportModel.report.city}">深圳</h1>
</div>
<div class="row">
<p>
当前温度:<span th:text="${reportModel.report.wendu}"></span>
</p>
</div>
<div class="row">
<p>
温馨提示:<span th:text="${reportModel.report.ganmao}"></span>
</p>
</div>
<div class="row">
<div class="card border-info" th:each="foreach : ${reportModel.report.forecast}">
<div clas="card-body text-info">
<p clas="card-text" th:text="${foreach.date}">日期</p>
<p clas="card-text" th:text="${foreach.type}">天气类型</p>
<p clas="card-text" th:text="${foreach.high}" 最高温度></p>
<p clas="card-text" th:text="${foreach.low}">最低温度</p>
<p clas="card-text" th:text="${foreach.fengxiang}">风向</p>
</div>
</div>
</div>
</div>
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"
integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo"
crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js"
integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49"
crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js"
integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy"
crossorigin="anonymous"></script>
<script th:src="@{/js/report.js}" type="text/javascript"></script>
</body>
</html>
report.js:
$(function(){
$("#selectCityId").change(function(){
var cityId=$("#selectCityId").val();
var url='/report/cityId/'+cityId;
window.location.href=url;
})
});
gradle:导入以下包
dependencies {
compile('org.springframework.boot:spring-boot-starter-web')
compile group: 'org.apache.httpcomponents', name: 'httpclient', version: '4.5.6'
compile group: 'org.springframework.boot', name: 'spring-boot-starter-data-redis', version: '2.0.5.RELEASE'
compile group: 'org.springframework.boot', name: 'spring-boot-starter-quartz', version: '2.0.5.RELEASE'
compile group: 'org.springframework.boot', name: 'spring-boot-starter-thymeleaf', version: '2.0.5.RELEASE'
testCompile('org.springframework.boot:spring-boot-starter-test')
}
浏览器输入请求地址如:http://localhost:8080/report/cityId/101200101,
控制台如图:
浏览器如图: