Spring Boot 数据可视化与图表集成

Spring Boot 数据可视化与图表集成

Spring Boot 数据可视化与图表集成

在这里插入图片描述
27.1 学习目标与重点提示

学习目标:掌握Spring Boot数据可视化与图表集成的核心概念与使用方法,包括数据可视化的定义与特点、图表工具的定义与特点、Spring Boot与图表工具的集成、Spring Boot的实际应用场景,学会在实际开发中处理数据可视化与图表集成问题。
重点:数据可视化的定义与特点图表工具的定义与特点Spring Boot与图表工具的集成Spring Boot的实际应用场景

27.2 数据可视化与图表工具概述

数据可视化与图表工具是Java开发中的重要组件。

27.2.1 数据可视化的定义

定义:数据可视化是指将数据通过图表、地图、仪表盘等形式直观地展示出来,帮助用户更好地理解和分析数据。
作用

  • 提高数据的可读性。
  • 帮助用户发现数据中的规律。
  • 支持快速决策。

常见的数据可视化工具

  • ECharts:ECharts是百度开源的一个数据可视化库。
  • Highcharts:Highcharts是一个基于JavaScript的数据可视化库。
  • D3.js:D3.js是一个基于JavaScript的数据可视化库。
  • Tableau:Tableau是一个商业数据可视化工具。

✅ 结论:数据可视化是指将数据通过图表、地图、仪表盘等形式直观地展示出来,作用是提高数据的可读性、帮助用户发现数据中的规律、支持快速决策。

27.2.2 图表工具的定义

定义:图表工具是一种用于创建和展示图表的软件工具,用于数据可视化。
作用

  • 实现图表的创建。
  • 实现图表的展示。
  • 提高数据的可视化效果。

常见的图表工具

  • ECharts:ECharts是百度开源的一个数据可视化库。
  • Highcharts:Highcharts是一个基于JavaScript的数据可视化库。
  • D3.js:D3.js是一个基于JavaScript的数据可视化库。
  • Tableau:Tableau是一个商业数据可视化工具。

✅ 结论:图表工具是一种用于创建和展示图表的软件工具,作用是实现图表的创建、展示、提高数据的可视化效果。

27.3 Spring Boot与图表工具的集成

Spring Boot与图表工具的集成是Java开发中的重要内容。

27.3.1 集成ECharts的步骤

定义:集成ECharts的步骤是指使用Spring Boot与ECharts集成的方法。
步骤

  1. 创建Spring Boot项目。
  2. 添加所需的依赖。
  3. 配置ECharts。
  4. 创建数据访问层。
  5. 创建业务层。
  6. 创建控制器类。
  7. 创建前端页面。
  8. 测试应用。

示例
pom.xml文件中的依赖:

<dependencies><!-- Web依赖 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- Thymeleaf依赖 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><!-- 测试依赖 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies>

application.properties文件中的配置:

# 服务器端口 server.port=8080 # Thymeleaf配置 spring.thymeleaf.cache=false spring.thymeleaf.mode=HTML spring.thymeleaf.encoding=UTF-8 spring.thymeleaf.suffix=.html spring.thymeleaf.prefix=classpath:/templates/ 

实体类:

publicclassProduct{privateLong id;privateString productId;privateString productName;privatedouble price;privateint sales;publicProduct(){}publicProduct(Long id,String productId,String productName,double price,int sales){this.id = id;this.productId = productId;this.productName = productName;this.price = price;this.sales = sales;}// Getter和Setter方法publicLonggetId(){return id;}publicvoidsetId(Long id){this.id = id;}publicStringgetProductId(){return productId;}publicvoidsetProductId(String productId){this.productId = productId;}publicStringgetProductName(){return productName;}publicvoidsetProductName(String productName){this.productName = productName;}publicdoublegetPrice(){return price;}publicvoidsetPrice(double price){this.price = price;}publicintgetSales(){return sales;}publicvoidsetSales(int sales){this.sales = sales;}@OverridepublicStringtoString(){return"Product{"+"id="+ id +",+ productId +'\''+",+ productName +'\''+", price="+ price +", sales="+ sales +'}';}}

Repository接口:

importorg.springframework.stereotype.Repository;importjava.util.ArrayList;importjava.util.List;importjava.util.stream.Collectors;@RepositorypublicclassProductRepository{privateList<Product> products =newArrayList<>();publicProductRepository(){ products.add(newProduct(1L,"P001","手机",1000.0,100)); products.add(newProduct(2L,"P002","电脑",5000.0,50)); products.add(newProduct(3L,"P003","电视",3000.0,80)); products.add(newProduct(4L,"P004","手表",500.0,200)); products.add(newProduct(5L,"P005","耳机",300.0,150));}publicList<Product>getAllProducts(){return products;}publicProductgetProductById(Long id){return products.stream().filter(product -> product.getId().equals(id)).findFirst().orElse(null);}publicvoidaddProduct(Product product){ product.setId((long)(products.size()+1)); products.add(product);}publicvoidupdateProduct(Product product){Product existingProduct =getProductById(product.getId());if(existingProduct !=null){ existingProduct.setProductId(product.getProductId()); existingProduct.setProductName(product.getProductName()); existingProduct.setPrice(product.getPrice()); existingProduct.setSales(product.getSales());}}publicvoiddeleteProduct(Long id){ products.removeIf(product -> product.getId().equals(id));}}

Service类:

importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.stereotype.Service;importjava.util.List;@ServicepublicclassProductService{@AutowiredprivateProductRepository productRepository;publicList<Product>getAllProducts(){return productRepository.getAllProducts();}publicProductgetProductById(Long id){return productRepository.getProductById(id);}publicvoidaddProduct(Product product){ productRepository.addProduct(product);}publicvoidupdateProduct(Product product){ productRepository.updateProduct(product);}publicvoiddeleteProduct(Long id){ productRepository.deleteProduct(id);}}

控制器类:

importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.stereotype.Controller;importorg.springframework.ui.Model;importorg.springframework.web.bind.annotation.*;importjava.util.List;@Controller@RequestMapping("/api/products")publicclassProductController{@AutowiredprivateProductService productService;@GetMapping("/")publicStringgetAllProducts(Model model){List<Product> products = productService.getAllProducts(); model.addAttribute("products", products);return"product-list";}@GetMapping("/{id}")publicStringgetProductById(@PathVariableLong id,Model model){Product product = productService.getProductById(id); model.addAttribute("product", product);return"product-detail";}@GetMapping("/add")publicStringaddProductForm(Model model){ model.addAttribute("product",newProduct());return"product-form";}@PostMapping("/add")publicStringaddProduct(@ModelAttributeProduct product){ productService.addProduct(product);return"redirect:/api/products/";}@GetMapping("/edit/{id}")publicStringeditProductForm(@PathVariableLong id,Model model){Product product = productService.getProductById(id); model.addAttribute("product", product);return"product-form";}@PostMapping("/edit/{id}")publicStringeditProduct(@PathVariableLong id,@ModelAttributeProduct product){ product.setId(id); productService.updateProduct(product);return"redirect:/api/products/";}@GetMapping("/delete/{id}")publicStringdeleteProduct(@PathVariableLong id){ productService.deleteProduct(id);return"redirect:/api/products/";}}

前端页面(product-list.html):

<!DOCTYPEhtml><htmllang="zh-CN"xmlns:th="http://www.thymeleaf.org"><head><metacharset="UTF-8"><title>产品列表</title><scriptsrc="https://cdn.jsdelivr.net/npm/[email protected]/dist/echarts.min.js"></script></head><body><h1>产品列表</h1><ahref="/api/products/add">添加产品</a><tableborder="1"><thead><tr><th>ID</th><th>产品ID</th><th>产品名称</th><th>价格</th><th>销量</th><th>操作</th></tr></thead><tbody><trth:each="product : ${products}"><tdth:text="${product.id}"></td><tdth:text="${product.productId}"></td><tdth:text="${product.productName}"></td><tdth:text="${product.price}"></td><tdth:text="${product.sales}"></td><td><ath:href="@{/api/products/edit/{id}(id=${product.id})}">编辑</a><ath:href="@{/api/products/delete/{id}(id=${product.id})}">删除</a></td></tr></tbody></table><h2>产品销量图表</h2><divid="salesChart"style="width: 800px;height: 400px;"></div><script>// 初始化图表var chartDom = document.getElementById('salesChart');var myChart = echarts.init(chartDom);var option;// 准备数据var productNames =[];var productSales =[];<th:block th:each="product : ${products}"> productNames.push('[('+ product.productName +')]'); productSales.push('[('+ product.sales +')]');</th:block>// 配置图表 option ={title:{text:'产品销量图表',left:'center'},tooltip:{trigger:'item'},legend:{orient:'vertical',right:10,top:'center'},series:[{name:'销量',type:'pie',radius:['40%','70%'],avoidLabelOverlap:false,itemStyle:{borderRadius:10,borderColor:'#fff',borderWidth:2},label:{show:false,position:'center'},emphasis:{label:{show:true,fontSize:20,fontWeight:'bold'}},labelLine:{show:false},data:[<th:block th:each="product : ${products}">{value:[(' + product.sales + ')],name:'[('+ product.productName +')']},</th:block>]}]};// 渲染图表 option && myChart.setOption(option);</script></body></html>

应用启动类:

importorg.springframework.boot.SpringApplication;importorg.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplicationpublicclassEChartsApplication{publicstaticvoidmain(String[] args){SpringApplication.run(EChartsApplication.class, args);}}

测试类:

importorg.junit.jupiter.api.Test;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.boot.test.context.SpringBootTest;importorg.springframework.boot.test.web.client.TestRestTemplate;importorg.springframework.boot.web.server.LocalServerPort;importstaticorg.assertj.core.api.Assertions.assertThat;@SpringBootTest(webEnvironment =SpringBootTest.WebEnvironment.RANDOM_PORT)classEChartsApplicationTests{@LocalServerPortprivateint port;@AutowiredprivateTestRestTemplate restTemplate;@TestvoidcontextLoads(){}@TestvoidtestGetAllProducts(){String response = restTemplate.getForObject("http://localhost:"+ port +"/api/products/",String.class);assertThat(response).contains("产品列表");}}

✅ 结论:集成ECharts的步骤包括创建Spring Boot项目、添加所需的依赖、配置ECharts、创建数据访问层、创建业务层、创建控制器类、创建前端页面、测试应用。

27.4 Spring Boot的实际应用场景

在实际开发中,Spring Boot数据可视化与图表集成的应用场景非常广泛,如:

  • 实现产品信息的图表展示。
  • 实现用户信息的图表展示。
  • 实现订单信息的图表展示。
  • 实现销售数据的图表展示。

示例

importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.stereotype.Controller;importorg.springframework.ui.Model;importorg.springframework.web.bind.annotation.*;importjava.util.List;@Controller@RequestMapping("/api/products")classProductController{@AutowiredprivateProductService productService;@GetMapping("/")publicStringgetAllProducts(Model model){List<Product> products = productService.getAllProducts(); model.addAttribute("products", products);return"product-list";}@GetMapping("/{id}")publicStringgetProductById(@PathVariableLong id,Model model){Product product = productService.getProductById(id); model.addAttribute("product", product);return"product-detail";}@GetMapping("/add")publicStringaddProductForm(Model model){ model.addAttribute("product",newProduct());return"product-form";}@PostMapping("/add")publicStringaddProduct(@ModelAttributeProduct product){ productService.addProduct(product);return"redirect:/api/products/";}@GetMapping("/edit/{id}")publicStringeditProductForm(@PathVariableLong id,Model model){Product product = productService.getProductById(id); model.addAttribute("product", product);return"product-form";}@PostMapping("/edit/{id}")publicStringeditProduct(@PathVariableLong id,@ModelAttributeProduct product){ product.setId(id); productService.updateProduct(product);return"redirect:/api/products/";}@GetMapping("/delete/{id}")publicStringdeleteProduct(@PathVariableLong id){ productService.deleteProduct(id);return"redirect:/api/products/";}}@ServiceclassProductService{@AutowiredprivateProductRepository productRepository;publicList<Product>getAllProducts(){return productRepository.getAllProducts();}publicProductgetProductById(Long id){return productRepository.getProductById(id);}publicvoidaddProduct(Product product){ productRepository.addProduct(product);}publicvoidupdateProduct(Product product){ productRepository.updateProduct(product);}publicvoiddeleteProduct(Long id){ productRepository.deleteProduct(id);}}@RepositoryclassProductRepository{privateList<Product> products =newArrayList<>();publicProductRepository(){ products.add(newProduct(1L,"P001","手机",1000.0,100)); products.add(newProduct(2L,"P002","电脑",5000.0,50)); products.add(newProduct(3L,"P003","电视",3000.0,80)); products.add(newProduct(4L,"P004","手表",500.0,200)); products.add(newProduct(5L,"P005","耳机",300.0,150));}publicList<Product>getAllProducts(){return products;}publicProductgetProductById(Long id){return products.stream().filter(product -> product.getId().equals(id)).findFirst().orElse(null);}publicvoidaddProduct(Product product){ product.setId((long)(products.size()+1)); products.add(product);}publicvoidupdateProduct(Product product){Product existingProduct =getProductById(product.getId());if(existingProduct !=null){ existingProduct.setProductId(product.getProductId()); existingProduct.setProductName(product.getProductName()); existingProduct.setPrice(product.getPrice()); existingProduct.setSales(product.getSales());}}publicvoiddeleteProduct(Long id){ products.removeIf(product -> product.getId().equals(id));}}@SpringBootApplicationpublicclassEChartsApplication{publicstaticvoidmain(String[] args){SpringApplication.run(EChartsApplication.class, args);}}// 测试类@SpringBootTest(webEnvironment =SpringBootTest.WebEnvironment.RANDOM_PORT)classEChartsApplicationTests{@LocalServerPortprivateint port;@AutowiredprivateTestRestTemplate restTemplate;@TestvoidcontextLoads(){}@TestvoidtestGetAllProducts(){String response = restTemplate.getForObject("http://localhost:"+ port +"/api/products/",String.class);assertThat(response).contains("产品列表");}}

输出结果

  • 访问http://localhost:8080/api/products/:返回产品列表页面,包含产品销量图表。
  • 点击“添加产品”:跳转到添加产品页面。
  • 点击“编辑”:跳转到编辑产品页面。
  • 点击“删除”:删除产品。

✅ 结论:在实际开发中,Spring Boot数据可视化与图表集成的应用场景非常广泛,需要根据实际问题选择合适的图表工具。

总结

本章我们学习了Spring Boot数据可视化与图表集成,包括数据可视化的定义与特点、图表工具的定义与特点、Spring Boot与图表工具的集成、Spring Boot的实际应用场景,学会了在实际开发中处理数据可视化与图表集成问题。其中,数据可视化的定义与特点、图表工具的定义与特点、Spring Boot与图表工具的集成、Spring Boot的实际应用场景是本章的重点内容。从下一章开始,我们将学习Spring Boot的其他组件、微服务等内容。

Read more

Flutter 组件 sse_stream 的适配 鸿蒙Harmony 实战 - 驾驭高性能 Server-Sent Events 流、实现鸿蒙端实时数据推送与长连接保活优化方案

Flutter 组件 sse_stream 的适配 鸿蒙Harmony 实战 - 驾驭高性能 Server-Sent Events 流、实现鸿蒙端实时数据推送与长连接保活优化方案

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 组件 sse_stream 的适配 鸿蒙Harmony 实战 - 驾驭高性能 Server-Sent Events 流、实现鸿蒙端实时数据推送与长连接保活优化方案 前言 在鸿蒙(OpenHarmony)生态的即时性应用场景中,如金融级实时行情、直播间弹幕以及 AI 模型的流式回复(Streaming Response),我们需要一种比轮询更高效、比 WebSocket 更轻量的数据下发机制。 SSE(Server-Sent Events)作为 HTML5 规范下的长连接利器,以其对 HTTP 协议的完美兼容和自动重连的天生特性,在现代移动开发中大放异彩。 sse_stream 库为 Flutter 提供了精简且强大的 SSE 接入能力。在鸿蒙适配实战中,

By Ne0inhk
从小项目到大型鸿蒙 App 的架构变化

从小项目到大型鸿蒙 App 的架构变化

子玥酱(掘金 / 知乎 / ZEEKLOG / 简书 同名) 大家好,我是子玥酱,一名长期深耕在一线的前端程序媛 👩‍💻。曾就职于多家知名互联网大厂,目前在某国企负责前端软件研发相关工作,主要聚焦于业务型系统的工程化建设与长期维护。 我持续输出和沉淀前端领域的实战经验,日常关注并分享的技术方向包括前端工程化、小程序、React / RN、Flutter、跨端方案, 在复杂业务落地、组件抽象、性能优化以及多端协作方面积累了大量真实项目经验。 技术方向:前端 / 跨端 / 小程序 / 移动端工程化 内容平台:掘金、知乎、ZEEKLOG、简书 创作特点:实战导向、源码拆解、少空谈多落地 文章状态:长期稳定更新,大量原创输出 我的内容主要围绕 前端技术实战、真实业务踩坑总结、框架与方案选型思考、行业趋势解读 展开。文章不会停留在“API 怎么用”,而是更关注为什么这么设计、在什么场景下容易踩坑、

By Ne0inhk
Linux手搓进程池:从原理到实现,手把手教你搞定进程复用

Linux手搓进程池:从原理到实现,手把手教你搞定进程复用

🔥个人主页:Cx330🌸 ❄️个人专栏:《C语言》《LeetCode刷题集》《数据结构-初阶》《C++知识分享》 《优选算法指南-必刷经典100题》《Linux操作系统》:从入门到入魔 《Git深度解析》:版本管理实战全解 🌟心向往之行必能至 🎥Cx330🌸的简介: 目录 前言: 一、先搞懂:进程池是什么?核心优势有哪些? 二、手搓进程池:分步实现(附完整代码) 步骤1:前期准备——定义任务类型与测试任务 步骤2:实现子进程工作逻辑——任务执行的核心 步骤3:封装Channel类——管理主从进程通信与子进程 步骤4:封装ProcessPool类——进程池核心管理逻辑 步骤5:主函数测试 三、编译运行与结果分析(附Makefile) 四、完整代码展示 五、进阶优化:让进程池更实用 六、常见坑点与注意事项

By Ne0inhk
【AIGC安全】CCF-CV企业交流会直播回顾:探寻AI安全治理,共筑可信AI未来

【AIGC安全】CCF-CV企业交流会直播回顾:探寻AI安全治理,共筑可信AI未来

文章目录 * 一、活动背景:AI技术快速发展与安全治理需求迫切 * 二、论坛内容 * 金耀辉:智能共生时代:平衡生成式AI的创新与风险 * 何延哲:人工智能安全检测评估的逻辑和要点 * 谢洪涛:面向特定人物深度伪造视频的主动防御与被动检测技术 * 郭丰俊:视觉内容安全技术的前沿进展与应用 * 赫然:生成式人工智能安全与治理 * 三、回顾总结 一、活动背景:AI技术快速发展与安全治理需求迫切 随着人工智能(AI)技术的迅猛进步,尤其是以ChatGPT为代表的大型语言模型技术的兴起,AI技术已广泛渗透至各个领域。然而,AI技术的快速发展亦伴随着一系列安全隐患的出现,如数据安全、知识产权、算法偏见、有害内容生成以及深度伪造、AI诈骗等。这些问题不仅引起了公众的广泛关注和深切忧虑,也对AI技术的健康发展和社会的和谐稳定构成了潜在威胁。全国网络安全标准化技术委员会等机构亦发布了相关框架和指导原则,旨在规范AI技术的发展和应用,降低其潜在风险。同时,学术界和工业界也在积极探索AI安全治理的途径,从立法、监管、前沿研究、技术实践等多个层面出发,寻求

By Ne0inhk