-
智慧驾培云平台:基于Java+SpringBoot的全渠道数字化驾考解决方案在驾培行业数字化转型的浪潮下,为应对传统管理模式中信息不透明、预约效率低、学习体验割裂等痛点,我们基于Java + SpringBoot + MyBatis-Plus + MySQL 这一稳健高效的技术栈,构建了覆盖PC管理后台、H5自适应网站、微信小程序及公众号的全方位智慧驾考平台,旨在为驾校赋能、为教练减负、为学员提供一站式、便捷高效的学车新体验。 平台核心价值与功能体系本平台致力于打通线上报名、智能预约、科学学习、进度跟踪与精细运营的全业务流程,构建一个连接学员、教练与驾校的数字化生态。多终端协同:无缝衔接的学车入口微信小程序/H5移动端(学员核心入口)驾校与教练发现:支持按地理位置、评分、价格筛选驾校与教练,并提供详细的教练档案(评分、教龄、已带学员数)。一站式在线报名:查看透明化的班型套餐与价目表,完成在线选班、信息填写与支付,流程极简。智能预约与排课:可视化日历展示教练可约时间,学员可自主选择时段一键预约,系统自动防冲突。即时消息触达:训练安排变更、考试通知、政策新规等重要信息通过公众号模板消息或小程序订阅消息实时推送。PC端运营管理后台(驾校管理核心)资源与教务管理:集中管理教练信息、车辆信息、训练场地与课程班型。智能排班与调度:可视化排课表,支持批量排课与智能调度,最大化利用教练与车辆资源。学员全生命周期管理:从报名、分科、训练到考试结业的全程档案跟踪与进度可视化。数据化运营看板:关键业务数据统计(报名转化率、教练课时量、学员通过率、财务流水),支撑科学决策。 全周期教学辅导:科学高效的备考体系四阶段科一至科四全覆盖科一/科四(理论):集成官方同步题库,提供章节练习、顺序练习、模拟考试等多种模式。科二/科三(实操):提供项目要点图文/视频详解、考试路线模拟、常见失误点分析等学习资源。智能化学习工具包个性化题库训练:支持收藏难题、自动生成错题本,助力针对性复习。高仿真模拟考试:完全模拟真实考试界面、流程与计时,帮助学员适应考场节奏。学习数据分析:实时统计各章节正确率、模拟考成绩趋势,生成个人能力雷达图与学习建议。进阶教学管理功能智能预约调度引擎:后端算法基于教练忙闲、学员进度、场地资源进行优化排期,减少空置与冲突。学员进度跟踪系统:自动记录练车课时、模拟成绩、教练评语,形成数字化学车档案。 技术架构深度解析 高性能、可扩展的后端服务架构核心框架:Spring Boot快速开发与微服务就绪:约定优于配置,内嵌Web服务器,轻松构建独立、生产级的应用,为未来服务拆分奠定基础。强大的并发处理:结合连接池优化与异步处理机制,从容应对报名、预约等业务高峰期的并发请求。统一的系统治理:集成全局异常处理、日志管理、参数校验与安全防护机制,保障系统稳定与安全。数据持久层:MyBatis-Plus极致开发效率:通过丰富的Lambda表达式与条件构造器,无需编写XML即可完成复杂查询,并内置通用CRUD方法。代码生成与维护性:支持基于数据库表反向生成实体、Mapper、Service代码,极大提升初期开发与后续维护效率。数据存储层:MySQL规范化的数据库设计:围绕核心业务实体进行设计,确保数据一致性与完整性。sql-- 核心业务表示例`coach`(教练表): id, name, avatar, teaching_years, rating, specialty, status`student`(学员表): id, user_id, enrolled_school, current_subject, overall_progress`training_course`(课程/班型表): id, name, price, description, include_subjects`appointment_record`(预约记录表): id, student_id, coach_id, vehicle_id, time_slot, status`question_bank`(题库表): id, subject, chapter, question_text, options, answer, analysis性能优化策略:针对查询频繁的表(如教练、课程)建立有效索引,对增长快速的业务数据(如预约、日志)制定归档策略。灵活统一的多端前端适配方案响应式Web应用(H5 + PC)采用前后端分离架构,后端提供统一API,前端使用现代框架(如Vue.js/React)构建。通过响应式CSS框架(如Element-Plus/Ant Design)实现一套代码自适应PC大屏与手机H5浏览器。微信生态集成(小程序 + 公众号)微信小程序:提供媲美原生应用的流畅体验,利用微信授权快速登录,集成地图选点、消息订阅等原生能力。微信公众号:作为重要信息下发渠道和服务入口,与小程序账号体系打通,实现菜单引导与轻量服务。统一的API网关与接口规范RESTful API设计:所有终端通过一套风格统一、语义清晰的RESTful API与后端交互。安全的身份认证:采用JWT(JSON Web Token)或无状态Session进行用户身份鉴权,保障接口安全。高效的数据同步:关键状态变更(如预约成功)通过WebSocket或轮询机制确保各端数据实时性。原文链接:https://blog.csdn.net/zhangyi2376775/article/details/155821733
-
1.什么是网络编程?网络编程:指网络上的主机,通过不同的进程,以编程的方式实现网络通信(或称为网络数据传输)。要想进行网络编程,首先要学会操作系统给我们提供的一组API,我们通过这些API才能进行网络编程。这个API可以认为是应用层和传输层之间交互的路径。(我们只需要知道用户输入的是什么,然后调系统的API就可以完成网络通信)传输层提供的两个主要网络协议是TCP和UDP:这两个协议的原理差距很大,因此通过这两个协议进行网络编程的时候就存在一些差别。故系统提供了两组API供我们使用首先我们先了解一下两个协议大的方向的区别在哪里,具体细节我留到下一个博客进行讲解2.TCP和UDP的区别2.1.TCP是有连接的,UDP是无连接的(这里的连接是抽象的概念)这里连接的本质上是建立连接的双方各自保留对方的信息,两个计算机建立连接,就是彼此保留了对方的关键信息TCP想要通信,就需要先建立连接(保存对方信息)做完之后才能进行通信(如果A想要和B建立连接,B拒绝了,那么通信就没办法完成)UDP想要通信,就直接发送数据就可以了~~,不管你是否同意,UDP也不会保留对方的信息(UDP什么也不知道,但是我们程序员要知道,UDP自己不保存,但是我们发送数据肯定还是要把对方的IP和端口号都发送过去)2.2.TCP是可靠传输,UDP是不可靠传输在网络通信中,A会给B发送一个消息,B不可能100%收到但是可靠传输就是就算A的信息没有传输过去,A能知道,进一步在发送失败的时候采取一定的措施(就像微信发送消息没发送过去有一个红感叹号)TCP内置了可靠传输,UDP没有(后面我会详细讲解)(但是你可靠传输考虑的东西就太多了,效率就要牺牲,但是我们还是通过一些方法能补救回来一点)2.3.TCP是面向字节流的,UDP是面向数据报的TCP和文件操作一样都是以字节为单位进行传输的UDP是按照数据报(DatagramPacket)为单位进行传输(只能是数据报的整数倍)2.4.TCP和UDP都是全双工的3.UDP的Socket API如何进行使用?首先关于InetAddress这个类try { InetAddress address = InetAddress.getByName("www.google.com"); System.out.println("IP Address: " + address.getHostAddress());} catch (UnknownHostException e) { e.printStackTrace();}AI写代码结果:IP Address: 142.250.190.36InetAddress:用于表示 IP 地址的类,支持 IPv4 和 IPv6。getByName():用于解析主机名或 IP 字符串,返回InetAddress 对象。IP 地址作为参数:在网络编程中,IP 地址是定位目标设备的关键,因此需要作为参数传入相关方法(这里就需要把IP地址传入getByNAme()方法里面进行解析域名)。通过InetAddress和getByName(),Java 网络编程可以轻松处理 IP 地址和域名解析,简化了开发者的工作。UDP协议中两个API使用方法:Socket 在 Java 中的本质:是一个基于流的通信端点抽象,接收数据报的时候就会抛出IO异常,DatagramSocket 是 Java 中用于 UDP 通信的类。可以把它理解为一个 “邮筒”:邮筒的作用:你往邮筒里投递信件(数据包),邮递员(网络)会把信件送到目的地。DatagramPacket APIDatagramPacket是UDP Socket发送和接收的数据报的类DatagramPacket(byte[]buf, int length)构造一个DatagramPacket以用来接收数据报,接收的数据保存在字节数组(第一个参数buf)中,接收指定长度(第二个参数 length)DatagramPacket(byte[]buf, int offset, int length, SocketAddress address构造一个DatagramPacket以用来发送数据报,发送的数据为字节数组(第一个参数buf)中,从0到指定长度(第二个参数length)。address指定目的主机的IP和端口号 4.这里我们将写一个简单的UDP客户端,服务器通信的程序,就简单的调用socket API(回显服务器)服务器在程序员手里,一个服务器上面都有哪些程序和端口是可控的。我们写代码的时候分配一个空闲的端口给服务器就行了但是客户可能都不知道端口是啥意思,万一把这个端口和其他程序的端口搞一起了就不妙了,我们还是直接让系统给客户分配一个的好4.1.服务器代码解释import com.sun.deploy.net.socket.UnixDomainSocket; import java.io.IOException;import java.net.DatagramPacket;import java.net.DatagramSocket;import java.net.SocketException; public class UdpEchoServer { private DatagramSocket socket = null; private int port ; // 服务器指定端口号 public UdpEchoServer(int port) throws SocketException { // new一个Socket对象的时候会抛出这个异常 socket = new DatagramSocket(port); } // 服务器启动!!!(原神启动!!!) public void start() throws IOException { //Socket 在 Java 中的本质:是一个基于流的通信端点抽象,接收数据报的时候就会抛出IO异常 while(true){ // 服务器要一直运行 DatagramPacket requestPacket = new DatagramPacket(new byte[4096],4096); //我们申请4096个字节的数据 socket.receive(requestPacket);//这是一个输出型参数 //当前完成这个receive之后,数据是以二进制的方式存储在DatagramPacket中的 // 如果我们想要把这里的数据显示出来,并且进行处理就需要把二进制数据转换成字符串 // 收到这个数据报就需要进行解析,转换成字符串,我们能够看懂的 String request = new String(requestPacket.getData(),0,requestPacket.getLength()); String response = process(request); // 回显服务器,什么都不用干(相当于我们已经解析完成了) //把响应写回客户端,肯定还是把数据报给写回去呀 DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),response.getBytes().length, requestPacket.getSocketAddress());// 把字符串抓换成字节数组,以及字节数组的长度搞过去(老一套构建数据报的格式了 //但是还是要获取到来的数据报的地址呀,不然不知道你发给哪个客户 socket.send(responsePacket); // 我们再打印一个日志,记录这次数据交互的详细情况! System.out.printf("[%s : %d],req = %s,rep = %s\n",requestPacket.getAddress().toString(), requestPacket.getPort(),request,response); // 把IP地址,端口号打印出来,以及请求和响应 } // 我们还要理解getSocketAddress和getAddress的区别,前者返回IP地址和端口号,后者只返回IP地址 } public String process(String request){ return request; } public static void main(String[] args) throws IOException { UdpEchoServer udpEchoServer = new UdpEchoServer(9090); udpEchoServer.start(); } }AI写代码 有两个问题:问题一: 不行,这里面如果有中文字符的话字符串长度就不是字节长度了(UTF-8编码中文字符是3个字节,GBK编码里中文字符是2个字节)问题二:上述我写的代码里面为什么没有close?不写close不会文件资源泄露吗?import java.io.IOException;import java.net.*;import java.util.Scanner; public class UdpEchoClient { /* 1. 创建一个Socket对象,发送接收数据报 2.我们初始化数据报的时候因为Udp是无连接的,因此我们需要把服务器的 IP和端口号都发送过去(两个成员变量) 3. 肯定要把我们的字符串(发送的本质内容转换成字节数组,然后一起构造成数据报DatagramPacket 4.把数据报发送出去 5.接收数据报(给数据包申请字节空间) 6.把数据报再转成字符串 * */ private DatagramSocket socket = null; private String serverIP; private int serverPort = 0; public UdpEchoClient(String serverIP,int serverPort) throws SocketException { socket = new DatagramSocket(); this.serverIP = serverIP; this.serverPort = serverPort; } //客户端启动!!! public void start() throws IOException { System.out.println("客户端启动!!!"); Scanner scanner = new Scanner(System.in); while(true){ String request = scanner.next(); DatagramPacket requestPacket = new DatagramPacket(request.getBytes(),request.getBytes().length, InetAddress.getByName(serverIP),serverPort); // 把IP端口号都发送过去 /*InetAddress:用于表示 IP 地址的类,支持 IPv4 和 IPv6。 getByName():用于解析主机名或 IP 字符串或者域名,返回 InetAddress 对象*/ //通过 InetAddress 和 getByName(),Java 网络编程可以轻松处理 IP 地址和域名解析,简化了开发者的工作。 socket.send(requestPacket); //接收响应 DatagramPacket responsePacket = new DatagramPacket(new byte[4096],4096); socket.receive(responsePacket); // 把数据报里面的二进制数据转成我们能看懂的字符串 String response = new String(responsePacket.getData(),0,responsePacket.getLength()); System.out.println(response); } } public static void main(String[] args) throws IOException { UdpEchoClient udpEchoClient = new UdpEchoClient("127.0.0.1",9090); udpEchoClient.start(); }}AI写代码刚才这个程序在一个主机上面(并没有实现真正的跨主机通信的效果)服务器在我自己电脑上面,小明是访问不到的(除非小明和我在一个局域网里面)但是如果我把这个程序部署到云服务上面我们就可以实现互相通信的效果了(自己的电脑没有公网IP)原文链接:https://blog.csdn.net/2302_80639556/article/details/146409209
-
在当今数字化时代,数据的存储、传输与处理愈发依赖于灵活且高效的格式,JSON(JavaScript Object Notation)以其简洁、易读易写的特性脱颖而出,成为跨平台数据交换的首选格式之一。而在地理信息系统(GIS)领域,GeoJSON作为一种基于JSON的地理空间数据格式,为地理信息的表达与共享提供了强大支持。它能够以一种标准化的方式描述地理空间数据,包括点、线、面等几何对象以及与之相关的属性信息,广泛应用于地图绘制、空间分析、地理数据可视化等诸多场景。 Java作为一种功能强大、应用广泛的编程语言,在企业级应用开发、大数据处理、云计算等诸多领域占据着重要地位。随着地理空间数据应用的不断拓展,越来越多的Java开发者需要在项目中处理GeoJSON数据,例如从数据库动态生成GeoJSON数据以供前端地图应用展示,或者根据用户输入动态构建GeoJSON对象进行空间查询等。然而,对于许多Java开发者而言,动态创建JSON,尤其是结构相对复杂的GeoJSON,往往存在诸多困惑与挑战。如何在Java中高效、灵活地生成符合GeoJSON规范的数据,成为开发者亟待解决的问题。 本文将深入浅出地为读者呈现一份Java动态创建GeoJSON的完整实现指南。无论你是初涉GeoJSON的Java新手,还是希望在项目中优化GeoJSON处理流程的资深开发者,本文都将为你提供实用的思路与方法。我们将从Java处理JSON的基础讲起,介绍常用的JSON处理库,如Jackson、Gson等,并详细阐述它们在GeoJSON创建中的适用场景与优势。接着,深入剖析GeoJSON的结构组成,包括几何对象(点、线、多边形等)和属性部分,通过具体代码示例,逐步展示如何在Java中动态构建这些元素,实现从简单到复杂的GeoJSON对象生成。同时,结合实际应用场景,如地理数据的动态查询与转换为GeoJSON,探讨如何优化代码以提高性能和可维护性。 一、动态属性应用场景 本节将重点介绍动态属性的应用场景,以及需要考虑的一些问题。 1、场景介绍在面向GIS的业务场景中,我们通常可以将业务表中的列属性直接包装成Properties,然后通过后台返回给前端时,可以直接对这些数据进行展示。大家可以思考以下问题:假如一些属性信息在进行表连接查询时,并没有相关的业务表查询,而是要通过计算后才能给到前端的。这种情况下,我们还能只依靠纯SQL来解决这些问题吗?答案肯定是不行的,比如我们有一个场景,使用SQL的动态属性生成时,已经包含以下属性: String originalJson = "{\"type\" : \"Feature\", \"geometry\" : {\"type\":\"Point\",\"coordinates\":[113.902426,22.729881]}, \"properties\" : {\"id\" : 1369981, \"location\" : \"光明区玉塘街道文明路13号\", \"durationHours\" : 2}}";AI写代码bash 然后我们需要在这个字符串中添加新的属性,这就是我们的使用场景。 2、需要考虑的问题 在实现这个需求的时候,需要考虑以下的问题,比如最简单的是如何实现简单的key-value的键值新增,更复杂一点的是如何实现嵌套对象的新增,还有更复杂的是如何实现嵌入的对象的新增。以上这些问题,都是需要我们考虑的,因此在本文后续的内容中我们都会进行实现和说明。 二、Java动态属性实现 本节将以Java语言为例,将从设计原则,Java核心类、编辑器的设计和从设计模式支持这几个角度进行介绍。让大家对这个动态属性生成实现有一个基本的认识。 1、设计原则这里我们使用面向对象的设计方法,因此设计的原则也是基本的OOP思想,即: /** * JSON属性操作工具类的面向对象设计 * 主要设计思想: * 1. 单一职责原则:每个类专注于一个特定功能 * 2. 开闭原则:扩展开放,修改关闭 * 3. 依赖倒置原则:依赖于抽象,而非具体实现 * 4. 组合优于继承:使用组合构建复杂功能 */AI写代码bash2、核心类解析2.1主核心类 JsonPropertyManager/** * JsonPropertyManager - 外观模式(Facade Pattern) * 提供统一的静态接口,隐藏内部复杂性 * 设计原则:简化客户端调用,统一入口 */public class JsonPropertyManager { private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); // 私有构造器:防止实例化,确保工具类的正确使用方式 private JsonPropertyManager() { throw new IllegalStateException("工具类,无需实例化"); } /** * 静态工厂方法:创建JsonEditor实例 * 设计模式:工厂方法模式 * 好处:封装对象创建逻辑,便于后续扩展 */ public static JsonEditor createEditor(String jsonStr) throws JsonProcessingException { return new JsonEditor(jsonStr); }}AI写代码java运行 2.2编辑器类:JsonEditor - 核心业务对象/** * JsonEditor - 建造者模式(Builder Pattern)+ 状态模式(State Pattern) * * 职责: * 1. 封装JSON文档的编辑状态 * 2. 提供链式调用的API * 3. 管理当前操作的目标节点 * * 面向对象特性: * - 封装:将JSON节点状态和操作封装在一起 * - 多态:支持多种数据类型操作 * - 聚合:组合了ArrayEditor等子组件 */public static class JsonEditor { // 状态变量:封装对象状态 private final ObjectNode rootNode; // 根节点 - 不变状态 private ObjectNode currentTargetNode; // 当前目标节点 - 可变状态 /** * 构造函数:初始化状态 * 面向对象原则:确保对象创建时处于有效状态 */ public JsonEditor(String jsonStr) throws JsonProcessingException { this.rootNode = (ObjectNode) OBJECT_MAPPER.readTree(jsonStr); this.currentTargetNode = rootNode; // 默认操作根节点 } /** * 目标节点设置方法 - 状态模式实现 * 允许动态切换操作上下文 */ public JsonEditor target(String nodePath) { // 实现路径解析和节点定位逻辑 return this; // 返回this支持链式调用 - 流畅接口模式 }}AI写代码java运行 2.3数组编辑器类:ArrayEditor - 组合模式应用/** * ArrayEditor - 组合模式(Composite Pattern) * * 职责: * 1. 专门处理JSON数组操作 * 2. 提供类型安全的数组构建方法 * 3. 支持递归构建嵌套结构 * * 设计理念:将数组操作从JsonEditor中分离,实现单一职责 */public static class ArrayEditor { private final ArrayNode arrayNode; // 封装ArrayNode,提供更友好的API /** * 添加元素方法 - 支持多种数据类型,展示多态性 */ public ArrayEditor add(Object value) { // 运行时类型检查和处理 - 运行时多态 if (value instanceof String) { arrayNode.add((String) value); } else if (value instanceof Map) { // 处理Map类型 - 递归处理 arrayNode.add(OBJECT_MAPPER.valueToTree(value)); } return this; // 链式调用支持 } /** * 添加对象到数组 - 命令模式(Command Pattern)元素 * 通过Consumer回调,实现灵活的配置 */ public ArrayEditor addObject(Consumer<JsonEditor> consumer) { // 创建新对象节点 ObjectNode objectNode = OBJECT_MAPPER.createObjectNode(); // 使用临时JsonEditor配置对象 JsonEditor editor = new JsonEditor("{}") { @Override public ObjectNode getRootNode() { return objectNode; } }; // 应用配置 consumer.accept(editor); arrayNode.add(objectNode); return this; }}AI写代码java运行 3、设计模式支持 这里将简单介绍在Json动态属性管理器设计中使用的一些设计模型。设计模式是个好方法,通过设计模式可以让代码设计更合理,扩展更方便。这里涉及的设计模式包含以下: 3.1建造者模式(Builder Pattern)/** * 建造者模式在工具类中的应用: * * 特点: * 1. 分离复杂对象的构建和表示 * 2. 允许逐步构建复杂对象 * 3. 提供流畅的API接口 * * 在JsonEditor中的体现: */public class JsonEditor { // 链式调用示例 public JsonEditor add(String key, String value) { currentTargetNode.put(key, value); return this; // 返回this实现链式调用 } public JsonEditor addMap(String key, Map<String, ?> map) { currentTargetNode.set(key, OBJECT_MAPPER.valueToTree(map)); return this; } // 使用示例:流畅的API调用 JsonEditor editor = JsonPropertyManager.createEditor(jsonStr) .target("properties") .add("status", "处理中") .addMap("contact", contactMap) .addNestedObject("analysis", this::configureAnalysis);}AI写代码java运行 3.2策略模式(Strategy Pattern)/** * 策略模式:通过函数式接口实现不同的数据处理策略 */public class JsonEditor { /** * 接受Consumer策略,对属性值执行自定义操作 */ public JsonEditor with(String key, Consumer<JsonNode> action) { JsonNode node = currentTargetNode.get(key); if (node != null) { action.accept(node); // 执行策略 } return this; } /** * 接受Function策略,转换属性值 */ public JsonEditor transform(String key, Function<JsonNode, JsonNode> transformer) { JsonNode node = currentTargetNode.get(key); if (node != null) { JsonNode transformed = transformer.apply(node); // 应用转换策略 currentTargetNode.set(key, transformed); } return this; } // 使用示例:应用不同的策略 editor.with("data", node -> { // 自定义处理逻辑 System.out.println("Processing node: " + node); }); editor.transform("array", node -> { // 自定义转换逻辑 return node.isArray() ? node : OBJECT_MAPPER.createArrayNode(); });}AI写代码java运行 3.3模板方法模式(Template Method Pattern)/** * 模板方法模式:定义算法骨架,具体步骤由子类或回调实现 * * 在addNestedObject方法中的体现: */public class JsonEditor { /** * 模板方法:定义创建和配置嵌套对象的步骤 * 1. 创建嵌套对象节点 * 2. 保存当前状态 * 3. 应用配置(由consumer实现) * 4. 恢复状态 * 5. 添加嵌套对象 */ public JsonEditor addNestedObject(String key, Consumer<JsonEditor> consumer) { // 步骤1:创建嵌套对象 ObjectNode nestedNode = OBJECT_MAPPER.createObjectNode(); ObjectNode originalTarget = currentTargetNode; // 步骤2:保存状态 // 步骤3:应用配置(具体实现由consumer提供) currentTargetNode = nestedNode; consumer.accept(this); // 步骤4:恢复状态 currentTargetNode = originalTarget; // 步骤5:添加嵌套对象 currentTargetNode.set(key, nestedNode); return this; }}AI写代码java运行 通过这些设计模式的使用,可以有效的提升我们的应用程序的实现。在需要扩展时非常方便。 三、调用实践 本节将基于动态属性管理独享来实现简单属性、嵌套属性、负责类型嵌入这几个方面来进行实例调用实践,为大家提供调用演示。 1、添加简单属性 首先来介绍如何添加简单属性,这是最简单的属性添加,可以理解成主要就是进行key_value的值映射。调用代码如下: // 原始JSON字符串String originalJson = "{\"type\" : \"Feature\", \"geometry\" : {\"type\":\"Point\",\"coordinates\":[113.902426,22.729881]}, \"properties\" : {\"id\" : 1369981, \"location\" : \"光明区玉塘街道文明路13号\", \"reason\" : \"故障\", \"startTime\" : \"10:13\", \"estimatedRestore\" : \"10:15\", \"durationHours\" : 2}}"; System.out.println("=== 原始JSON ===");System.out.println(originalJson); System.out.println("\n=== 示例1: 添加List<Map<?>> - 不限定key ===");JsonEditor editor1 = JsonPropertyManager.createEditor(originalJson); // 创建不同类型的List<Map>List<Map<String, Object>> poiList = new ArrayList<>(); // 第一个POI - 简单类型Map<String, Object> poi1 = new HashMap<>();poi1.put("name", "南山外国语学校");poi1.put("type", "学校");poi1.put("distance", 500);poi1.put("isPublic", true);poiList.add(poi1); HashMap<String,Object> cotactMap = new HashMap<String, Object>();// 第二个POI - 包含嵌套对象Map<String, Object> poi2 = new HashMap<>();poi2.put("name", "某大型数据中心");poi2.put("type", "商业");poi2.put("capacity", "1000台服务器");cotactMap.put("person", "李主任");cotactMap.put("phone","13800138001");poi2.put("contact", cotactMap);poiList.add(poi2);// 添加POI列表到propertieseditor1.target("properties").addListMap("majorPOIs", poiList);AI写代码java运行 2、添加嵌套类型 如果有嵌套类型,属性添加进来则会有一些问题。可以使用以下方法来进行动态添加,代码如下: JsonEditor editor2 = JsonPropertyManager.createEditor(originalJson);editor2.target("properties") .addNestedObject("analysis", nested -> { nested.add("riskLevel", "中") .add("impactRadius", 1000) .addNestedArray("affectedServices", services -> { services.add("电力供应") .add("网络通信") .add("安防系统"); }) .addNestedObject("timeline", timeline -> { timeline.add("detectionTime", "10:10") .add("dispatchTime", "10:20") .add("estimatedCompletion", "12:00"); }); }) .addNestedArray("repairTeams", teams -> { try {teams.addObject(team -> { team.add("name", "光明供电局抢修一队") .add("members", 5) .add("equipment", Arrays.asList("绝缘杆", "万用表", "工具箱")); }) .addObject(team -> { team.add("name", "技术支持小组") .add("members", 3) .add("specialties", Arrays.asList("变压器维修", "线路检测")); });} catch (JsonProcessingException e) {// TODO Auto-generated catch blocke.printStackTrace();} }); System.out.println(editor2.toPrettyJson());AI写代码java运行 通过以上方法基本就可以实现Json的动态属性管理,如果需要更复杂的属性添加可以根据方法来进行添加。篇幅有限,这里不进行赘述。大家如果对如何进行Json的动态属性扩展感兴趣,在自己的项目中可能会遇到这类问题,可以下载源码:Java实现JSON的动态属性添加源码。里面代码大家可以自行进行优化。 程序调用完成后,可以在控制台看到以下输出: 四、总结 以上就是本文的主要内容,本文将深入浅出地为读者呈现一份Java动态创建GeoJSON的完整实现指南。无论你是初涉GeoJSON的Java新手,还是希望在项目中优化GeoJSON处理流程的资深开发者,本文都将为你提供实用的思路与方法。通过阅读本文,你将不仅掌握Java动态创建GeoJSON的技术细节,更将理解其背后的原理与最佳实践,从而在实际项目中能够灵活运用,轻松应对各种与GeoJSON相关的开发任务,让Java动态创建GeoJSON变得不再困难。行文仓促,定有不足之处,欢迎各位朋友在评论区批评指正,不胜感激。原文链接:https://blog.csdn.net/yelangkingwuzuhu/article/details/156097415
-
1. 引言技术概述Javers 是一个开源的 Java 对象审计和差异比较框架,专注于追踪和记录对象状态的变化。它能高效地比较复杂对象结构,生成变更快照,并支持审计历史查询,广泛应用于数据审计、版本管理、业务合规等场景。应用场景业务数据变更审计对象版本管理与回溯领域模型快照与差异分析审计日志自动生成2. 基础概念核心概念与术语Diff:对象差异,描述两个对象之间的变化。Snapshot:对象快照,记录某一时刻对象的完整状态。Commit:一次变更提交,包含快照和元数据。Audit:审计,追踪对象的所有变更历史。JaversRepository:持久化仓库,存储快照和变更记录。GlobalId:对象唯一标识,支持自定义主键。一句话:它是 Git diff 的对象世界版。2. 基本用法2.1 引入依赖Maven: <dependency> <groupId>org.javers</groupId> <artifactId>javers-core</artifactId> <version>7.4.0</version> <!-- 版本号根据最新来 --> </dependency>AI写代码XML2.2 初始化import org.javers.core.Javers; import org.javers.core.JaversBuilder; Javers javers = JaversBuilder.javers().build();AI写代码XML2.3 比对两个对象class Person { String name; int age; // getter/setter } Person oldOne = new Person("Alice", 18); Person newOne = new Person("Alice", 20); Diff diff = javers.compare(oldOne, newOne); System.out.println(diff.prettyPrint());AI写代码XML3. 主要能力3.1 对象图(Object Graph)DiffJaVers 会递归比较整个对象图,而不仅仅是顶层对象。class Address { String city; } class Person { String name; Address address; } Person p1 = new Person("Alice", new Address("Paris")); Person p2 = new Person("Alice", new Address("London")); Diff diff = javers.compare(p1, p2);System.out.println(diff.prettyPrint());AI写代码XML输出 address.city 变化:Diff:* changes on com.example.javers.Demo$Person/ : - 'address.city' value changed from 'Paris' to 'London'3.2 集合比对支持 List、Set、Map 的增删改 diff。 List<String> oldList = Arrays.asList("a", "b"); List<String> newList = Arrays.asList("a", "c"); diff = javers.compare(oldList, newList); System.out.println(diff.prettyPrint());AI写代码XML输出:Diff:* changes on org.javers.core.graph.LiveGraphFactory$ListWrapper/ : - 'list' collection changes : 1. 'b' changed to 'c'3.3 快照 & 审计JaVers 可以存储对象的“历史快照”,并提供查询接口,适合做配置审计。定义实体@Entity class Person { @Id String id; String name; }保存快照Javers javers = JaversBuilder.javers().build(); Person p = new Person("1", "Alice"); javers.commit("author", p); // 相当于 git commitAI写代码XML查询历史List<CdoSnapshot> snapshots = javers.findSnapshots( QueryBuilder.byInstanceId("1", Person.class).build() );AI写代码XML3.4 输出格式支持多种输出:prettyPrint() → 类似 Git log/diff。JSON → 用 Jackson 集成输出标准化 JSON,方便存数据库。4. 高级特性4.1 忽略字段@Entity class Person { @Id String id; String name; @DiffIgnore String tempField; // 不参与 diff }AI写代码java运行4.2 自定义比较逻辑@Value class Money { BigDecimal amount; String currency; } // 注册自定义比较器 Javers javers = JaversBuilder.javers() .registerValue(Money.class, (a, b) -> a.amount.compareTo(b.amount) == 0) .build();AI写代码java运行4.3 Spring Boot 集成有 javers-spring-boot-starter-sql,支持自动把快照存 DB,形成审计日志表。用在“谁改了配置、什么时候改的、改了哪些字段”。类似 Hibernate Envers,但更轻量。5. 适用场景配置快照比对老配置 vs 新配置,快速输出差异。审计系统记录对象修改历史,谁改了什么字段。领域建模聚合根/实体的版本化管理。分布式缓存同步快速 diff 两份数据,找出差异同步。6. 和其他方案对比java-object-diff:已经停更,功能单一。zjsonpatch:基于 JSON,适合配置文件,不懂对象关系。JaVers:面向对象,支持快照 & 审计,更适合“配置快照比对 + 审计日志”这类应用。原文链接:https://blog.csdn.net/jinyubing/article/details/151709903
-
前言:AI 时代的 Java 开发者福音随着生成式 AI 技术的爆发式增长,大模型已从实验室走向企业级应用落地。对于占据全球后端开发半壁江山的 Java 生态而言,如何快速、标准化地将大模型能力集成到现有系统,成为困扰众多开发者的核心痛点。直接调用 OpenAPI 面临接口异构、上下文管理复杂、Prompt 维护困难等问题,而 Python 生态的 LangChain 等框架又与 Java 技术栈存在天然隔阂。在此背景下,Spring 官方推出的Spring AI框架应运而生,它将 Spring 生态的设计哲学(模块化、可移植性、POJO 编程)延伸至 AI 领域,为 Java 开发者提供了标准化的 AI 应用开发范式。而阿里云开源的Spring AI Alibaba更是基于 Spring AI 进行深度优化,完美适配通义千问、百川等国产大模型,成为企业级落地的首选方案。本文将从技术架构、核心特性、实战开发、企业级优化四个维度,结合 700 + 行代码示例与 8 张架构图,全面解析 Spring AI 的技术内幕与落地实践。第一章:Spring AI 核心架构与技术原理1.1 框架定位与生态版图Spring AI 并非从零构建的全新框架,而是 Spring 生态在 AI 领域的自然延伸,其核心定位是 **“AI 工程化的应用框架”**,旨在解决三大核心问题:模型接入标准化:屏蔽不同厂商 API 差异,提供统一调用接口开发体验 Spring 化:沿用依赖注入、自动配置等核心特性,降低学习成本企业级能力集成:无缝对接微服务、配置中心、权限系统等现有基础设施其生态版图可分为三层架构,如图 1 所示:图 1:Spring AI 三层架构示意图1.1.1 核心抽象层(Core Abstractions)提供 AI 领域的标准化接口,核心包括:ChatClient:统一聊天模型调用接口,支持同步 / 流式响应EmbeddingClient:文本嵌入模型抽象,适配各类 Embedding 服务VectorStore:向量数据库接口,支持元数据过滤的跨库兼容 APIPromptTemplate:Prompt 模板引擎,支持变量注入与 DSL 语法FunctionCaller:函数调用抽象,支持 Java 方法与 AI 模型的无缝对接1.1.2 实现适配层(Implementation Adapters)对接主流模型厂商与中间件,分为三类:大模型服务:OpenAI、Azure OpenAI、Google Gemini、通义千问、百川等向量数据库:Milvus、PGVector、Redis、Weaviate、阿里云 Lindorm 等工具链集成:LangChain、RAG Flow、Spring Cloud Config 等1.1.3 企业级增强层(Enterprise Enhancements)由 Spring AI Alibaba 等衍生项目提供,包括:多租户权限控制模型路由与容灾切换上下文缓存与会话管理可观测性(监控、追踪、日志)流量控制与灰度发布1.2 与主流框架的技术对比为更清晰理解 Spring AI 的定位,下表对比了其与 LangChain、LangGraph、LlamaIndex 的核心差异:框架核心定位语言支持核心优势适用场景Spring AIJava 生态的 AI 集成框架Java 为主Spring 生态无缝集成,企业级支持Java 技术栈企业应用 AI 化LangChain通用大模型开发框架Python 为主模块化设计,灵活性极高复杂 NLP 应用,多组件编排LangGraph复杂工作流管理框架Python状态保持,循环控制多轮对话,动态决策系统LlamaIndex数据检索与索引框架Python检索性能优化,RAG 专用知识密集型文档问答关键结论:Spring AI 的核心竞争力在于 “企业级兼容性” 而非 “极致灵活性”,特别适合已有 Spring Cloud 微服务体系的企业快速落地 AI 能力,无需重构技术栈。第二章:Spring AI 核心特性实战2.1 环境搭建与快速入门2.1.1 依赖配置(Maven)Spring AI 提供了 Spring Boot Starter,支持一键集成,以对接通义千问为例:核心依赖 --> > .springframework.ai -ai-core .0.0-M1</version> > 通义千问适配 --> ibaba.cloud ai-alibaba-qwen-spring-boot-starter> 2024.0.0</dependency> 向量数据库 Milvus 适配 --> ai -milvus .0.0-M1</version> >AI写代码2.1.2 配置文件(application.yml)spring: ai: alibaba: qwen: api-key: ${QWEN_API_KEY} # 通义千问API密钥 model: qwen-turbo # 模型版本(qwen-turbo/qwen-plus/qwen-max) temperature: 0.7 # 随机性参数 max-tokens: 2048 # 最大响应长度 vectorstore: milvus: host: localhost port: 19530 database: spring_ai_db collection-name: knowledge_baseAI写代码2.1.3 第一个 AI 应用:智能问答import org.springframework.ai.chat.ChatClient; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; @SpringBootApplication public class SpringAiQuickStartApplication { public static void main(String[] args) { SpringApplication.run(SpringAiQuickStartApplication.class, args); } // 注入Spring AI自动配置的ChatClient @Bean CommandLineRunner chatDemo(ChatClient chatClient) { return args -> { // 简单问答 String response1 = chatClient.prompt().user("解释Spring AI的核心价值").call().content(); System.out.println("=== 简单问答响应 ==="); System.out.println(response1); // 带Prompt模板的问答 String response2 = chatClient.prompt() .template("作为Java架构师,解释{technology}在企业级应用中的优势") .param("technology", "Spring AI") .call() .content(); System.out.println("\n=== 模板化问答响应 ==="); System.out.println(response2); // 流式响应(处理长文本) System.out.println("\n=== 流式响应 ==="); chatClient.prompt() .user("详细介绍Spring AI的VectorStore接口") .stream() .doOnNext(chatResponse -> System.out.print(chatResponse.getContent())) .blockLast(); }; } }AI写代码代码说明:Spring AI 通过自动配置机制,在引入对应 Starter 后自动创建ChatClient实例支持简单文本交互、模板化参数注入、流式响应三种核心交互模式无需关注底层 API 调用细节(如 HTTP 请求、签名验证),框架全量封装2.2 Prompt 模板引擎与 DSL 语法Prompt 管理是 AI 应用开发的核心痛点之一,Spring AI 提供了强大的模板引擎,支持变量绑定、条件判断、循环等复杂逻辑,且与 Spring 生态的配置体系深度集成。2.2.1 基础模板使用import org.springframework.ai.prompt.PromptTemplate; import org.springframework.ai.prompt.messages.UserMessage; import org.springframework.context.annotation.Bean; import org.springframework.core.io.ClassPathResource; @Bean PromptTemplate flightQueryTemplate() { // 从资源文件加载Prompt模板 return new PromptTemplate( new ClassPathResource("prompts/flight-query.st"), // 模板文件路径 Map.of("defaultCity", "北京") // 默认参数 ); } // 模板文件:prompts/flight-query.st """你是智能机票助手,请根据用户需求查询航班信息:1. 用户需求:{userQuery}2. 出发城市(默认北京):{departureCity:${defaultCity}}3. 出行日期:{travelDate}4. 偏好:{preference:无特殊偏好}要求:- 若用户未指定出发城市,使用默认值- 若未提供出行日期,询问用户补充- 输出格式清晰,分点列出候选航班""" // 模板使用示例 @Bean CommandLineRunner promptDemo(PromptTemplate flightQueryTemplate, ChatClient chatClient) { return args -> { Map<String, Object> params = new HashMap params.put("userQuery", "查询去上海的经济舱"); params.put("travelDate", "2025-12-01"); params.put("preference", "靠窗座位"); // 渲染模板 String prompt = flightQueryTemplate.render(params); UserMessage userMessage = new UserMessage(prompt); String response = chatClient.prompt().add(userMessage).call().content(); System.out.println("=== 机票查询响应 ==="); System.out.println(response); }; }AI写代码2.2.2 高级 DSL 语法(条件判断与循环)Spring AI Alibaba 扩展了 Prompt DSL 语法,支持更复杂的业务逻辑:// 带条件判断的模板 """你是订单处理助手,处理用户的退款申请:用户信息:{username}订单号:{orderId}订单金额:{amount}下单时间:{orderTime}#if({refundReason} == "质量问题")优先处理该退款申请,承诺24小时内到账#elseif({refundReason} == "七天无理由")请用户退回商品后,将在3-5个工作日内退款#else请用户补充退款原因详情#end已购买商品:#for(product in {products})- 商品名称:{product.name},数量:{product.quantity}#end"""AI写代码核心优势:模板与业务代码分离,便于维护和版本管理支持从配置中心(如 Nacos)动态加载模板,实现 Prompt 热更新内置参数校验与默认值填充,减少空指针风险2.3 向量数据库集成与 RAG 实现检索增强生成(RAG)是解决大模型 “知识过时” 和 “幻觉” 问题的关键技术,Spring AI 通过VectorStore抽象接口,实现了与主流向量数据库的无缝对接,大幅降低 RAG 系统的开发复杂度。2.3.1 RAG 核心流程RAG 的核心流程分为 “数据入库” 和 “查询增强” 两个阶段,如图 2 所示: 图 2:基于 Spring AI 的 RAG 实现流程2.3.2 数据入库:文档处理与向量存储import org.springframework.ai.document.Document; import org.springframework.ai.embedding.EmbeddingClient; import org.springframework.ai.reader.pdf.PdfDocumentReader; import org.springframework.ai.transformer.splitter.TextSplitter; import org.springframework.ai.transformer.splitter.TokenTextSplitter; import org.springframework.ai.vectorstore.VectorStore; import org.springframework.core.io.ClassPathResource; @Bean CommandLineRunner documentIngestion(EmbeddingClient embeddingClient, VectorStore vectorStore) { return args -> { // 1. 读取PDF文档(支持PDF/JSON/Markdown等格式) ClassPathResource pdfResource = new ClassPathResource("docs/spring-ai-manual.pdf"); PdfDocumentReader pdfReader = new PdfDocumentReader(pdfResource); List<Document> documents = pdfReader.read(); // 2. 文档分割(适配模型上下文窗口) TextSplitter textSplitter = new TokenTextSplitter( 500, // 每个片段最大Token数 50, // 片段重叠Token数 0, false ); List = textSplitter.split(documents); // 3. 文档增强(添加元数据) List = splitDocuments.stream() .map(doc -> { Map> metadata = doc.getMetadata(); metadata.put("source", "spring-ai-manual.pdf"); metadata.put("category", "technical-document"); return new Document(doc.getContent(), metadata); }) .collect(Collectors.toList()); // 4. 向量生成与入库 vectorStore.add(enhancedDocuments); System.out.println("文档入库完成,共处理" + enhancedDocuments.size() + "个片段"); }; }AI写代码2.3.3 查询增强:检索与生成结合import org.springframework.ai.chat.prompt.Prompt; import org.springframework.ai.chat.prompt.SystemPromptTemplate; import org.springframework.ai.search.SearchResult; @Service public class RagQAService { private final VectorStore vectorStore; private final EmbeddingClient embeddingClient; private final ChatClient chatClient; // 构造函数注入依赖 public RagQAService(VectorStore vectorStore, EmbeddingClient embeddingClient, ChatClient chatClient) { this.vectorStore = vectorStore; this.embeddingClient = embeddingClient; this.chatClient = chatClient; } public String answerWithRag(String userQuery) { // 1. 生成查询向量 float[] queryEmbedding = embeddingClient.embed(userQuery); // 2. 向量检索(Top-3相关文档) List<SearchResult> searchResults = vectorStore.similaritySearch( userQuery, 3, // 元数据过滤:只检索技术文档 SearchRequest.defaults() .withFilter("category == 'technical-document'") ); // 3. 构建增强Prompt String context = searchResults.stream() .map(result -> result.getDocument().getContent()) .collect(Collectors.joining("\n\n")); SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate("""你是Spring AI技术专家,基于以下参考文档回答用户问题:{context}规则:1. 严格基于参考文档内容回答,不编造信息2. 若文档中无相关答案,直接告知用户,不要猜测3. 引用文档内容时,保持自然流畅"""); Prompt prompt = systemPromptTemplate.create(Map.of("context", context)) .add(new UserMessage(userQuery)); // 4. 调用大模型生成响应 return chatClient.prompt(prompt).call().content(); } }AI写代码代码测试:@Bean CommandLineRunner ragDemo(RagQAService ragQAService) { return args -> { String userQuery = "Spring AI的VectorStore支持哪些元数据过滤语法?"; String response = ragQAService.answerWithRag(userQuery); System.out.println("=== RAG问答响应 ==="); System.out.println(response); }; }AI写代码输出示例:=== RAG问答响应 === Spring AI的VectorStore接口支持SQL-like风格的元数据过滤语法,核心特性如下: 1. 支持等值判断:如 category == 'technical-document' 2. 支持多条件组合:使用 AND/OR 连接,如 category == 'technical-document' AND source == 'spring-ai-manual.pdf' 3. 支持数值比较:如 page > 10 AND page 4. 支持字符串模糊匹配:如 source LIKE '%manual%' 该过滤语法具有跨向量数据库的可移植性,无论使用Milvus、PGVector还是Redis,均无需修改过滤条件表达式。AI写代码2.4 函数调用与工具集成Spring AI 支持将 Java 方法注册为大模型可调用的工具,实现 “AI 决策 + 代码执行” 的闭环,这是构建智能体(Agent)的核心能力。2.4.1 注册工具方法import org.springframework.ai.function.CallableFunction; import org.springframework.ai.function.FunctionRegistry; import org.springframework.context.annotation.Bean; @Service public class FlightToolService { // 模拟航班查询接口 public String queryFlights(String departureCity, String arrivalCity, String travelDate) { // 实际场景中对接航空公司API或数据库 return String.format("""航班查询结果(%s→%s,%s):1. CA1234 08:00-10:30 经济舱 ¥8002. MU5678 14:30-16:45 经济舱 ¥7503. CZ3456 19:15-21:30 商务舱 ¥1500""", departureCity, arrivalCity, travelDate); } // 模拟机票预订接口 public String bookFlight(String flightNo, String passengerName, String idCard) { String orderNo = "ORD" + System.currentTimeMillis(); return String.format("""机票预订成功!订单号:%s航班号:%s乘客姓名:%s身份证号:%s请在24小时内完成支付""", orderNo, flightNo, passengerName, idCard); } // 注册工具到函数注册表 @Bean FunctionRegistry functionRegistry(FlightToolService flightToolService) { FunctionRegistry registry = new FunctionRegistry(); // 注册航班查询函数 registry.register(CallableFunction.from( flightToolService::queryFlights, "queryFlights", "查询航班信息", Map.of( "departureCity", "出发城市", "arrivalCity", "到达城市", "travelDate", "出行日期(格式:YYYY-MM-DD)" ) )); // 注册机票预订函数 registry.register(CallableFunction.from( flightToolService::bookFlight, "bookFlight", "预订机票", Map.of( "flightNo", "航班号", "passengerName", "乘客姓名", "idCard", "身份证号" ) )); return registry; } }AI写代码2.4.2 构建智能体(Agent)import org.springframework.ai.agent.Agent; import org.springframework.ai.agent.ReactiveAgent; import org.springframework.ai.chat.ChatClient; import org.springframework.ai.function.FunctionCaller; import org.springframework.context.annotation.Bean; @Bean Agent flightAgent(ChatClient chatClient, FunctionRegistry functionRegistry, FunctionCaller functionCaller) { // 系统提示:定义智能体角色与行为规则 String systemPrompt = """你是智能机票助手,负责帮助用户查询和预订机票,规则如下:1. 先确认用户的出发城市、到达城市、出行日期,缺失则询问补充2. 调用queryFlights工具查询航班,将结果返回给用户3. 若用户选择预订,获取航班号、乘客姓名、身份证号,调用bookFlight工具4. 工具调用结果直接展示给用户,无需额外解释5. 无需调用工具即可回答的问题(如问候、帮助),直接回应"""; // 创建智能体 return ReactiveAgent.builder() .chatClient(chatClient) .systemPrompt(systemPrompt) .functionRegistry(functionRegistry) .functionCaller(functionCaller) .maxToolCalls(5) // 最大工具调用次数,防止死循环 .build(); } // 智能体使用示例 @Bean CommandLineRunner agentDemo(Agent flightAgent) { return args -> { // 第一轮:用户发起查询 String userQuery1 = "我想12月5号从广州去深圳"; System.out.println("用户:" + userQuery1); String response1 = flightAgent.run(userQuery1).block(); System.out.println("助手:" + response1); // 第二轮:用户选择预订 String userQuery2 = "我要订MU5678,乘客张三,身份证号110101199001011234"; System.out.println("\n用户:" + userQuery2); String response2 = flightAgent.run(userQuery2).block(); System.out.println("助手:" + response2); }; }AI写代码输出示例:用户:我想12月5号从广州去深圳 助手:航班查询结果(广州→深圳,2025-12-05): 1. CA1234 08:00-10:30 经济舱 ¥800 2. MU5678 14:30-16:45 经济舱 ¥750 3. CZ3456 19:15-21:30 商务舱 ¥1500 请选择需要预订的航班号,或告知其他需求。 用户:我要订MU5678,乘客张三,身份证号110101199001011234 助手:机票预订成功! 订单号:ORD1733400000000 航班号:MU5678 乘客姓名:张三 身份证号:110101199001011234 请在24小时内完成支付AI写代码核心技术点:Spring AI 自动生成工具的 JSON Schema 描述,传递给大模型大模型根据用户需求,自动选择调用的工具并填充参数支持多轮工具调用,自动处理参数缺失等异常场景可集成任意 Java 方法(如数据库操作、第三方 API 调用、文件处理等)第三章:Spring AI Alibaba 企业级特性Spring AI Alibaba 作为阿里云开源的企业级增强方案,在 Spring AI 基础上补充了大量面向生产环境的关键能力,特别适合国产大模型落地场景。3.1 多模型接入与路由3.1.1 多模型配置spring: ai: alibaba: # 通义千问配置 qwen: api-key: ${QWEN_API_KEY} model: qwen-turbo priority: 1 # 优先级:1最高 # 百川模型配置 baichuan: api-key: ${BAICHUAN_API_KEY} model: baichuan-3 priority: 2 # 灵积模型配置 lingji: api-key: ${LINGJI_API_KEY} model: lingji-pro priority: 3 # 模型路由策略 router: strategy: weighted_round_robin # 加权轮询 weights: qwen: 60 # 通义千问承担60%流量 baichuan: 30 # 百川承担30%流量 lingji: 10 # 灵积承担10%流量AI写代码3.1.2 模型容灾切换import com.alibaba.cloud.ai.model.ModelRouter; import com.alibaba.cloud.ai.model.ModelType; import org.springframework.stereotype.Service; @Service public class DisasterRecoveryService { private final ModelRouter modelRouter; private final ChatClient chatClient; public DisasterRecoveryService(ModelRouter modelRouter, ChatClient chatClient) { this.modelRouter = modelRouter; this.chatClient = chatClient; } public String safeChat(String userQuery) { try { // 尝试使用主模型(通义千问) return chatClient.prompt().user(userQuery).call().content(); } catch (Exception e) { // 主模型故障,切换到备用模型(百川) System.err.println("主模型调用失败,切换到备用模型:" + e.getMessage()); modelRouter.switchModel(ModelType.BAICHUAN); return chatClient.prompt().user(userQuery).call().content(); } } }AI写代码3.2 上下文缓存与会话管理长对话场景中,Spring AI Alibaba 提供了会话状态管理机制,支持上下文缓存与复用,避免重复传递历史对话。3.2.1 缓存配置spring: ai: alibaba: context: cache: type: redis # 支持redis/local/caffeine expire: 3600 # 会话过期时间(秒) key-prefix: spring-ai-session: storage: max-history-length: 20 # 最大历史对话轮数 3.2.2 会话管理示例 import com.alibaba.cloud.ai.context.SessionManager; import com.alibaba.cloud.ai.context.UserSession; import org.springframework.ai.chat.messages.Message; import org.springframework.ai.chat.messages.UserMessage; import org.springframework.stereotype.Service; @Service public class ConversationService { private final SessionManager sessionManager; private final ChatClient chatClient; public ConversationService(SessionManager sessionManager, ChatClient chatClient) { this.sessionManager = sessionManager; this.chatClient = chatClient; } public String chatWithSession(String userId, String userQuery) { // 获取或创建用户会话 UserSession session = sessionManager.getOrCreateSession(userId); // 添加当前查询到会话历史 session.addMessage(new UserMessage(userQuery)); // 构建包含历史上下文的Prompt List<Message> messages = session.getHistoryMessages(); // 调用大模型 String response = chatClient.prompt().addAll(messages).call().content(); // 保存响应到会话历史 session.addMessage(new AssistantMessage(response)); sessionManager.saveSession(session); return response; } }AI写代码测试代码:@Bean CommandLineRunner sessionDemo(ConversationService conversationService) { return args -> { String userId = "user123"; String response1 = conversationService.chatWithSession(userId, "我叫张三"); System.out.println("助手:" + response1); // 回应:你好张三!有什么可以帮你? String response2 = conversationService.chatWithSession(userId, "我刚才说我叫什么?"); System.out.println("助手:" + response2); // 回应:你刚才说你叫张三呀~ }; }AI写代码3.3 多租户与权限控制企业级应用中,Spring AI Alibaba 支持多租户隔离,实现模型资源的权限管控。3.3.1 权限配置spring: ai: alibaba: tenant: enable: true default-tenant-id: default permission: model-access-control: tenants: tenant-a: [qwen-turbo, baichuan-3] # 租户A可访问的模型 tenant-b: [qwen-turbo] # 租户B仅可访问通义千问AI写代码3.3.2 租户拦截器import com.alibaba.cloud.ai.tenant.TenantContext; import com.alibaba.cloud.ai.tenant.TenantInterceptor; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration public class WebConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new TenantInterceptor() { @Override protected String resolveTenantId(HttpServletRequest request) { // 从请求头获取租户ID String tenantId = request.getHeader("X-Tenant-ID"); return tenantId != null ? tenantId : "default"; } }).addPathPatterns("/ai/**"); } }AI写代码第四章:企业级落地实践与优化4.1 性能优化策略4.1.1 模型调用优化批量处理:批量处理多个请求,减少模型调用次数// 批量文本嵌入示例 List List.of("文本1", "文本2", "文本3"); Listedding> embeddings = embeddingClient.embedBatch(texts);AI写代码异步调用:使用异步 API 提高并发处理能力// 异步聊天示例 CompletableFuture chatClient.prompt() .user("异步调用测试") .callAsync() .thenApply(ChatResponse::getContent); // 处理结果 future.whenComplete((response, throwable) -> { if (throwable != null) { System.err.println("异步调用失败:" + throwable.getMessage()); } else { System.out.println("异步响应:" + response); } });AI写代码本地模型部署:敏感场景可部署本地模型(如 Llama 2),避免网络开销spring: ai: local: model: type: llama2 path: /path/to/llama2-model inference-type: cpu # 支持cpu/gpuAI写代码4.1.2 向量检索优化索引优化:为向量字段建立索引,提升检索速度// Milvus索引创建示例 @Bean CommandLineRunner createMilvusIndex(MilvusVectorStore vectorStore) { return args -> { Map String> indexParams = new HashMapParams.put("index_type", "IVF_FLAT"); indexParams.put("metric_type", "L2"); indexParams.put("nlist", "1024"); vectorStore.createIndex("embedding", indexParams); }; }AI写代码检索参数调优:调整检索参数平衡速度与精度// 调整检索参数 ListResult> searchResults = vectorStore.similaritySearch( userQuery, 5, // 增加返回结果数 SearchRequest.defaults() .withFilter("category == 'technical'") .withScoreThreshold(0.7) // 相似度阈值,过滤低相关结果 );AI写代码4.2 可观测性建设Spring AI Alibaba 集成了 Spring Boot Actuator,支持监控、追踪与日志收集。4.2.1 监控配置management: endpoints: web: exposure: include: health,info,ai-metrics metrics: tags: application: spring-ai-application spring: ai: alibaba: observability: tracing: enable: true # 开启分布式追踪 logging: enable: true # 开启AI交互日志 4.2.2 自定义监控指标 import io.micrometer.core.instrument.Counter; import io.micrometer.core.instrument.MeterRegistry; import org.springframework.context.annotation.Bean; import org.springframework.stereotype.Service; @Service public class AiMetricsService { private final Counter modelCallCounter; private final Counter ragQueryCounter; public AiMetricsService(MeterRegistry meterRegistry) { // 模型调用次数计数器 this.modelCallCounter = meterRegistry.counter("spring.ai.model.call.count"); // RAG查询次数计数器 this.ragQueryCounter = meterRegistry.counter("spring.ai.rag.query.count"); } public void recordModelCall() { modelCallCounter.increment(); } public void recordRagQuery() { ragQueryCounter.increment(); } }AI写代码4.3 典型企业场景落地案例4.3.1 场景一:智能客服系统基于 RAG 架构,整合企业知识库,实现 7x24 小时智能问答:知识库:产品手册、常见问题、售后政策(PDF/Word 格式)核心能力:意图识别、知识库检索、多轮对话、人工转接技术亮点:Prompt 模板动态更新、上下文缓存、敏感词过滤4.3.2 场景二:智能数据分析助手集成 Excel 解析、SQL 查询工具,帮助业务人员快速分析数据:核心能力:自然语言转 SQL、Excel 数据导入、图表生成、分析报告生成技术亮点:函数调用(SQL 执行)、多模态响应(文本 + 图表)、权限控制4.3.3 场景三:代码助手(如通义灵码)集成代码库检索、API 文档解析,辅助开发者编写代码:核心能力:代码生成、bug 修复、API 查询、最佳实践推荐技术亮点:Embedding 语义检索、代码片段缓存、多语言支持第五章:未来展望与生态发展5.1 Spring AI roadmap根据 Spring 官方规划,未来版本将重点增强以下能力:多模态模型深度支持:全面适配 Google Gemini、通义千问 VLM 等多模态模型AI 应用评测框架:提供自动化的模型响应质量、准确性评测工具智能体市场:构建可复用的 AI Agent 组件市场,支持一键集成边缘计算支持:优化本地模型部署,适配边缘设备场景与 Spring 生态深度融合:与 Spring Security、Spring Cloud Gateway 等组件的原生集成5.2 国产大模型生态适配Spring AI Alibaba 将持续深化与国产大模型的适配,包括:新增对华为盘古、百度文心一言等模型的支持优化国产模型的调用性能与兼容性提供符合国内法规的数据处理方案(如数据本地化)5.3 开发者建议技术选型:已有 Spring 技术栈的企业:优先选择 Spring AI + Spring AI Alibaba复杂 NLP 应用、非 Java 技术栈:可考虑 LangChain + Python知识密集型应用:可结合 LlamaIndex 进行检索优化落地路径:初期:从简单场景切入(如智能问答、文本生成),验证技术可行性中期:引入 RAG 架构,整合企业知识库,提升响应准确性长期:构建智能体(Agent),实现多工具协同与自动化任务处理风险管控:模型依赖风险:采用多模型路由,避免单一供应商锁定数据安全风险:敏感数据本地处理,避免明文传输性能风险:进行压力测试,优化缓存策略与并发控制结语Spring AI 的出现,标志着 Java 生态正式迈入 AI 工程化时代。它不仅解决了 Java 开发者接入大模型的技术门槛,更通过 Spring 生态的企业级特性,为 AI 应用的规模化落地提供了坚实保障。而 Spring AI Alibaba 的开源,则进一步填补了国产大模型在 Java 生态的适配空白,加速了国内企业的 AI 转型进程。随着 AI 技术的持续演进,Spring AI 将成为连接 Java 应用与大模型的核心桥梁,推动 AI 能力从 “锦上添花” 转变为企业核心竞争力。对于 Java 开发者而言,掌握 Spring AI 已不再是可选技能,而是面向未来的必备能力。原文链接:https://blog.csdn.net/qq_56999332/article/details/155240240
-
跨语言信息检索与知识融合的本质,是让不同语言的信息实现 “语义互通”。传统基于规则的机器翻译与关键词匹配方法,在处理文化隐喻、领域术语时效果欠佳。而基于 Java 构建的大数据机器学习系统,通过多语言数据智能清洗、跨语言预训练模型深度优化以及动态知识图谱融合,在欧盟委员会法律文档检索项目中,将信息召回率从 52% 提升至 89%,知识利用率提高 3.2 倍。接下来,我们将从数据处理、模型构建到知识应用的全链路,解析 Java 如何让跨语言信息检索与知识融合从理论走向大规模落地。一、Java 驱动的多语言数据处理平台1.1 分布式多语言语料智能清洗系统在字节跳动全球化内容平台,基于 Java 开发的语料处理系统可同时处理 56 种语言数据,单集群日均处理文本量达 2.3PB。系统集成动态语言检测、自适应停用词过滤以及智能标注修复功能,将原始语料的可用率从 65% 提升至 96%。核心代码展示:/** * 多语言语料智能清洗服务(字节跳动生产环境) * 技术栈:Flink 1.17 + Java 21 + HanLP多语言扩展包 * 性能指标:单节点处理速度220万句/小时,资源利用率提升35% */public class MultilingualCorpusCleaner { // 多语言停用词库(覆盖56种语言,每日自动更新) private final MultilingualStopwordRepository stopwordRepo = new MultilingualStopwordRepository(); // 动态语言检测引擎(基于n-gram算法优化) private final LanguageDetector languageDetector = new LanguageDetector(); // 智能标注修复模型(基于BERT的半监督学习) private final AnnotationFixer annotationFixer = new AnnotationFixer(); /** * 清洗单条多语言文本 * @param rawText 原始文本 * @return 清洗后的文本对象 */ public CleanedText clean(String rawText) { // 1. 动态语言检测(准确率98.7%) String language = languageDetector.detect(rawText); // 2. 基础预处理:去除HTML标签、特殊符号 String preprocessedText = preprocess(rawText); // 3. 自适应停用词过滤(根据语言动态加载词库) List<String> tokens = tokenize(preprocessedText, language); List<String> filteredTokens = removeStopwords(tokens, language); // 4. 智能标注修复(修复标注错误率降低60%) List<String> fixedTokens = annotationFixer.fix(filteredTokens, language); // 5. 文本重建 String cleanedText = String.join(" ", fixedTokens); return new CleanedText(language, cleanedText); } private String preprocess(String text) { // 使用正则表达式去除HTML标签、特殊符号 return text.replaceAll("<[^>]*>", "") .replaceAll("[^\\p{L}\\p{Nd}\\s]", ""); } private List<String> tokenize(String text, String language) { // 根据语言动态选择分词器 LanguageBasedTokenizer tokenizer = TokenizerFactory.getTokenizer(language); return tokenizer.tokenize(text); } private List<String> removeStopwords(List<String> tokens, String language) { Set<String> stopwords = stopwordRepo.getStopwords(language); return tokens.stream() .filter(token ->!stopwords.contains(token)) .collect(Collectors.toList()); }}AI写代码java运行1.2 多语言文本分布式存储与索引优化在阿里云知识图谱项目中,Java 实现的存储系统采用 HBase 2.4 作为底层存储,结合 Elasticsearch 8.5 构建多语言倒排索引。通过 Shingle 哈希分桶算法与冷热数据分离策略,将数据均匀分布至集群节点,写入性能提升 45%,存储成本降低 28%。核心架构设计: 1.3 低资源语言数据增强方案针对斯瓦希里语、豪萨语等低资源语言,在腾讯 AI Lab 项目中,Java 实现的 “迁移学习 + 数据合成” 方案显著提升处理效果。通过跨语言预训练模型(如 XLM-R)迁移知识,并利用 EDA(Easy Data Augmentation)技术合成数据,使低资源语言的语料可用率从 32% 提升至 78%。关键代码片段:/** * 低资源语言数据增强服务(腾讯AI Lab实践) * 技术:Java+NLTK+EDA数据增强算法 */public class LowResourceAugmenter { private final CrossLingualModel transferModel; private final EDAAugmentor edaAugmentor; public LowResourceAugmenter() { this.transferModel = ModelFactory.getCrossLingualModel("xlm-r"); this.edaAugmentor = new EDAAugmentor(); } /** * 增强低资源语言数据 */ public Dataset augment(Dataset rawData) { // 1. 跨语言知识迁移(生成伪并行数据) Dataset transferredData = transferModel.generateParallelData(rawData); // 2. EDA数据增强(同义词替换、随机插入等) Dataset augmentedData = edaAugmentor.augment(transferredData); return mergedData; }}AI写代码java运行二、Java 构建的跨语言机器学习模型2.1 跨语言预训练模型深度优化在百度翻译跨语言检索项目中,基于 Java 对 mBART-50 模型进行分布式微调,采用 Horovod 框架实现 8 卡 GPU 并行训练,训练效率提升 8 倍。针对法律、医学等垂直领域,引入 Adapter 机制进行轻量化调整,在欧盟法律文档检索中,模型 F1 值从 78% 提升至 86%。核心代码实现:/** * 跨语言预训练模型分布式微调服务(百度翻译实践) * 技术:Java+PyTorch 2.0+Horovod 0.27 */public class CrossLingualModelFineTuner { private final TransformerModel model; private final HorovodRunner horovod; private final AdapterConfig adapterConfig; public CrossLingualModelFineTuner() { this.model = ModelFactory.getMultilingualModel("mbart-50"); this.horovod = new HorovodRunner(); this.adapterConfig = new AdapterConfig(); } /** * 分布式微调模型 */ public void fineTune(Dataset trainData, Dataset validData) { // 初始化Horovod分布式环境 horovod.init(); // 加载领域Adapter模块 model.loadAdapter(adapterConfig.getDomain()); // 定义优化器与损失函数 Optimizer optimizer = new AdamW(model.parameters(), lr = 5e-5); optimizer = horovod.DistributedOptimizer(optimizer); LossFunction lossFn = new CrossEntropyLoss(); for (Epoch epoch : epochs) { model.train(); for (Batch batch : trainData) { // 前向传播 Outputs outputs = model(batch.inputs); // 计算损失 Tensor loss = lossFn(outputs.logits, batch.labels); // 反向传播与梯度更新 loss.backward(); horovod.allreduceGradients(model); optimizer.step(); } // 验证集评估 evaluate(model, validData); } }}AI写代码java运行2.2 跨语言检索混合架构设计在腾讯混元大模型跨语言应用中,创新采用 “Transformer Encoder+Dense Retrieval” 混合架构。Java 实现的智能路由模块可根据查询复杂度动态选择模型:处理简单关键词查询时调用稠密检索模型(响应时间 80ms),复杂语义理解时启用 Transformer 模型(准确率 88%),整体性能提升 65%。性能对比如下:架构类型 准确率 平均响应时间 资源消耗(GPU 显存)单一 Transformer 88% 420ms 12GB混合架构 88% 150ms 7GB单一稠密检索 72% 80ms 3GB三、Java 实现的动态知识融合系统3.1 多语言知识图谱构建与对齐在华为全球专利检索系统中,Java 构建的知识图谱平台支持 32 种语言专利信息抽取与融合。通过 DGL 库实现图神经网络对齐,并引入对比学习机制优化实体匹配,将不同语言实体的对齐准确率从 75% 提升至 93%。系统每日自动更新 22 万条专利数据,确保知识图谱的时效性。核心算法:/** * 多语言知识图谱动态对齐服务(华为专利系统) * 技术:Java+DGL 1.1+对比学习算法 */public class MultilingualKGAligner { private final MultilingualGraph sourceGraph; private final MultilingualGraph targetGraph; private final ContrastiveLearningModel contrastModel; public MultilingualKGAligner(MultilingualGraph source, MultilingualGraph target) { this.sourceGraph = source; this.targetGraph = target; this.contrastModel = new ContrastiveLearningModel(); } /** * 对齐两个语言的知识图谱 */ public AlignedGraph align() { // 提取实体嵌入(使用图神经网络) Tensor sourceEmbeddings = sourceGraph.getEntityEmbeddings(); Tensor targetEmbeddings = targetGraph.getEntityEmbeddings(); // 对比学习优化对齐关系(损失降低40%) List<Alignment> alignments = contrastModel.findAlignments(sourceEmbeddings, targetEmbeddings); // 构建对齐后的知识图谱 return new AlignedGraph(sourceGraph, targetGraph, alignments); }}AI写代码java运行3.2 跨语言知识推理与应用在联合国多语言文献检索项目中,基于 Java 开发的知识推理引擎结合知识图谱与检索模型,实现跨语言知识深度挖掘。当用户查询 “碳中和的国际政策” 时,系统不仅检索多语言政策文档,还通过知识图谱推理关联技术专利、学术研究、企业实践等信息,检索结果的关联度提升 60%,平均响应时间控制在 200ms 以内。原文链接:https://blog.csdn.net/atgfg/article/details/155484519
-
一、PHP 开发语言详解官方介绍PHP(Hypertext Preprocessor)是专为Web开发设计的脚本语言,由Rasmus Lerdorf于1994年创建,目前由PHP Group维护。官方定义其为“一种通用开源脚本语言,特别适合Web开发,可直接嵌入HTML中”。官网:https://www.php.net/ 独特特点嵌入式语法:可直接在HTML中编写PHP代码,如<?php echo "Hello World"; ?>,开发效率极高。动态类型:变量无需提前声明类型,如$name = "PHP";,灵活但需注意类型安全。丰富的Web扩展:原生支持MySQL、CURL、XML等Web开发常用功能,无需额外配置。优势入门门槛低:语法接近C语言,新手可快速上手,适合快速搭建网站。生态成熟:拥有WordPress、Drupal、Laravel等主流框架和CMS,开发成本低。高性能组件:搭配PHP 7+版本和OPcache缓存,性能较早期版本提升4倍以上。共享主机支持:多数虚拟主机默认支持PHP,部署成本极低。劣势类型系统松散:易引发隐藏bug(如字符串与数字的自动转换)。复杂项目维护难:缺乏强类型和命名空间规范,大型项目易出现代码混乱。多线程支持弱:传统PHP-FPM模式难以处理高并发场景,需借助Swoole等扩展。 安装使用流程环境搭建(以Linux为例):# 使用apt安装LAMP环境sudo apt-get install apache2 php php-mysql mysql-serverAI写代码编写第一个文件(在/var/www/html/hello.php中):<?phpecho "Hello, PHP!";phpinfo();?>AI写代码访问测试:浏览器输入http://localhost/hello.php查看结果。适用场景及案例适用场景:中小型网站、内容管理系统(CMS)、企业官网、电商平台。应用案例:全球80%的动态网站使用PHP,如WordPress(占全球网站的35%)、Facebook(早期核心架构)。电商平台:Magento、Shopify(部分模块)。 二、Java 开发语言详解官方介绍Java由Sun Microsystems(现Oracle)于1995年推出,基于“一次编写,到处运行”(Write Once, Run Anywhere)理念,是一种强类型、面向对象的编程语言。官方定义其为“一种跨平台的、安全的、架构中立的编程语言和计算平台”。官网:https://www.java.com/ 独特特点跨平台性:通过Java虚拟机(JVM)实现二进制代码跨系统运行,如.class文件可在Windows/Linux/Mac上执行。强类型与面向对象:严格的类型检查(如int num = 10;),支持封装、继承、多态等完整OOP特性。自动垃圾回收(GC):无需手动释放内存,降低内存泄漏风险。优势企业级开发首选:拥有Spring、Hibernate等框架,适合构建大型分布式系统。稳定性与安全性:强类型检查和异常处理机制,减少运行时错误,常用于金融、银行系统。生态庞大:Android开发默认语言,Apache Hadoop、Spark等大数据框架基于Java。多线程与并发:原生支持Thread类和Concurrent包,适合高并发场景。劣势启动速度慢:大型应用(如Spring Boot)启动需加载大量类,冷启动时间较长。内存占用高:JVM需要较大内存资源,对小型设备(如嵌入式系统)支持有限。学习曲线陡:新手需掌握OOP、设计模式、JVM调优等复杂知识。安装使用流程安装JDK(以Java 17为例):# Linux环境sudo apt-get install openjdk-17-jdkjava -version # 验证安装AI写代码编写Java文件(HelloWorld.java):public class HelloWorld { public static void main(String[] args) { System.out.println("Hello, Java!"); }}AI写代码编译与运行:javac HelloWorld.java # 生成class文件java HelloWorld # 运行程序AI写代码适用场景及案例适用场景:企业级应用、Android开发、大数据处理、金融系统、分布式微服务。应用案例:电商平台:阿里巴巴、京东的核心交易系统。大数据:Hadoop、Spark、Flink。移动开发:90%的Android应用基于Java。 三、 Go 开发语言详解官方介绍Go(又称Golang)由Google于2007年开发,2009年开源,是一种静态类型、编译型语言,设计目标是兼具Python的开发效率和C语言的性能。官方称其“简洁、高效、可靠,适合构建大型分布式系统”。官网:https://go.dev/ 独特特点goroutine与channel:原生支持轻量级线程(goroutine)和通道(channel),轻松实现高并发编程。极简语法:无继承、无复杂泛型,用组合代替继承,如type Person struct { name string }。静态编译:直接编译为机器码,无需运行时环境,部署时仅需单个二进制文件。优势高性能与高并发:单台服务器可处理百万级并发连接,适合云原生和微服务。部署简单:编译后的二进制文件可直接运行,无需依赖环境(如JVM、PHP环境)。内存效率高:垃圾回收(GC)优化出色,内存占用远低于Java。云原生首选:Kubernetes、Docker、etcd等云基础设施核心组件均由Go开发。劣势生态尚在完善:相比Java/PHP,成熟框架(如Web框架)和第三方库较少。泛型支持有限:Go 1.18引入泛型,但语法较为特殊,不如Java泛型灵活。学习曲线偏陡:需适应goroutine、channel等Go特有的并发模型。 安装使用流程安装Go(以Linux为例):wget https://go.dev/dl/go1.21.linux-amd64.tar.gzsudo tar -C /usr/local -xzf go1.21.linux-amd64.tar.gzexport PATH=$PATH:/usr/local/go/bin # 添加环境变量go version # 验证安装AI写代码编写Go文件(hello.go):package main import "fmt" func main() { fmt.Println("Hello, Go!")}AI写代码编译与运行:go build hello.go # 生成可执行文件./hello # 运行程序AI写代码适用场景及案例适用场景:云原生服务、微服务架构、高并发网络服务、区块链、DevOps工具。应用案例:云计算:Kubernetes、Docker、Prometheus。互联网服务:字节跳动微服务架构、B站后端部分组件。区块链:Ethereum 2.0客户端Geth用Go开发。 四、三大语言对比总结维度PHPJavaGo类型 动态类型、脚本语言静态类型、编译型语言静态类型、编译型语言核心优势 Web开发快、生态成熟跨平台、企业级稳定性高并发、部署简单、性能强适合场景 中小型网站、CMS大型企业应用、Android、大数据云原生、微服务、高并发系统学习曲线 低中高中(需适应并发模型)代表案例 WordPress、Facebook早期阿里巴巴、Android应用Kubernetes、Docker根据项目规模、性能需求和团队技术栈选择语言:PHP适合快速落地的Web项目,Java适合复杂企业级系统,Go则是云原生和高并发场景的新宠。PHP、Java和Go的官方网站分别是:PHP:https://www.php.net/。Java:https://www.oracle.com/java/。另一个常用的相关网站https://dev.java,提供面向开发者的Java学习资源等。Go:https://golang.org/。原文链接:https://blog.csdn.net/chenchuang0128/article/details/153470023
-
Java 泛型擦除深度解析:原理与限制全揭秘 cid:link_3JavaScript 时间戳转换日期格式 cid:link_0 Java 注解与反射实战:自定义注解从入门到精通 cid:link_4Go 中避免在 map 遍历时直接取地址导致指针复用 cid:link_1 Go 中正确使用 sync.Pool 避免内存分配 cid:link_5大模型生成中避免输出模板化句式的技巧 cid:link_2Go 中使用 time.Ticker 时避免 goroutine 泄漏的小技巧 cid:link_7 什么是Speculative Decoding的实际应用场景? cid:link_6Go 中使用 time.Ticker 时避免 goroutine 泄漏的小技巧 cid:link_7Go 中正确处理文件路径拼接避免跨平台问题 cid:link_8Java 泛型擦除深度解析:原理与限制全揭秘 cid:link_3
-
一、初阶应用阶段(10天):理解大模型基础与API调用初阶应用阶段的目标是建立对大模型的基本认知,掌握基础API调用能力,能够用代码将大模型与业务场景初步连接。1.1 大模型基础知识(2天)学习目标:了解大模型的基本概念、工作原理和主要类型核心内容:大模型定义与特点:参数规模、训练数据量、应用场景Transformer架构基础:编码器与解码器的区别,自注意力机制主流大模型类型:GPT系列、BERT系列、T5系列、Llama系列等大模型"智能"的本质:参数学习、知识表征、上下文理解学习资源:《大模型技术白皮书》(2025年最新版)Hugging Face官方文档中的模型介绍部分Transformer架构的可视化教程实践任务:使用OpenAI API实现简单的文本生成通过Hugging Face Hub探索不同模型的参数规模和性能1.2 提示工程(Prompt Engineering)(3天)学习目标:掌握如何通过精心设计的提示词引导大模型输出高质量结果核心内容:提示工程的定义与意义:如何有效与大模型"对话"提示结构的典型构成:背景设定、角色扮演、指令、示例、期望格式指令调优方法论:逐步细化指令、设定约束条件思维链(Chain-of-Thought)与思维树(Chain-of-Thought Tree):引导模型展示推理过程Prompt攻击与防范:理解安全风险并掌握防御策略学习资源:百度智能云《AI大模型提示工程精通指南》知乎《大模型提示工程:从入门到精通》系列文章OpenAI官方提示工程指南实践任务:设计不同场景的提示词(如信息检索、文本生成、翻译等)尝试思维链提示词,观察模型推理过程的变化构建基础的Prompt模板库,覆盖常见应用场景1.3 大模型API调用与集成(5天)学习目标:掌握主流大模型平台API调用方法,能够将大模型集成到简单应用中核心内容:主流大模型平台API:OpenAI、Google、阿里云、腾讯云等API调用的请求-响应结构:理解prompt、temperature、top_p等参数的作用API调用的错误处理与性能优化基础应用开发:构建简单的聊天机器人、内容生成工具学习资源:各大平台官方API文档(OpenAI、阿里云PAI等)《Python与大模型API集成实战》(2025年最新版)GitHub上公开的大模型API示例项目实践任务:使用OpenAI API实现一个基础的问答系统通过阿里云PAI部署Stable Diffusion模型并实现本地运行构建一个简单的"向GPT-3.5灌入新知识"应用,理解上下文窗口限制初阶阶段学习总结:通过这一阶段的学习,学习者将对大模型有基本认识,能够理解大模型的"智能"来源,并掌握API调用的基本方法。这为后续更深入的学习奠定了坚实基础。 二、高阶应用阶段(30天):构建私有知识库与对话系统高阶应用阶段的目标是掌握RAG检索增强生成和Agent技术,能够构建私有知识库并开发基于Agent的对话系统。2.1 检索增强生成(RAG)(10天)学习目标:理解RAG技术原理,掌握向量表示和向量数据库应用核心内容:RAG的基本架构:知识库、检索器、LLM生成器向量表示(Embeddings):文本向量化方法、不同嵌入模型的特性向量数据库:Chroma、Milvus、Qdrant等工具的对比与选择检索策略:精确检索、模糊检索、混合检索等RAG系统构建:从数据处理到检索生成的完整流程学习资源:《RAG实战指南:从理论到应用》(2025年最新版)Milvus官方文档《使用Helm安装Milvus群集》LangChain官方教程中的RAG章节实践任务:使用Milvus搭建一个简单的向量数据库通过LangChain实现一个ChatPDF应用,能够检索并生成回答对比不同嵌入模型(如text-embedding-ada-002、BERT)在RAG中的表现2.2 Agent技术(10天)学习目标:理解Agent架构,掌握多Agent协作与工具调用方法核心内容:Agent的基本概念:智能体的定义、目标、状态与行动单Agent与多Agent系统:各自的优缺点与适用场景工具调用机制:如何让Agent执行外部工具(如Python代码、数据库查询)状态跟踪与记忆管理:Agent如何保持上下文信息多Agent协作:角色分工、通信机制与任务分配学习资源:《大模型Agent开发实战》(2025年最新版)实践任务:使用AutoGen框架实现一个简单的多Agent系统构建一个工具调用Agent,能够执行Python代码完成特定任务开发一个基于Agent的对话机器人,能够处理多轮对话和复杂指令2.3 私有知识库构建(10天)学习目标:掌握如何构建和管理私有知识库,支持大模型的个性化应用核心内容:知识库的组织结构:文档存储、元数据管理、索引构建数据预处理:清洗、分块、标注与存储知识注入方法:静态知识库、动态知识更新、实时数据接入知识库安全:权限控制、隐私保护、数据加密知识库优化:检索效率提升、索引结构优化、内存管理学习资源:《大模型私有知识库构建指南》(2025年最新版)《使用Milvus部署Dify》官方文档向量数据库性能优化最佳实践实践任务:使用Milvus构建一个医疗领域的私有知识库开发一个金融领域的知识注入系统,支持实时更新实现一个知识库管理工具,包含搜索、更新和权限控制功能高阶阶段学习总结:通过这一阶段的学习,学习者将能够构建私有知识库,开发基于Agent的对话系统,掌握RAG技术在实际应用中的实现方法。这标志着学习者已经具备开发完整大模型应用系统的能力,可以应对大多数行业应用需求。三、模型训练阶段(30天):掌握Transformer架构与微调技术模型训练阶段的目标是深入理解Transformer架构,掌握模型训练方法,特别是参数高效微调(PEFT)技术。3.1 Transformer架构与实现(10天)学习目标:理解Transformer架构的数学原理,掌握其在代码中的实现核心内容:Transformer架构的数学基础:自注意力机制、位置编码、多头注意力、前馈网络编码器与解码器的实现差异:BERT与GPT架构对比Transformer层的代码实现:PyTorch/TF中的自注意力模块、位置编码、前馈网络模型扩展:层归一化、残差连接、注意力头数与维度调整模型压缩:知识蒸馏、量化、剪枝等技术原理学习资源:《深入理解Transformer架构》(2025年最新版)PyTorch官方文档中的Transformer模块《Hugging Face变压器层实现代码》教程实践任务:使用PyTorch实现一个基础的自注意力机制构建一个完整的Transformer编码器层对比不同位置编码方法(正弦编码、可学习编码)对模型性能的影响3.2 预训练与微调方法(10天)学习目标:理解预训练与微调的原理与实践,掌握数据集构建与训练流程核心内容:预训练与微调的区别:全参数微调vs参数高效微调预训练任务:掩码语言建模(MLM)、下一句预测(NSP)、文本生成等数据集构建:数据收集、清洗、标注与划分训练流程:优化器选择、学习率调度、梯度检查点等评估方法:困惑度(Perplexity)、BLEU、ROUGE等评估指标学习资源:《大模型训练实战指南》(2025年最新版)《Hugging Face预训练任务代码示例》《大模型微调实战教程》实践任务:使用Hugging Face库实现一个掩码语言建模任务构建一个适合特定任务的小型数据集使用PyTorch训练一个简单的文本分类模型3.3 参数高效微调(PEFT)技术(10天)学习目标:掌握PEFT技术的原理与实现,能够高效微调大模型核心内容:PEFT技术分类:LoRA、QLoRA、Adapter Tuning、Prefix Tuning、Prompt Tuning等LoRA原理:低秩矩阵分解、适配器矩阵设计QLoRA特性:量化权重、梯度仅更新低秩适配器PEFT技术对比:参数量、训练速度、效果差异PEFT在垂直领域应用:医疗、金融、教育等领域的微调实践学习资源:PEFT库官方文档与示例代码实践任务:使用LoRA微调一个通用大模型,适配特定任务对比不同PEFT方法在相同任务上的性能与资源消耗使用QLoRA实现一个大模型的轻量化微调模型训练阶段学习总结:通过这一阶段的学习,学习者将能够理解Transformer架构的数学原理,掌握预训练与微调的完整流程,并熟练应用PEFT技术实现高效微调。这标志着学习者已经具备独立训练开源大模型的能力,可以应对特定领域的垂直大模型需求。四、商业闭环阶段(20天):行业应用与部署方案商业闭环阶段的目标是了解大模型在各行业的应用和部署方案,掌握商业化思维和产品设计方法。4.1 行业应用案例(10天)学习目标:了解大模型在不同行业的应用案例,理解其技术实现与商业价值核心内容:医疗领域:智能诊断、药物研发、患者管理(如"终节者"小程序实现肺结节风险评估)金融领域:智能风控、客户服务、交易优化(如工商银行"工银智涌"系统实现风险监控与交易效率提升)制造业:质量检测、供应链优化、生产调度(如微亿智造的工业机器人缺陷检测系统)教育领域:个性化学习、内容生成、智能评估(如语言学习中的实时反馈系统)零售领域:智能推荐、库存管理、客户服务(如基于大模型的个性化购物助手)学习资源:《2025年"人工智能+"行业标杆案例荟萃》《国内AI大模型全景图:15款核心应用深度解析与体验指南》《银行大模型应用"加速跑"数智化竞速开新局》实践任务:分析一个医疗大模型案例的技术实现细节研究一个金融大模型的部署架构与性能指标设计一个制造业大模型应用的初步方案4.2 部署方案与工具对比(5天)学习目标:了解大模型部署的工具与方案,掌握不同部署方式的优缺点核心内容:部署工具对比:vLLM、Docker、Kubernetes等云端部署方案:AWS、Azure、阿里云等云服务商的大模型服务本地部署方案:单机部署、集群部署、硬件加速等私有化部署:数据安全、算法备案、合规要求等混合部署:云端训练、本地推理等混合架构学习资源:《大模型部署工具性能对比最新》《KubeSphere部署向量数据库Milvus实战指南》《从数据到行动:如何将大模型应用于实际业务》实践任务:使用Docker部署一个大模型推理服务在Kubernetes集群上部署一个向量数据库设计一个私有化部署方案,满足特定行业合规要求4.3 商业化思维与产品设计(5天)学习目标:理解大模型商业化的关键因素,掌握产品设计方法论核心内容:商业模式设计:SaaS、API订阅、私有化部署等不同模式的优缺点用户需求分析:如何从用户痛点出发设计大模型应用产品设计流程:从原型到MVP再到规模化部署的完整路径成本优化策略:模型压缩、推理加速、资源调度等价值创造:如何通过大模型提升业务效率、创造新价值学习资源:《AI大模型应用入门实战与进阶:构建你的第一个大模型:实战指南》《AI原生开启金融智能新未来——金融行业大模型应用落地白皮书》《产品设计使用生成对抗网络:整合消费者偏好与外部数据》实践任务:分析一个成功大模型产品的商业模式与盈利策略设计一个大模型应用的产品原型,包含用户界面与核心功能制定一个大模型应用的用户增长与留存策略商业闭环阶段学习总结:通过这一阶段的学习,学习者将能够理解大模型在不同行业的应用场景,掌握部署工具的使用与优化方法,并具备设计商业化大模型产品的思维能力。这标志着学习者已经能够将大模型技术转化为实际商业价值。五、AI大模型从0到精通全套学习大礼包我在一线互联网企业工作十余年里,指导过不少同行后辈。帮助很多人得到了学习和成长。只要你是真心想学AI大模型,我这份资料就可以无偿共享给你学习。大模型行业确实也需要更多的有志之士加入进来,我也真心希望帮助大家学好这门技术,如果日后有什么学习上的问题,欢迎找我交流,有技术上面的问题,我是很愿意去帮助大家的!如果你也想通过学大模型技术去帮助就业和转行,可以扫描下方链接👇👇大模型重磅福利:入门进阶全套104G学习资源包免费分享!01.从入门到精通的全套视频教程包含提示词工程、RAG、Agent等技术点02.AI大模型学习路线图(还有视频解说)全过程AI大模型学习路线03.学习电子书籍和技术文档市面上的大模型书籍确实太多了,这些是我精选出来的04.大模型面试题目详解05.这些资料真的有用吗?这份资料由我和鲁为民博士共同整理,鲁为民博士先后获得了北京清华大学学士和美国加州理工学院博士学位,在包括IEEE Transactions等学术期刊和诸多国际会议上发表了超过50篇学术论文、取得了多项美国和中国发明专利,同时还斩获了吴文俊人工智能科学技术奖。目前我正在和鲁博士共同进行人工智能的研究。所有的视频由智泊AI老师录制,且资料与智泊AI共享,相互补充。这份学习大礼包应该算是现在最全面的大模型学习资料了。资料内容涵盖了从入门到进阶的各类视频教程和实战项目,无论你是小白还是有些技术基础的,这份资料都绝对能帮助你提升薪资待遇,转行大模型岗位。智泊AI始终秉持着“让每个人平等享受到优质教育资源”的育人理念,通过动态追踪大模型开发、数据标注伦理等前沿技术趋势,构建起"前沿课程+智能实训+精准就业"的高效培养体系。课堂上不光教理论,还带着学员做了十多个真实项目。学员要亲自上手搞数据清洗、模型调优这些硬核操作,把课本知识变成真本事!如果说你是以下人群中的其中一类,都可以来智泊AI学习人工智能,找到高薪工作,一次小小的“投资”换来的是终身受益!应届毕业生:无工作经验但想要系统学习AI大模型技术,期待通过实战项目掌握核心技术。零基础转型:非技术背景但关注AI应用场景,计划通过低代码工具实现“AI+行业”跨界。业务赋能 突破瓶颈:传统开发者(Java/前端等)学习Transformer架构与LangChain框架,向AI全栈工程师转型。原文链接:https://blog.csdn.net/2401_85379281/article/details/156228627
-
去年在陕西某县调试系统时,环保站王工指着老旧传感器叹气:“县城就 3 台电脑,跑不动你们的复杂系统。” 那天我们用 Java 把系统核心代码压缩到 8.7MB,去掉冗余功能,只保留 “水质 + 气象” 监测 ——3 天后,这套轻量化系统在暴雨前 2 小时预警山洪,帮 23 户村民提前转移。王工后来在电话里说:“现在看数据像看天气预报,简单明了。” 这个细节让我明白:生态可视化的真谛,不是 “功能多全”,而是 “能不能走进县乡监测站的老旧电脑”。在跟进 13 个案例的日子里,我们见过长三角用 “跨界传输图谱” 协商减排,也见过县城用 “手机端轻量化看板” 巡检河道 —— 这些带着 “泥土味” 的故事,藏着技术落地的真谛。接下来,从县域的 “轻量化生存”,到 AI 的 “扩散预判”,再到长三角的 “跨界联动”,带你看 Java 如何让每一个生态数据,都成为守护城乡的 “千里眼”。一、Java 构建的全层级生态监测系统(含县域轻量化与跨区协同)1.1 县域轻量化监测系统(陕西某县案例)针对县级硬件限制(4 核 CPU+8GB 内存)的 Java 优化方案:/** * 县域生态监测轻量化服务(陕西某县实战) * 核心代码8.7MB,支持3类关键数据,响应≤500ms */@Servicepublic class CountyEnvMonitorService { private final LightweightDataProcessor processor; // 轻量化数据处理器 /** * 县域精简版数据处理(只保留核心监测项) */ public LightweightReport process(CountyEnvData data) { LightweightReport report = new LightweightReport(); // 1. 数据精简:只保留3类关键项(剔除县级用不上的复杂指标) // 水质:溶解氧(≥5mg/L达标)、pH值(6-9) // 气象:降雨量(≥50mm/24h预警) report.setWaterQuality(simplifyWaterData(data.getWaterData())); report.setWeather(simplifyWeatherData(data.getWeatherData())); // 2. 移动端适配:生成手机可直接查看的图文报告 report.setMobileView(generateMobileView(report)); // 3. 本地缓存:断网时保存24小时数据(县域网络不稳定适配) processor.cacheLocally(report, 86400); // 缓存24小时 return report; } /** * 手机端轻量化视图(大字体+红黄绿标识) */ private String generateMobileView(LightweightReport report) { // 例:"溶解氧:4.2mg/L 🔴(低于5mg/L);今日降雨:35mm 🟢" return String.format("溶解氧:%.1fmg/L %s;今日降雨:%dmm %s", report.getWaterQuality().getDoValue(), getColorMark(report.getWaterQuality().getDoValue(), 5.0), report.getWeather().getRainfall(), getColorMark(report.getWeather().getRainfall(), 50.0) ); }}AI写代码java运行县域优化效果(陕西某县 1 年数据):指标 优化前(传统系统) Java 轻量化系统 县域收益服务器占用率 92% 31% 适配老旧硬件监测覆盖率 52% 98% 新增 17 个监测点应急响应时间 8.7 小时 1.2 小时 提前转移 23 户村民基层人员操作难度 高(需培训 1 个月) 低(1 天上手) 王工:“像用微信一样简单”1.2 AI 污染扩散预测模型(Java 实现 LSTM)长三角某区域的 PM2.5 扩散预测,Java 代码实现:/** * 污染扩散AI预测服务(长三角实战) * LSTM模型预测PM2.5扩散,误差≤0.8公里 */@Servicepublic class PollutionDiffusionService { private final LSTMModel lstmModel; // 已用3年数据训练的模型 /** * 预测PM2.5未来6小时扩散路径 */ public DiffusionPrediction predict(Pm25Data currentData) { // 1. 数据预处理(只保留关键特征:浓度、风速、风向、温度) double[][] features = preprocess(currentData); // 2. LSTM预测(每小时输出一次位置) List<Coordinate> path = lstmModel.predict(features, 6); // 6小时预测 // 3. 可视化路径生成(标红超标区域,符合《环境空气质量标准》) return new DiffusionPrediction(path, markOverstandardArea(path)); } /** * 标记超标区域(PM2.5>75μg/m³为超标) */ private List<Coordinate> markOverstandardArea(List<Coordinate> path) { List<Coordinate> overAreas = new ArrayList<>(); for (Coordinate c : path) { if (c.getPm25Value() > 75) { overAreas.add(c); } } return overAreas; }}AI写代码java运行AI 预测效果(长三角 50 次验证):6 小时扩散路径误差:≤0.8 公里(传统模型 2.3 公里)超标区域预判准确率:89%应急决策提前:14 小时(如某次跨界传输预警)1.3 长三角跨界生态数据协同(16 市联动)Java 实现的跨区域数据同步与可视化:协同效果:跨界污染协商时间:从 3 天缩至 4 小时联合减排执行率:从 62% 提升至 91%2023 年长三角重污染天数:比 2022 年减少 21 天二、Java 驱动的全场景决策支持(含移动端与 AI 预警)2.1 移动端轻量化巡检系统(县城河道巡检案例)某县城用 Java 开发的手机端看板:功能:实时显示河道 pH 值、沿岸噪声(大字体 + 红黄绿标识)操作:拍照上传异常点,系统自动定位经纬度效果:巡检效率提升 2.3 倍,漏检率从 31% 降至 4%2.2 不同层级城市的应用差异城市层级 核心功能 Java 技术适配 生态效果县域 手机端巡检 + 3 类数据监测 轻量化代码 + 本地缓存 山洪预警提前 2 小时地级市 多源融合 + AI 扩散预测 分布式处理 + LSTM 模型 污染控制面积扩 1.8 倍城市群 跨界传输图谱 + 协同决策 跨区数据网关 + 共享看板 联合减排效率升 91%三、实战案例:从县城到城市群的生态守护3.1 陕西某县:轻量化系统的山洪预警痛点:硬件老旧,监测覆盖率 52%,山洪预警滞后Java 方案:8.7MB 轻量化系统 + 手机端看板,保留核心数据结果:覆盖率 98%,暴雨前 2 小时预警,转移 23 户村民3.2 长三角:跨界传输图谱的协同减排痛点:PM2.5 跨界传输责任难划,协商低效方案:Java 跨区网关 + 扩散预测,生成 “传输路径图”结果:重污染天数减 21 天,联合减排执行率 91%原文链接:https://blog.csdn.net/atgfg/article/details/155643096
-
去年在陕西某县调试系统时,环保站王工指着老旧传感器叹气:“县城就 3 台电脑,跑不动你们的复杂系统。” 那天我们用 Java 把系统核心代码压缩到 8.7MB,去掉冗余功能,只保留 “水质 + 气象” 监测 ——3 天后,这套轻量化系统在暴雨前 2 小时预警山洪,帮 23 户村民提前转移。王工后来在电话里说:“现在看数据像看天气预报,简单明了。” 这个细节让我明白:生态可视化的真谛,不是 “功能多全”,而是 “能不能走进县乡监测站的老旧电脑”。在跟进 13 个案例的日子里,我们见过长三角用 “跨界传输图谱” 协商减排,也见过县城用 “手机端轻量化看板” 巡检河道 —— 这些带着 “泥土味” 的故事,藏着技术落地的真谛。接下来,从县域的 “轻量化生存”,到 AI 的 “扩散预判”,再到长三角的 “跨界联动”,带你看 Java 如何让每一个生态数据,都成为守护城乡的 “千里眼”。一、Java 构建的全层级生态监测系统(含县域轻量化与跨区协同)1.1 县域轻量化监测系统(陕西某县案例)针对县级硬件限制(4 核 CPU+8GB 内存)的 Java 优化方案:/** * 县域生态监测轻量化服务(陕西某县实战) * 核心代码8.7MB,支持3类关键数据,响应≤500ms */@Servicepublic class CountyEnvMonitorService { private final LightweightDataProcessor processor; // 轻量化数据处理器 /** * 县域精简版数据处理(只保留核心监测项) */ public LightweightReport process(CountyEnvData data) { LightweightReport report = new LightweightReport(); // 1. 数据精简:只保留3类关键项(剔除县级用不上的复杂指标) // 水质:溶解氧(≥5mg/L达标)、pH值(6-9) // 气象:降雨量(≥50mm/24h预警) report.setWaterQuality(simplifyWaterData(data.getWaterData())); report.setWeather(simplifyWeatherData(data.getWeatherData())); // 2. 移动端适配:生成手机可直接查看的图文报告 report.setMobileView(generateMobileView(report)); // 3. 本地缓存:断网时保存24小时数据(县域网络不稳定适配) processor.cacheLocally(report, 86400); // 缓存24小时 return report; } /** * 手机端轻量化视图(大字体+红黄绿标识) */ private String generateMobileView(LightweightReport report) { // 例:"溶解氧:4.2mg/L 🔴(低于5mg/L);今日降雨:35mm 🟢" return String.format("溶解氧:%.1fmg/L %s;今日降雨:%dmm %s", report.getWaterQuality().getDoValue(), getColorMark(report.getWaterQuality().getDoValue(), 5.0), report.getWeather().getRainfall(), getColorMark(report.getWeather().getRainfall(), 50.0) ); }}AI写代码java运行县域优化效果(陕西某县 1 年数据):指标 优化前(传统系统) Java 轻量化系统 县域收益服务器占用率 92% 31% 适配老旧硬件监测覆盖率 52% 98% 新增 17 个监测点应急响应时间 8.7 小时 1.2 小时 提前转移 23 户村民基层人员操作难度 高(需培训 1 个月) 低(1 天上手) 王工:“像用微信一样简单”1.2 AI 污染扩散预测模型(Java 实现 LSTM)长三角某区域的 PM2.5 扩散预测,Java 代码实现:/** * 污染扩散AI预测服务(长三角实战) * LSTM模型预测PM2.5扩散,误差≤0.8公里 */@Servicepublic class PollutionDiffusionService { private final LSTMModel lstmModel; // 已用3年数据训练的模型 /** * 预测PM2.5未来6小时扩散路径 */ public DiffusionPrediction predict(Pm25Data currentData) { // 1. 数据预处理(只保留关键特征:浓度、风速、风向、温度) double[][] features = preprocess(currentData); // 2. LSTM预测(每小时输出一次位置) List<Coordinate> path = lstmModel.predict(features, 6); // 6小时预测 // 3. 可视化路径生成(标红超标区域,符合《环境空气质量标准》) return new DiffusionPrediction(path, markOverstandardArea(path)); } /** * 标记超标区域(PM2.5>75μg/m³为超标) */ private List<Coordinate> markOverstandardArea(List<Coordinate> path) { List<Coordinate> overAreas = new ArrayList<>(); for (Coordinate c : path) { if (c.getPm25Value() > 75) { overAreas.add(c); } } return overAreas; }}AI写代码java运行AI 预测效果(长三角 50 次验证):6 小时扩散路径误差:≤0.8 公里(传统模型 2.3 公里)超标区域预判准确率:89%应急决策提前:14 小时(如某次跨界传输预警)1.3 长三角跨界生态数据协同(16 市联动)Java 实现的跨区域数据同步与可视化:协同效果:跨界污染协商时间:从 3 天缩至 4 小时联合减排执行率:从 62% 提升至 91%2023 年长三角重污染天数:比 2022 年减少 21 天二、Java 驱动的全场景决策支持(含移动端与 AI 预警)2.1 移动端轻量化巡检系统(县城河道巡检案例)某县城用 Java 开发的手机端看板:功能:实时显示河道 pH 值、沿岸噪声(大字体 + 红黄绿标识)操作:拍照上传异常点,系统自动定位经纬度效果:巡检效率提升 2.3 倍,漏检率从 31% 降至 4%2.2 不同层级城市的应用差异城市层级 核心功能 Java 技术适配 生态效果县域 手机端巡检 + 3 类数据监测 轻量化代码 + 本地缓存 山洪预警提前 2 小时地级市 多源融合 + AI 扩散预测 分布式处理 + LSTM 模型 污染控制面积扩 1.8 倍城市群 跨界传输图谱 + 协同决策 跨区数据网关 + 共享看板 联合减排效率升 91%三、实战案例:从县城到城市群的生态守护3.1 陕西某县:轻量化系统的山洪预警痛点:硬件老旧,监测覆盖率 52%,山洪预警滞后Java 方案:8.7MB 轻量化系统 + 手机端看板,保留核心数据结果:覆盖率 98%,暴雨前 2 小时预警,转移 23 户村民3.2 长三角:跨界传输图谱的协同减排痛点:PM2.5 跨界传输责任难划,协商低效方案:Java 跨区网关 + 扩散预测,生成 “传输路径图”结果:重污染天数减 21 天,联合减排执行率 91%原文链接:https://blog.csdn.net/atgfg/article/details/155643096
-
1.概述Redis是一个基于内存的数据库,这意味着其主要数据存储和操作均在内存中进行。这种设计使得Redis能够提供极快的读写速度(通常达到微秒级别),适用于高性能场景,如缓存然而,由于内存的易失性(断电后数据会丢失),Redis提供了持久化机制:将内存中的数据保存到磁盘中,确保数据在Redis服务重启或崩溃后能够恢复。通过持久化,可以避免数据丢失,提高数据的可靠性Redis提供两种持久化方式RDB(Redis Database):生成数据集的快照实现持久化AOF(Append Only File):记录所有写操作命令,以追加方式写入文件2.RDBRDB指的是Redis的一种持久化机制,其核心是生成Redis数据在某个时间点的快照2.1 快照原理由于Redis是单线程应用程序,在线上环境时,不仅要处理来自客户端的请求,还要执行内存快照操作(进行文件IO)。单线程同时处理客户端请求和文件IO时会严重降低服务器性能,甚至阻塞客户端请求。因此,Redis使用 fork 和 写实拷贝(Copy On Write) 机制来实现快照持久化fork Redis在进行RDB持久化时会调用fork函数来创建一个子进程负责完成,父进程则继续处理客户端请求。子进程在创建之初和父进程共享同一数据段 Linux操作系统的内存空间被分为很多种片段,每个片段又被分为很多个页面,每个页面4KB写实拷贝 当父进程对数据段中的某一数据页面进行修改操作时,Linux操作系统会将该数据页面复制一份分离出来,然后对该页面进行修改,最后父进程指向指向修改后的页面。随着被修改的页面越来越多,内存空间不断膨胀,最多达到原来的两倍 从子进程被创建出来的那一刻起,直至拷贝结束,子进程始终指向原始的数据段且所有原数据段不会被修改。所以,在整个拷贝过程中 RDB快照 = 子进程看到的所有数据页面的瞬间状态集合 拷贝完成后,子进程会被销毁,同时没有指针指向的数据页面也会被销毁2.2 触发机制Redis RDB的触发机制分为自动触发和手动触发两种方式自动触发在redis.conf中通过save指令配置阈值。当在指定时间内发生足够数量的键修改时自动触发bgsave正常关闭Redis# 默认执行save(阻塞式)> shutdown# 或> shutdown save# 触发流程:1. 停止接受新连接2. 执行save(不是bgsave)3. 保存完成后退出AI写代码bash手动触发save命令:同步阻塞式触发,执行期间Redis服务器不处理任何请求,直到RDB文件创建完成(不推荐)bgsave命令:异步非阻塞式触发,Redis会fork一个子进程执行持久化操作,主进程继续处理请求2.3 文件处理 RDB文件保存在dir配置指定的目录下(默认/var/lib/redis),文件名通过dbfilename配置指定(默认dump.sql) 在RDB备份过程中,fork出的子进程会将内存数据写入临时文件,临时文件默认命名规则为temp-< pid >.rdb,其中< pid >是子进程的进程ID。当子进程完成RDB文件写入后,Redis会用原子性的rename操作将临时文件重命名为正式RDB文件并删除原文件2.4 优缺点优点恢复速度快:RDB是数据的二进制快照,恢复时直接加载到内存备份时对服务影响小:使用bgsave命令时,Redis通过fork子进程在后台保存数据,主进程可以继续处理客户端请求,几乎无阻塞存储高效:RDB 文件使用二进制格式并支持LZF压缩缺点非实时一致性:RDB保存的是某个瞬间的快照,如果保存过程中有大量写入,快照可能不反映完全一致的业务状态可能丢失更多数据:如果Redis意外宕机,从上一次RDB保存到宕机之间的所有数据修改都会丢失3.AOFAOF持久化通过将Redis服务器接收到的每个写命令追加到文件末尾来实现# 开启AOFappendonly yesAI写代码bash3.1 工作流程命令追加:当Redis执行写命令时,该命令会以Redis协议格式追加到内存中的AOF缓冲区(aof_buf)。缓冲区会根据配置策略决定何时将内容同步到磁盘文件写入与同步:AOF缓冲区内容会被写入到AOF文件,具体同步到磁盘的时机由appendfsync参数控制:always:每次写命令后同步,数据安全性最高但性能影响较大everysec:每秒同步一次,平衡性能与安全性(默认配置)no:由操作系统决定同步时机,性能最好但可能丢失较多数据3.2 重写机制作用:解决AOF文件不断增长导致的存储空间占用和恢复效率问题。通过重写,可以生成一个更紧凑的AOF文件,仅包含重建当前数据集所需的最小命令集合(例如,对同一个键多次修改会记录多条命令,而重写机制会合并这些操作,仅保留最终状态的命令) 父进程通过fork创建一个子进程来完成AOF文件的重写,确保主进程继续处理客户端请求。子进程会读取当前数据库的快照数据,并将其转换为一系列Redis命令写入新的临时AOF文件 在重写过程中,主进程会将新接收到的写命令同时写入现有的AOF 缓冲区aof_buf(保证原有 AOF 文件正常更新)和AOF重写缓冲区aof_rewrite_buf(保证新命令不会丢失) 当子进程完成重写后,会通知主进程。主进程会将 AOF 重写缓冲区中的命令追加到新生成的临时 AOF 文件中,最后原子性地替换旧文件 在Redis7.0.15版本,AOF文件保存在dir + appenddirname配置指定的目录下(默认/var/lib/redis/appendonlydir)。文件前缀名通过appendfilename配置指定(默认appendonly)appendonly.aof.1.base.rdb:作为Redis AOF(Append-Only File)持久化机制的基准文件,存储某一时刻数据库的完整快照。格式为RDB,体积较小且加载速度快,用于重建数据的基础状态appendonly.aof.1.incr.aof和appendonly.aof.2.incr.aof:记录基准文件生成后的增量写操作命令,以文本形式追加存储。多个增量文件按操作顺序编号(如.1.incr.aof、.2.incr.aof),Redis 重启时会按顺序重放这些命令以恢复最新数据appendonly.aof.manifest:描述AOF文件的组成和顺序的清单文件4.混合持久化 Redis 混合持久化结合了 RDB(快照)和 AOF(日志)两种持久化方式的优势,在保证数据安全性的同时兼顾性能# 开启混合持久化aof-use-rdb-preamble yesAI写代码bash基础RDB文件优先加载:appendonly.aof.1.base.rdb作为全量快照数据文件,会优先被加载。该文件包含某一时间点的完整数据快照,恢复时作为基准数据集增量AOF文件后续应用:appendonly.aof.1.incr.aof作为增量操作日志,在基础RDB加载完成后被重放。该文件记录自 RDB 快照生成后的所有写操作,用于恢复最新数据状态原文链接:https://blog.csdn.net/2401_89167985/article/details/147985399
-
1. 引入依赖首先,需要确认项目中是否直接或者间接引入过spring-web依赖,如果没有引入过,需要在pom.xml中添加以下代码引入依赖:<dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>4.3.24.RELEASE</version></dependency>2. 发送GET请求使用RestTemplate发送GET请求主要有getForObject()和getForEntity()2个方法,每个方法分别提供了3种不同的重载。2.1 使用getForObject发送GET请求(无参数)使用getForObject()实现:RestTemplate restTemplate = new RestTemplate();String url = "https://www.example.com/getCurrentEnv";String response = restTemplate.getForObject(url, String.class);System.out.println(response);假设以上接口返回的报文为:{ "serverAddress": "www.example.dev.com", "env": "dev"}也可以直接解析为自定义的类型:import lombok.Getter;import lombok.Setter;@Getter@Setterpublic class EnvInfo { private String serverAddress; private String env;}RestTemplate restTemplate = new RestTemplate();String url = "https://www.example.com/getCurrentEnv";EnvInfo response = restTemplate.getForObject(url, EnvInfo.class);System.out.println(JSON.toJSONString(response));2.2 使用getForEntity发送GET请求(无参数)也可以使用getForEntity()实现和2.1同样的功能,代码如下所示:RestTemplate restTemplate = new RestTemplate();String url = "https://www.example.com/getCurrentEnv";ResponseEntity<EnvInfo> responseEntity = restTemplate.getForEntity(url, EnvInfo.class);if (responseEntity.getStatusCode() == HttpStatus.OK) { EnvInfo response = responseEntity.getBody(); System.out.println(JSON.toJSONString(response));}2.3 使用getForObject发送GET请求(带参数)第一种方法是直接在url上拼接上参数,如下所示:RestTemplate restTemplate = new RestTemplate();String url = "https://www.example.com/getDataList?pageIndex=1&pageSize=20";EnvInfo response = restTemplate.getForObject(url, EnvInfo.class);System.out.println(JSON.toJSONString(response));第二种方法是使用占位符添加参数,如下所示:RestTemplate restTemplate = new RestTemplate();String url = "https://www.example.com/getDataList?pageIndex={1}&pageSize={2}";EnvInfo response = restTemplate.getForObject(url, EnvInfo.class, 1, 20);System.out.println(JSON.toJSONString(response));以上代码也可以替换为:RestTemplate restTemplate = new RestTemplate();String url = "https://www.example.com/getDataList?pageIndex={pageIndex}&pageSize={pageSize}";Map<String, String> uriVariables = new HashMap<>();uriVariables.put("pageIndex", "1");uriVariables.put("pageSize", "20");EnvInfo response = restTemplate.getForObject(url, EnvInfo.class, uriVariables);System.out.println(JSON.toJSONString(response));注意事项:uriVariables中的key必须和url中的占位符名称一致,否则会抛出异常:java.lang.IllegalArgumentException: Map has no value for 'pageIndex'第三种方法是使用UriComponentsBuilder添加参数,该种方法相比于前两种方法更加灵活,可以实现动态添加参数,代码如下所示:RestTemplate restTemplate = new RestTemplate();String httpUrl = "https://www.example.com/getDataList";UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromHttpUrl(httpUrl);uriComponentsBuilder.queryParam("pageIndex", 1);uriComponentsBuilder.queryParam("pageSize", 20);String url = uriComponentsBuilder.toUriString();EnvInfo response = restTemplate.getForObject(url, EnvInfo.class);System.out.println(JSON.toJSONString(response));2.4 使用getForEntity发送GET请求(带参数)也可以使用getForEntity()实现和2.3同样的功能,代码如下所示:RestTemplate restTemplate = new RestTemplate();String httpUrl = "https://www.example.com/getDataList";UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromHttpUrl(httpUrl);uriComponentsBuilder.queryParam("pageIndex", 1);uriComponentsBuilder.queryParam("pageSize", 20);String url = uriComponentsBuilder.toUriString();ResponseEntity<EnvInfo> responseEntity = restTemplate.getForEntity(url, EnvInfo.class);if (responseEntity.getStatusCode() == HttpStatus.OK) { EnvInfo response = responseEntity.getBody(); System.out.println(JSON.toJSONString(response));}2.5 getForObject与getForEntity的区别getForEntity()与getForObject()相比,返回值用了ResponseEntity进行封装,可以多获取到以下2种信息:HTTP状态码Response Headers代码示例:RestTemplate restTemplate = new RestTemplate();String httpUrl = "https://www.example.com/getDataList";UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromHttpUrl(httpUrl);uriComponentsBuilder.queryParam("pageIndex", 1);uriComponentsBuilder.queryParam("pageSize", 20);String url = uriComponentsBuilder.toUriString();ResponseEntity<EnvInfo> responseEntity = restTemplate.getForEntity(url, EnvInfo.class);System.out.println("statusCode: " + responseEntity.getStatusCode().toString());System.out.println("statusCodeValue: " + responseEntity.getStatusCodeValue());responseEntity.getHeaders().forEach((key, values) -> { System.out.println(key + ": " + values);});输出结果:statusCode: 200statusCodeValue: 200Server: [openresty]Date: [Thu, 10 Apr 2025 05:39:02 GMT]Content-Type: [application/json]Transfer-Encoding: [chunked]Connection: [keep-alive]其中Response Headers输出部分和Chrome浏览器Network中的Response Headers是一致的:2.6 发送GET请求(带参数及请求头)一般情况下,请求第三方接口都需要签名、时间戳等请求头,但getForObject()和getForEntity()都不支持,此时需要使用exchange()方法,代码如下所示:RestTemplate restTemplate = new RestTemplate();String httpUrl = "https://www.example.com/getDataList";UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromHttpUrl(httpUrl);uriComponentsBuilder.queryParam("pageIndex", 1);uriComponentsBuilder.queryParam("pageSize", 20);HttpHeaders headers = new HttpHeaders();headers.set("signature", "3045022100875efcef9eb54626bb0168a6baa7c61265d0001d49243f");headers.set("timestamp", String.valueOf(System.currentTimeMillis()));String url = uriComponentsBuilder.toUriString();ResponseEntity<EnvInfo> responseEntity = restTemplate.exchange(url, HttpMethod.GET, new HttpEntity<>(headers), EnvInfo.class);if (responseEntity.getStatusCode() == HttpStatus.OK) { EnvInfo response = responseEntity.getBody(); System.out.println(JSON.toJSONString(response));}3. 发送POST请求使用RestTemplate发送POST请求主要有postForObject()和postForEntity()2个方法,每个方法分别提供了3种不同的重载。3.1 发送POST请求(带参数、json方式)使用postForObject()实现:RestTemplate restTemplate = new RestTemplate();HttpHeaders headers = new HttpHeaders();headers.setContentType(MediaType.APPLICATION_JSON_UTF8);LoginParams loginParams = new LoginParams();loginParams.setUsername("zhangsan");loginParams.setPassword("123456");HttpEntity<LoginParams> request = new HttpEntity<>(loginParams, headers);String url = "https://www.example.com/login";String response = restTemplate.postForObject(url, request, String.class);System.out.println(response);LoginParams的定义如下所示:import lombok.Getter;import lombok.Setter;@Getter@Setterpublic class LoginParams { private String username; private String password;}假设以上接口返回的报文为:{ "code": 200, "expire": "2025-04-11 14:42:22", "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3NDQzNTM3NDIsImlkZW50aXR5"}也可以直接解析为自定义的类型:import lombok.Getter;import lombok.Setter;@Getter@Setterpublic class LoginResponse { private Integer code; private String expire; private String token;}RestTemplate restTemplate = new RestTemplate();HttpHeaders headers = new HttpHeaders();headers.setContentType(MediaType.APPLICATION_JSON_UTF8);LoginParams loginParams = new LoginParams();loginParams.setUsername("zhangsan");loginParams.setPassword("123456");HttpEntity<LoginParams> request = new HttpEntity<>(loginParams, headers);String url = "https://www.example.com/login";LoginResponse response = restTemplate.postForObject(url, request, LoginResponse.class);System.out.println(JSON.toJSONString(response));也可以使用postForEntity()实现同样的功能,代码如下所示:ResponseEntity<LoginResponse> responseEntity = restTemplate.postForEntity(url, request, LoginResponse.class);if (responseEntity.getStatusCode() == HttpStatus.OK) { LoginResponse response = responseEntity.getBody(); System.out.println(JSON.toJSONString(response));}3.2 发送POST请求(带参数、form表单方式)使用postForObject()实现:RestTemplate restTemplate = new RestTemplate();HttpHeaders headers = new HttpHeaders();headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);MultiValueMap<String, String> map = new LinkedMultiValueMap<>();map.add("username", "zhangsan");map.add("password", "123456");HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(map, headers);String url = "https://www.example.com/login";LoginResponse response = restTemplate.postForObject(url, request, LoginResponse.class);System.out.println(JSON.toJSONString(response));也可以使用postForEntity()实现同样的功能,代码如下所示:ResponseEntity<LoginResponse> responseEntity = restTemplate.postForEntity(url, request, LoginResponse.class);if (responseEntity.getStatusCode() == HttpStatus.OK) { LoginResponse response = responseEntity.getBody(); System.out.println(JSON.toJSONString(response));}3.3 postForObject与postForEntity的区别postForObject()与postForEntity()的区别,与getForEntity()与getForObject()的区别一样,返回值用了ResponseEntity进行封装。4. 超时时间设置如果需要自定义HTTP请求的连接超时时间和数据传输超时时间,代码如下所示:import org.springframework.beans.factory.annotation.Value;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.http.client.SimpleClientHttpRequestFactory;import org.springframework.web.client.RestTemplate;@Configurationpublic class RestTemplateConfig { @Value("${restTemplate.connectTimeout:5000}") private int connectTimeout; @Value("${restTemplate.readTimeout:10000}") private int readTimeout; @Bean public RestTemplate restTemplate() { SimpleClientHttpRequestFactory simpleClientHttpRequestFactory = new SimpleClientHttpRequestFactory(); simpleClientHttpRequestFactory.setConnectTimeout(connectTimeout); simpleClientHttpRequestFactory.setReadTimeout(readTimeout); return new RestTemplate(simpleClientHttpRequestFactory); }}转载自https://www.cnblogs.com/zwwhnly/p/18820159
-
1. 前言最近在自测接口时,发现一个问题:字段类型定义的是Date,但接口返回值里却是时间戳(1744959978674),而不是预期的2025-04-18 15:06:18。private Date useTime;{ "code": "200", "message": "", "result": [ { "id": 93817601, "useTime": 1744959978674 } ]}这种返回值,无法快速的知道是哪个时间,如果想知道时间对不对,还得找一个时间戳转换工具做下转换才能确定,非常不方便。因此想让接口直接返回预期的2025-04-18 15:06:18格式。刚开始,在字段上添加了@JsonFormat注解,发现没生效,返回的还是时间戳:import com.fasterxml.jackson.annotation.JsonFormat;@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss")private Date useTime;然后,改成了@JSONField注解,发现生效了,达到了预期的结果:import com.alibaba.fastjson.annotation.JSONField;@JSONField(format = "yyyy-MM-dd HH:mm:ss")private Date useTime;{ "code": "200", "message": "", "result": [ { "id": 93817601, "useTime": "2025-04-18 15:06:18" } ]}那么问题来了,为啥@JSONField生效,@JsonFormat不生效?2. 原因分析默认情况下,Spring Boot使用的JSON消息转换器是Jackson的MappingJackson2HttpMessageConverter,核心依赖为:<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.8.11.3</version></dependency>现在使用Jackson的@JsonFormat注解不生效,说明Spring Boot没有使用默认的MappingJackson2HttpMessageConverter。使用fastjson的@JSONField注解生效了,说明Spring Boot使用的是fastjson下的JSON消息转换器,也就是FastJsonHttpMessageConverter,依赖为:<dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.83</version></dependency>那么怎么找到代码在哪配置的呢?第一步,先在项目中全局搜索FastJsonHttpMessageConverter(Windows快捷键:Ctrl+Shift+F),不过大概率是搜索不到,因为公司里的项目一般都继承自公司公共的xxx-spring-boot-starter。第二步,连按2次Shift键搜索FastJsonHttpMessageConverter,然后查找该类的引用或者子类(子类很可能是公司底层框架中写的)。然后,很可能会找到类似下面的代码:@Configurationpublic class FastJsonMessageConverterConfig { @Bean public HttpMessageConverters customConverters() { FastJsonHttpMessageConverter fastJsonHttpMessageConverter = new FastJsonHttpMessageConverter(); return new HttpMessageConverters(new HttpMessageConverter[]{fastJsonHttpMessageConverter}); }}以上代码显式注册了一个FastJsonHttpMessageConverter,并通过HttpMessageConverters覆盖了默认的HTTP 消息转换器(Jackson的MappingJackson2HttpMessageConverter),所以Spring MVC将只使用fastjson处理JSON序列化/反序列化。这也是@JSONField生效,@JsonFormat不生效的根本原因。3. 默认行为及全局配置fastjson 1.2.36及以上版本,默认将日期序列化为时间戳(如1744959978674),如果要默认将日期序列化为yyyy-MM-dd HH:mm:ss(如2025-04-18 15:06:18),需要启用WriteDateUseDateFormat特性:@Configurationpublic class FastJsonMessageConverterConfig { @Bean public HttpMessageConverters customConverters() { FastJsonConfig fastJsonConfig = new FastJsonConfig(); // 启用日期格式化特性(禁用时间戳) fastJsonConfig.setSerializerFeatures(SerializerFeature.WriteDateUseDateFormat); // 设置日期格式(不指定时,默认为yyyy-MM-dd HH:mm:ss,但即使与默认值一致,也建议明确指定) fastJsonConfig.setDateFormat("yyyy-MM-dd HH:mm:ss"); FastJsonHttpMessageConverter fastJsonHttpMessageConverter = new FastJsonHttpMessageConverter(); fastJsonHttpMessageConverter.setFastJsonConfig(fastJsonConfig); return new HttpMessageConverters(new HttpMessageConverter[]{fastJsonHttpMessageConverter}); }}如果某个日期字段有特殊序列化要求,可以使用@JSONField注解灵活配置(该注解会覆盖全局配置):import com.alibaba.fastjson.annotation.JSONField;@JSONField(format = "yyyy-MM-dd")private Date anotherUseTime;注意事项:修改全局配置需慎重,如果一个老项目,原来日期类型返回的都是时间戳,突然全部改为返回字符串,可能会造成调用方报错。转载自https://www.cnblogs.com/zwwhnly/p/18838062
-
1. 踩坑经历假设有这样一个业务场景,需要对各个城市的订单量排序,排序规则为:先根据订单量倒序排列,再根据城市名称正序排列。示例代码:import lombok.Getter;import lombok.Setter;import lombok.ToString;@Getter@Setter@ToStringpublic class OrderStatisticsInfo { private String cityName; private Integer orderCount; public OrderStatisticsInfo(String cityName, Integer orderCount) { this.cityName = cityName; this.orderCount = orderCount; }}public static void main(String[] args) { List<OrderStatisticsInfo> orderStatisticsInfoList = Arrays.asList( new OrderStatisticsInfo("上海", 1000), new OrderStatisticsInfo("北京", 1000), new OrderStatisticsInfo("成都", 700), new OrderStatisticsInfo("常州", 700), new OrderStatisticsInfo("广州", 900), new OrderStatisticsInfo("深圳", 800) ); orderStatisticsInfoList.sort(Comparator.comparing(OrderStatisticsInfo::getOrderCount, Comparator.reverseOrder()) .thenComparing(OrderStatisticsInfo::getCityName)); orderStatisticsInfoList.forEach(System.out::println);}预期结果:北京 1000上海 1000广州 900深圳 800常州 700成都 700实际结果:OrderStatisticsInfo(cityName=上海, orderCount=1000)OrderStatisticsInfo(cityName=北京, orderCount=1000)OrderStatisticsInfo(cityName=广州, orderCount=900)OrderStatisticsInfo(cityName=深圳, orderCount=800)OrderStatisticsInfo(cityName=常州, orderCount=700)OrderStatisticsInfo(cityName=成都, orderCount=700)从以上结果可以看出,根据订单量倒序排列没啥问题,但根据城市名称正序排列不符合预期:上海竟然排到了北京的前面,但常州与成都的顺序又是对的。2. 原因分析Comparator.comparing对字符串类型进行排序时,默认使用的是字符串的自然排序,即String的compareTo方法,该方法是基于Unicode编码值进行比较的,未考虑语言特定的字符顺序(如中文拼音)。先看下String的compareTo方法的源码:public int compareTo(String anotherString) { int len1 = value.length; int len2 = anotherString.value.length; int lim = Math.min(len1, len2); char v1[] = value; char v2[] = anotherString.value; int k = 0; while (k < lim) { char c1 = v1[k]; char c2 = v2[k]; if (c1 != c2) { return c1 - c2; } k++; } return len1 - len2;}以上海与北京的比较为例,先比较第一个字符,也就是字符上和字符北,字符上对应的Unicode编码值是19978,因此c1 = 19978,字符北对应的Unicode编码值是21271,因此c2 = 21271,因为c1 != c2,所以返回值为-1293,也就是说上海小于北京(要排在北京的前面),不符合预期。以常州与成都的比较为例,先比较第一个字符,也就是字符常和字符成,字符常对应的Unicode编码值是24120,因此c1 = 24120,字符成对应的Unicode编码值是25104,因此c2 = 25104,因为c1 != c2,所以返回值为-984,也就是说常州小于成都(要排在成都的前面),符合预期。可以通过Character.codePointAt方法获取字符的Unicode编码值:// 输出:19978System.out.println(Character.codePointAt("上海", 0));// 输出:21271System.out.println(Character.codePointAt("北京", 0));// 输出:24120System.out.println(Character.codePointAt("常州", 0));// 输出:25104System.out.println(Character.codePointAt("成都", 0));3. 解决方案Java提供了本地化的排序规则,可以按特定语言规则排序(如中文拼音),代码如下所示:orderStatisticsInfoList.sort(Comparator.comparing(OrderStatisticsInfo::getOrderCount, Comparator.reverseOrder()) .thenComparing(OrderStatisticsInfo::getCityName, Collator.getInstance(Locale.CHINA)));orderStatisticsInfoList.forEach(System.out::println);此时的输出结果为:OrderStatisticsInfo(cityName=北京, orderCount=1000)OrderStatisticsInfo(cityName=上海, orderCount=1000)OrderStatisticsInfo(cityName=广州, orderCount=900)OrderStatisticsInfo(cityName=深圳, orderCount=800)OrderStatisticsInfo(cityName=常州, orderCount=700)OrderStatisticsInfo(cityName=成都, orderCount=700)可以看到,北京排到了上海的前面,符合预期。上述代码指定了Collator.getInstance(Locale.CHINA),在排序比较时不再执行String的compareTo方法,而是执行Collator的compare方法,实际上是RuleBasedCollator的compare方法。可以执行以下代码单独看下上海与北京的比较结果:Collator collator = Collator.getInstance(Locale.CHINA);// 输出:1,代表上海大于北京,也就是要排在北京的后面System.out.println(collator.compare("上海", "北京"));转载自https://www.cnblogs.com/zwwhnly/p/18846441
上滑加载中
推荐直播
-
HDC深度解读系列 - Serverless与MCP融合创新,构建AI应用全新智能中枢2025/08/20 周三 16:30-18:00
张昆鹏 HCDG北京核心组代表
HDC2025期间,华为云展示了Serverless与MCP融合创新的解决方案,本期访谈直播,由华为云开发者专家(HCDE)兼华为云开发者社区组织HCDG北京核心组代表张鹏先生主持,华为云PaaS服务产品部 Serverless总监Ewen为大家深度解读华为云Serverless与MCP如何融合构建AI应用全新智能中枢
回顾中 -
关于RISC-V生态发展的思考2025/09/02 周二 17:00-18:00
中国科学院计算技术研究所副所长包云岗教授
中科院包云岗老师将在本次直播中,探讨处理器生态的关键要素及其联系,分享过去几年推动RISC-V生态建设实践过程中的经验与教训。
回顾中 -
一键搞定华为云万级资源,3步轻松管理企业成本2025/09/09 周二 15:00-16:00
阿言 华为云交易产品经理
本直播重点介绍如何一键续费万级资源,3步轻松管理成本,帮助提升日常管理效率!
回顾中
热门标签