这是学习的第三篇文章,由于天气信息需要更新,所以我们需要一个定时器定时去获取一下最新的信息。由于本项目实现比较简单就可以用quartz来实现。

quartz如何整合

数据需要定时地刷新,不能等到用户来获取的时候才更新,这里用最常用的quartz定时器来实现。

首先时引入依赖:

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>

下面定义一个执行的任务,先什么都不干,就打印一句话即可:

1
2
3
4
5
6
7
8
@Slf4j
public class WeatherDataSyncJob extends QuartzJobBean {
@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
log.info("天气数据同步任务开始");

}
}

下面要定义配置类:

首先是要定义这个任务的细节:JobDetail,就是说我们配置的这个quartz里面的任务是谁?给他起个名字啥的。

后面个是定义触发器,决定了刚才定义的这个JobDetail多长时间执行一次。这里是为了模拟想过,定义了两秒就执行一次。那么我们启动项目后,看到的效果应该是每两秒打印一次日志。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Configuration
public class QuartzConfig {
//定义一个jobDetail,就是注册一个定时任务,具体如何执行时在WeatherDataSyncJob中定义
//具体何时执行,是下面的Trigger定义
@Bean
public JobDetail weatherDataSyncDetail(){
return JobBuilder.newJob(WeatherDataSyncJob.class).
withIdentity("WeatherDataSyncJob").
storeDurably().build();
}
//触发器
@Bean
public Trigger weatherDataSyncTrigger(){
SimpleScheduleBuilder scheduleBuilder = SimpleScheduleBuilder
.simpleSchedule()
.withIntervalInSeconds(2)//两秒去自动执行一次
.repeatForever();
return TriggerBuilder.newTrigger().forJob(weatherDataSyncDetail())
.withIdentity("weatherDataSyncTrigger")
.withSchedule(scheduleBuilder).build();
}

}

拉取城市信息

网站: http://mobile.weather.com.cn/js/citylist.xml

比如我将江苏省的单独拿出来:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
<xml>
<c c1="0">
<d d1="101190101" d2="南京" d3="nanjing" d4="江苏"/>
<d d1="101190102" d2="溧水" d3="lishui" d4="江苏"/>
<d d1="101190103" d2="高淳" d3="gaochun" d4="江苏"/>
<d d1="101190104" d2="江宁" d3="jiangning" d4="江苏"/>
<d d1="101190105" d2="六合" d3="luhe" d4="江苏"/>
<d d1="101190106" d2="江浦" d3="jiangpu" d4="江苏"/>
<d d1="101190107" d2="浦口" d3="pukou" d4="江苏"/>
<d d1="101190201" d2="无锡" d3="wuxi" d4="江苏"/>
<d d1="101190202" d2="江阴" d3="jiangyin" d4="江苏"/>
<d d1="101190203" d2="宜兴" d3="yixing" d4="江苏"/>
<d d1="101190204" d2="锡山" d3="xishan" d4="江苏"/>
<d d1="101190301" d2="镇江" d3="zhenjiang" d4="江苏"/>
<d d1="101190302" d2="丹阳" d3="danyang" d4="江苏"/>
<d d1="101190303" d2="扬中" d3="yangzhong" d4="江苏"/>
<d d1="101190304" d2="句容" d3="jurong" d4="江苏"/>
<d d1="101190305" d2="丹徒" d3="dantu" d4="江苏"/>
<d d1="101190401" d2="苏州" d3="suzhou" d4="江苏"/>
<d d1="101190402" d2="常熟" d3="changshu" d4="江苏"/>
<d d1="101190403" d2="张家港" d3="zhangjiagang" d4="江苏"/>
<d d1="101190404" d2="昆山" d3="kunshan" d4="江苏"/>
<d d1="101190405" d2="吴中" d3="wuzhong" d4="江苏"/>
<d d1="101190407" d2="吴江" d3="wujiang" d4="江苏"/>
<d d1="101190408" d2="太仓" d3="taicang" d4="江苏"/>
<d d1="101190501" d2="南通" d3="nantong" d4="江苏"/>
<d d1="101190502" d2="海安" d3="haian" d4="江苏"/>
<d d1="101190503" d2="如皋" d3="rugao" d4="江苏"/>
<d d1="101190504" d2="如东" d3="rudong" d4="江苏"/>
<d d1="101190507" d2="启东" d3="qidong" d4="江苏"/>
<d d1="101190508" d2="海门" d3="haimen" d4="江苏"/>
<d d1="101190509" d2="通州" d3="tongzhou" d4="江苏"/>
<d d1="101190601" d2="扬州" d3="yangzhou" d4="江苏"/>
<d d1="101190602" d2="宝应" d3="baoying" d4="江苏"/>
<d d1="101190603" d2="仪征" d3="yizheng" d4="江苏"/>
<d d1="101190604" d2="高邮" d3="gaoyou" d4="江苏"/>
<d d1="101190605" d2="江都" d3="jiangdu" d4="江苏"/>
<d d1="101190606" d2="邗江" d3="hanjiang" d4="江苏"/>
<d d1="101190701" d2="盐城" d3="yancheng" d4="江苏"/>
<d d1="101190702" d2="响水" d3="xiangshui" d4="江苏"/>
<d d1="101190703" d2="滨海" d3="binhai" d4="江苏"/>
<d d1="101190704" d2="阜宁" d3="funing" d4="江苏"/>
<d d1="101190705" d2="射阳" d3="sheyang" d4="江苏"/>
<d d1="101190706" d2="建湖" d3="jianhu" d4="江苏"/>
<d d1="101190707" d2="东台" d3="dongtai" d4="江苏"/>
<d d1="101190708" d2="大丰" d3="dafeng" d4="江苏"/>
<d d1="101190709" d2="盐都" d3="yandu" d4="江苏"/>
<d d1="101190801" d2="徐州" d3="xuzhou" d4="江苏"/>
<d d1="101190802" d2="铜山" d3="tongshan" d4="江苏"/>
<d d1="101190803" d2="丰县" d3="fengxian" d4="江苏"/>
<d d1="101190804" d2="沛县" d3="peixian" d4="江苏"/>
<d d1="101190805" d2="邳州" d3="pizhou" d4="江苏"/>
<d d1="101190806" d2="睢宁" d3="suining" d4="江苏"/>
<d d1="101190807" d2="新沂" d3="xinyi" d4="江苏"/>
<d d1="101190901" d2="淮安" d3="huaian" d4="江苏"/>
<d d1="101190902" d2="金湖" d3="jinhu" d4="江苏"/>
<d d1="101190903" d2="盱眙" d3="xuyi" d4="江苏"/>
<d d1="101190904" d2="洪泽" d3="hongze" d4="江苏"/>
<d d1="101190905" d2="涟水" d3="lianshui" d4="江苏"/>
<d d1="101190906" d2="淮阴区" d3="huaiyinqu" d4="江苏"/>
<d d1="101190908" d2="淮安区" d3="huaianqu" d4="江苏"/>
<d d1="101191001" d2="连云港" d3="lianyungang" d4="江苏"/>
<d d1="101191002" d2="东海" d3="donghai" d4="江苏"/>
<d d1="101191003" d2="赣榆" d3="ganyu" d4="江苏"/>
<d d1="101191004" d2="灌云" d3="guanyun" d4="江苏"/>
<d d1="101191005" d2="灌南" d3="guannan" d4="江苏"/>
<d d1="101191101" d2="常州" d3="changzhou" d4="江苏"/>
<d d1="101191102" d2="溧阳" d3="liyang" d4="江苏"/>
<d d1="101191103" d2="金坛" d3="jintan" d4="江苏"/>
<d d1="101191104" d2="武进" d3="wujin" d4="江苏"/>
<d d1="101191201" d2="泰州" d3="taizhou" d4="江苏"/>
<d d1="101191202" d2="兴化" d3="xinghua" d4="江苏"/>
<d d1="101191203" d2="泰兴" d3="taixing" d4="江苏"/>
<d d1="101191204" d2="姜堰" d3="jiangyan" d4="江苏"/>
<d d1="101191205" d2="靖江" d3="jingjiang" d4="江苏"/>
<d d1="101191301" d2="宿迁" d3="suqian" d4="江苏"/>
<d d1="101191302" d2="沭阳" d3="shuyang" d4="江苏"/>
<d d1="101191303" d2="泗阳" d3="siyang" d4="江苏"/>
<d d1="101191304" d2="泗洪" d3="sihong" d4="江苏"/>
<d d1="101191305" d2="宿豫" d3="suyu" d4="江苏"/>
</c>
</xml>

其实思路很简单,就是从xml文件中获取所有的城市信息,转换为城市列表对象。然后遍历城市中的id,就可以根据id拼接url去直接去调用天气的接口去查询天气,然后重新覆盖redis中的天气数据即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@Slf4j
public class WeatherDataSyncJob extends QuartzJobBean {
@Autowired
private IWeatherDataService weatherDataService;
@Autowired
private ICityDataService cityDataService;

@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
log.info("天气数据同步任务开始");
//获取城市列表
List<City> cityList = null;
try {
cityList = cityDataService.listCity();
} catch (Exception e) {
log.error("获取城市列表失败!",e);
}

//遍历城市id获取天气
for(City city:cityList){
String cityId = city.getCityId();
log.info("定时器更新了{}这个城市的天气信息", city.getCityName());
weatherDataService.syncDataByCityId(cityId);
}

log.info("天气数据同步任务结束");
}
}

具体如何读取xml文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
@Service
@Slf4j
public class CityDataServiceImpl implements ICityDataService {
@Override
public List<City> listCity() throws Exception {
//读取xml文件
Resource resource = new ClassPathResource("citylist.xml");
//读取文件的buffer流
BufferedReader bf = new BufferedReader(new InputStreamReader(resource.getInputStream(),"UTF-8"));
StringBuffer buffer = new StringBuffer();
String line = "";

while((line = bf.readLine()) != null){
buffer.append(line);
}
//此时数据已经读到buffer里了
bf.close();

//xml转换为java对象
CityList cityList = (CityList) XmlBuilder.xmlStrToObj(CityList.class,buffer.toString());

return cityList.getCityList();
}
}


/**
* @Author 【swg】.
* @Date 2018/11/19 17:09
* @DESC xml转换为对象
* @CONTACT 317758022@qq.com
*/
public class XmlBuilder {
public static Object xmlStrToObj(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;
}
}

其中,解析xml我们用到了JAXB,他是什么呢?维基百科:

JAXB(Java Architecture for XML Binding简称JAXB)允许Java开发人员将Java类映射为XML表示方式。JAXB提供两种主要特性:将一个Java对象序列化为XML,以及反向操作,将XML解析成Java对象。换句话说,JAXB允许以XML格式存储和读取数据,而不需要程序的类结构实现特定的读取XML和保存XML的代码。