# springboot-experiment03
**Repository Path**: classmate-he/springboot-experiment03
## Basic Information
- **Project Name**: springboot-experiment03
- **Description**: No description available
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2021-05-17
- **Last Updated**: 2021-08-31
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
### **实验三 全球新型冠状病毒实时数据统计应用程序的设计与实现**
## 一、实验目的
1、 掌握使用Spring框架自带的RestTemplate工具类爬取网络数据;
2、 掌握使用Spring框架自带的计划任务功能;
3、 掌握使用Apache Commons CSV组件解释CSV文件;
4、 掌握Java 8的Stream API处理集合类型数据;
5、 了解使用模板引擎或前端框架展示数据。
## 二、实验环境
1、 JDK 1.8或更高版本
2、 Maven 3.6+
3、 IntelliJ IDEA
4、 commons-csv 1.8+
## 三、实验任务
1、 通过IntelliJ IDEA的Spring Initializr向导创建Spring Boot项目。
2、 添加功能模块:spring MVC、lombok、commons-csv等。
创建项目时手动勾选这两个功能模块。没有commons-csv模块可选,所以在创建完成后手动添加
3、 爬取全球冠状病毒实时统计数据。(Java,Spring)
4、 使用Spring框架自带的RestTemplate工具类爬取数据。
**RestTemplate**采用同步方式执行 HTTP 请求的类,底层使用 JDK 原生 HttpURLConnection API ,或者 HttpComponents等其他 HTTP 客户端请求类库。
**RestTemplate** 工具类提供模板化的方法让开发者能更简单地发送 HTTP 请求。
**RestTemplate** 就是 Spring框架 封装的处理同步 HTTP 请求的工具类。
如果直接使用HTTP客户端请求Gitee上的文件,会报403的错误,但使用浏览器访问是正常的。经过分析,我们使用HTTP客户端请求Gitee上的文件时,需要设置一个请求头部User-Agent,否则会报403异常。
```java
public static List getDate() throws IOException {
//爬取CSV文件
String url = "https://gitee.com/dgut-sai/COVID-19/raw/master/csse_covid_19_data/csse_covid_19_time_series/time_series_covid19_confirmed_global.csv";
RequestEntity requestEntity = RequestEntity
.get(URI.create(url))
.headers(httpHeaders -> httpHeaders.add("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36 QIHU 360SE"))
.build();
ResponseEntity exchange = new RestTemplate().exchange(requestEntity, Resource.class);
Resource body = exchange.getBody();
InputStream inputStream = body.getInputStream();
Reader reader = new InputStreamReader(inputStream);
// 数据解析
Iterable records = CSVFormat.RFC4180.withFirstRecordAsHeader().parse(reader);
List list = new ArrayList<>();
for(CSVRecord csvRecord : records){
ChangeRecord.change(list,csvRecord);
}
System.out.println("--------成功获取CSV数据--------");
return list;
```
如上图所示,我们先实例化一个RequestEntity对象,再通过RestTemplate的exchange方法获取csv文件,这个文件的数据会封装到一个Resource对象中。我们可以通过Resource对象的getInputStream方法获取csv文件的输入流。
5、 分析csv文件的数据结构,定义model类。
```java
import lombok.Data;
@Data
public class RegionStates {
private String state;
private String country;
private int latestTotalCases;
private int diffFromPrevDay;
}
```
6、 使用Apache Commons CSV组件解释CSV文件。
```
public static void change(List list, CSVRecord record){
RegionStates regionStates = new RegionStates();
regionStates.setState(record.get("Province/State"));
regionStates.setCountry(record.get("Country/Region"));
regionStates.setLatestTotalCases(Integer.parseInt(record.get(record.size() - 1)));
regionStates.setDiffFromPrevDay(Integer.parseInt(record.get(record.size()-1))-Integer.parseInt(record.get(record.size()-2)));
System.out.println(regionStates);
list.add(regionStates);
}
```
7、 使用Spring框架自带的计划任务功能定时更新统计数据。
注意:在Spring Boot应用程序中,Spring框架自带的计划任务默认是在单线程中执行的,这是因为Spring Boot在初始化计划任务线程池的时候,默认的线程池大小是 1 ,也就是说只有一个线程来执行所有计划任务,那么当有一个计划任务要耗费很长时间执行时,其它的计划任务即使到了执行时间点也只能继续等待。
我们可以通过属性文件设置计划任务线程池的大小,实现并行执行计划任务,如下图所示:
8、 要确保应用程序启动时,获取一次统计数据。
重写InitializingBean的afterpropertiesSet方法,在这个方法里面做程序运行时需要的初始化数据
9、 单元测试。
```java
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = {GetCSVData.class})
class Test3ApplicationTests {
@Autowired
private GetCSVData getCSVData;
@Test
void testGetData() throws IOException {
List list = getCSVData.getDate();
for (RegionStates state : list)
{
System.out.println(state);
}
}
```
10、 定义Cotroller控制器。
```java
//Service
@Service
public class DataService {
@Autowired
GetCSVData getCSVData;
List list;
public List getAll() throws IOException{
return getCSVData.getData();
}
public List getByState(String state) throws IOException{
list = getCSVData.getData();
List filteredList = new ArrayList<>();
for (RegionStates item : list){
if(item.getState().equals(state)){
filteredList.add(item);
}
}
return filteredList;
}
public List getByCountry(String country) throws IOException{
list = getCSVData.getData();
List filteredList = new ArrayList<>();
for (RegionStates item : list){
if(item.getCountry().equals(country)){
filteredList.add(item);
}
}
return filteredList;
}
}
```
```java
//Controller
@Controller
public class DataController {
@Autowired
DataService service;
List list;
@GetMapping("/all")
@ResponseBody
public List getAll() throws IOException {
list = service.getAll();
return list;
}
@GetMapping("/searchByState/{state}")
@ResponseBody
public List searchByState(@PathVariable("state")String state)throws IOException{
list = service.getByState(state);
return list;
}
@GetMapping("/searchByCountry/{country}")
@ResponseBody
public List searchByCountry(@PathVariable("country")String country)throws IOException{
list = service.getByCountry(country);
return list;
}
}
```
11、 定义前端数据展示页面。
```javascript
```
