# springboot_experiment3 **Repository Path**: chentian123/springboot_experiment3 ## Basic Information - **Project Name**: springboot_experiment3 - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2020-05-11 - **Last Updated**: 2020-12-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 东莞理工学院网络空间安全学院实验报告 院(系)名称:网络空间安全学院 专业班级: 17软卓2班 学号: 201741404247 姓名: 陈钿 实验题目: 实验三 全球新型冠状病毒实时数据统计应用程序的设计与实现 实验日期:2020.5.10 实验(上机)学时: 4 成绩: ------ # 1.实验目的 1. 掌握使用Spring框架自带的RestTemplate工具类爬取网络数据; 2. 掌握使用Spring框架自带的计划任务功能; 3. 掌握使用Apache Commons CSV组件解释CSV文件; 4. 掌握Java 8的Stream API处理集合类型数据; 5. 了解使用模板引擎或前端框架展示数据。 ------ # 2.实验环境 1. JDK 1.8或更高版本 2. Maven 3.6+ 3. IntelliJ IDEA 4. commons-csv 1.8+ *** # 3.实验任务 ### 1. 通过IntelliJ IDEA的Spring Initializr向导创建Spring Boot项目。 ### 2. 添加功能模块:spring MVC、lombok、commons-csv等。 - 截图如下: - pom.xml文件代码如下: ```xml org.apache.commons commons-csv 1.8 ``` ### 3. 使用Spring框架自带的RestTemplate工具类爬取数据。 - 代码如下: ``` java RequestEntity requestEntity = RequestEntity .get(URI.create(URL)) .headers(httpsHeaders -> httpsHeaders.add("User-Agent", "陈钿")) .build(); ResponseEntity exchange = new RestTemplate().exchange(requestEntity, Resource.class); Resource body = exchange.getBody(); ``` 说明:先实例化一个RequestEntity对象,再通过RestTemplate的exchange方法获取csv文件,这个文件的数据会封装到一个Resource对象中,即"body"。 *** ### 4.分析csv文件的数据结构,定义model类 - 自定义的model类: ```java @Data @Builder public class RegionStats { private String state; // 州/省 private String country; // 国家 private int latestTotalCases; // 最新确诊数 private int diffFromPrevDay; // 新增确诊数 } ``` *** ### 5. 使用Apache Commons CSV组件解释CSV文件 - 判断前面爬取的Resource对象"body"是否有数据; - 通过该Resource对象的getInputStream方法获取csv文件的输入流; - 利用for循环流中的数据封装成自定义的model对象,并将对象写入到List列表中。 - 代码如下: ```java if (body != null) { InputStreamReader inputStreamReader = new InputStreamReader(body.getInputStream(), "UTF-8"); CSVParser parser = new CSVParser(inputStreamReader, CSVFormat.EXCEL.withHeader()); regionStatsList.clear(); for (CSVRecord record : parser) { regionStatsList.add(RegionStats.builder() .state(record.get("Province/State")) .country(record.get("Country/Region")) .latestTotalCases(Integer.parseInt(record.get(record.size() - 1))) .diffFromPrevDay(Integer.parseInt(record.get(record.size() - 1)) - Integer.parseInt(record.get(record.size() - 2))) .build()); } } ``` 说明:方法 record.get("...")用于获取指定列的数据。 *** ### 6. 使用Spring框架自带的计划任务功能定时更新统计数据并确保应用程序启动时,获取一次统计数据。 - 给爬数据的方法加上@Scheduled注解(每天凌晨1点更新数据)和@PostConstruct注解(启动项目时获取数据) ```java @Scheduled(cron = "${chen.Schedules.updateVirusDataCron}") @PostConstruct public void fetchCoronaVirusData() throws IOException { ``` 说明:@Scheduled的参数在配置文件中(chen.Schedules.updateVirusDataCron=0 0 1 * * *) @PostConstruct该注解被用来修饰一个非静态的void()方法。被@PostConstruct修饰的方法会在服务器加载Servlet的时候运行,并且只会被服务器执行一次。PostConstruct在构造函数之后执行,init()方法之前执行。 ### 7. 单元测试 - 自动装配Service组件,因有@PostConstruct注解,项目启动后对象就被初始化,打印数据看是否成功获取到数据。 - 单元测试代码如下: ```java @SpringBootTest class SpringbootExperiment3ApplicationTests { @Autowired GetDataService getDataService; @Test void contextLoads(){ System.out.println(getDataService.regionStatsList); } ``` - 测试结果如图: *** ### 8. 定义Cotroller控制器。 - 控制器功能:统计全球总确诊人数、当日新增确诊人数;若未带搜索参数(国家名),则返回全部数据,有带搜索参数(国家名)则返回搜索结果; ```java @Controller public class GetDataController { @Autowired GetDataService getDataService; @GetMapping("/") public String index(Model model,String country){ Integer latestTotalCases; Integer diffFromPrevDay; Date nowTime = new Date(); List totalList = getDataService.regionStatsList; if(country!=null){ List countList = totalList.stream(). filter(regionStats -> regionStats.getCountry().equals(country)) .collect(Collectors.toList()); model.addAttribute("totalList",countList); } else { model.addAttribute("totalList",totalList); } latestTotalCases = totalList.stream().mapToInt(RegionStats::getLatestTotalCases).sum(); diffFromPrevDay = totalList.stream().mapToInt(RegionStats::getDiffFromPrevDay).sum(); model.addAttribute("latestTotalCases",latestTotalCases); model.addAttribute("diffFromPrevDay",diffFromPrevDay); model.addAttribute("nowTime",nowTime); System.out.println(country); return "index"; } } ``` 说明:搜索功能通过Stream API实现,利用.filter()筛选出数据,并用.collect(Collectors.toList())封装到List列表。 ### 9. 定义前端数据展示页面 - 页面通过thymeleaf模板引擎实现数据展示 - 搜索结果: *** # 4. 实验总结 - 完成该实验让我学到了很多东西,第一次接触java爬虫,没有想象中的难。老师的实验也比较潮流,实用性高,老师很棒,愿意花更多时间跟着老师学习。