# 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 ```