1、概览 本文将带你了解如何使用 Spring AI 通过 OpenAI 聊天模型从图像中提取结构化数据。
OpenAI 聊天模型可以分析上传的图像并返回相关信息。它还能返回结构化的输出结果,这些输出结果可以很容易地通过管道输送到其他应用程序,以进行进一步操作。
举例说明,我们要创建一个 Web 服务,接收客户端上传的图像并将其发送给 OpenAI,以计算图像中彩色汽车的数量。Web 服务会以 JSON 格式返回颜色数量。
2、Spring Boot 配置 在 Maven pom.xml 中添加以下 Spring Boot Start Web 和 Spring AI Model OpenAI 依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>3.4.1</version> </dependency> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-openai-spring-boot-starter</artifactId> <version>1.0.0-M6</version> </dependency> 在 Spring Boot application.yml 文件中,必须要提供用于验证 OpenAI API 的 API 密钥(spring.ai.openai.api-key)和能够执行图像分析的聊天模型(spring.ai.openai.chat.options.model)的配置。
有多种支持图像分析的 模型,如 gpt-4o-mini、gpt-4o 和 gpt-4.5-preview。像 gpt-4o 这样的大模型知识面较广,但成本较高,而像 gpt-4o-mini 这样的小型模型成本较低,延迟也较小。我们可以根据自己的需求来选择型号。
本文选择使用 gpt-4o 模型:
spring: ai: openai: api-key: "<YOUR-API-KEY>" chat: options: model: "gpt-4o" 配置完毕后,Spring Boot 就会自动加载 OpenAiAutoConfiguration 以注册 ChatClient 等 Bean。
1、简介 有时,我们需要在后台运行 Java 程序,即在 SSH 终端关闭后还能继续运行。
2、将 Java 作为后台进程运行 在后台运行 Java 程序的最简单方法之一是在 shell 脚本中使用 & 操作符:
#!/bin/sh java -jar /web/server.jar & echo $! > startupApp.pid 最后的 & 可确保进程在后台运行,而 echo $! > startupApp.pid 的作用是获取最后执行的后台命令的 PID(进程 ID),并且重定向输出到 startupApp.pid 文件中。该 PID 可唯一标识运行中的进程,以后可用于进程管理任务,如监控或停止进程。
不过,这种方法有一个缺点。如果我们断开了 SSH 会话,进程仍可能被终止,这会导致后台程序意外停止。
此外,由于进程不是作为服务来管理的,因此更难对其进行正确控制。我们无法像使用 systemd 等正规服务管理器那样轻松查看日志或启动/停止进程。
3、利用 Nohup 保活进程 如果想在断开 SSH 会话后还让后台进程继续执行,那么 nohup 就可以解决这个问题。nohup(不挂起)可以让进程在关闭会话或终端后继续运行。
nohup 用法如下:
nohup java -jar /web/server.jar > output.log 2>&1 & echo $! > startupApp.pid nohup 会让进程继续运行,即使关闭了终端会话。> output.log 2>&1 表示将标准输出和错误输出重定向到一个名为 output.
🐞 Bug 修复 由于目录权限问题,使用原生镜像容器映像在 podman 上构建 Spring Boot 失败 #45256 Neo4jReactiveDataAutoConfiguration 假定某些 Bean 可用 #45235 使用空数据库名称时出现错误的 jOOQ 异常翻译 #45219 MessageSourceMessageInterpolator 不会在 message 与其 code 匹配时替换参数 #45213 IntegrationMbeanExporter 不符合由所有 BeanPostProcessors 处理的条件,使用 JMX 时会显示警告 #45194 OAuth2AuthorizationServerJwtAutoConfiguration 错误地使用了 @ConditionalOnClass #45178 MongoDB 的依赖管理缺少 Kotlin coroutine 驱动模块 #45159 ImagePlatform 可能导致 “OS must not be empty” 的 IllegalArgumentException #45153 TypeUtils 无法处理不同位置上名称相同的泛型 #45039 HttpClient 5 5.4.3 会中断本地 Docker transport #45028 不能使用 spring.datasource.hikari.data-source-class-name,因为驱动程序类名总是必需的,而 Hikari 不同时接受这两个名称。 #45002 如果 JDBC URL 用于未知驱动程序,则应用自定义 JdbcConnectionDetails 的后处理会在 Hikari 中触发 NPE #44998 在 Hikari 中,当尝试使用未知驱动程序的 JDBC URL 构建数据源时,DataSourceBuilder 会触发 NPE。 #44995 SSL 配置不监控链接文件的更改 #44887 EmbeddedLdapAutoConfiguration 不应依赖于 PreDestroy #44874 DataSourceTransactionManagerAutoConfiguration 应在 DataSourceAutoConfiguration 之后运行 #44819 JsonValueWriter 会对深度嵌套的项目抛出 StackOverflowError 错误#44627 在响应式 web 应用中,如果不使用 ‘file:’ 前缀,SslBundle 将无法打开 store 文件位置 #44535 使用结构化日志记录 Path 对象抛出 StackOverflowError #44507 📔 文档 将 @Component 设为 javadoc 链接 #45258 修复指向 buildpacks.
1、简介 本文将带你了解 @EnableEurekaClient 与 @EnableDiscoveryClient 的区别。
这俩注解在 Spring Cloud 微服务应用中,都用于客户端发现。
2、微服务中的服务注册 在以分布式和松散耦合为特点的微服务领域,随着服务数量的增加,维护准确的服务列表并确保它们之间的无缝通信变得越来越复杂。手动跟踪服务的健康状况和可用性是一项资源密集且容易出错的工作。这时,服务注册中心就变得非常有用。
服务注册中心是一个保存可用服务信息的数据库。它是服务注册、查询和管理的中心点。
当微服务启动时,它会在服务注册中心中注册自己。
3、@EnableDiscoveryClient 注解 @EnableDiscoveryClient 是 Spring Boot 提供的一种更通用的注解,使其成为服务发现的灵活选择。
可以将其视为一种约定,它允许我们的应用现在和将来与不同的服务注册中心协同工作。它包含在 spring-cloud-commons 依赖中,成为 Spring Cloud 服务发现机制的核心部分。
但是,它并不能独立工作,实际运行时需要真正的实现,如 Eureka、Consul 或 Zookeeper。
4、@EnableEurekaClient 注解 Eureka 最初由 Netflix 开发,提供了一个强大的服务发现解决方案。Eureka 客户端是 Eureka 的基本组成部分。
具体来说,应用可以作为客户端,在使用 Eureka 实现的服务注册中心注册。此外,Spring Cloud 还为其提供了一个抽象层,与 Spring Boot 结合使用,可大大简化 Eureka 与微服务架构的集成,从而提高开发人员的工作效率。
@EnableEurekaClient 注解在这一过程中起着至关重要的作用。更确切地说,它是作为客户端的服务向服务注册中心注册时使用的注解之一。
4.1、实现 Eureka Client 只有当 pom.xml 中包含 Eureka 客户端依赖 时,Eureka 客户端才能工作:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> 还要定义 spring-cloud-commons <dependencyManagement>:
<dependencyManagement> <dependencies> <dependency> <groupId>org.
功能特点 简化多表操作:MyBatis-Plus-Join 是 MyBatis-Plus 的扩展,让开发者能更便捷地进行多表关联查询、子查询等操作,通过简洁的API满足复杂查询需求。例如,在进行多表联合查询时,无需编写大量复杂的SQL语句或配置文件,只需简单配置和调用相关方法,即可实现多表数据的关联查询。 支持多种特性:支持列枚举、别名、逻辑删除、TypeHandle、一对一、一对多等功能,还支持 Lambda 和字符串两种查询方式,以及自定义字段映射和结果转换、动态条件构造和链式调用等高级特性。比如,在进行数据查询时,可以根据不同的条件动态构造查询语句,灵活地实现各种复杂的查询逻辑。 兼容性好:作为 MyBatis-Plus 的插件,它完全兼容 MyBatis-Plus 的使用习惯,引入后不会对现有工程产生影响,能与 MyBatis-Plus 原有功能无缝集成。 依赖配置 以Maven项目为例,在pom.xml文件中添加以下依赖即可使用:
<dependency> <groupId>com.github.yulichang</groupId> <artifactId>mybatis-plus-join-boot-starter</artifactId> <version>1.5.3</version> </dependency> 最新版 MyBatis-Plus-Join 需要配合 MyBatis-Plus 3.1.2 及以上的版本使用。
代码示例 假设存在用户表 user 和订单表 order,它们之间通过 user_id 关联,以下是使用 mybatis-plus-join-boot-starter 进行多表查询的示例:
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.github.yulichang.base.MPJBaseMapper; import com.github.yulichang.wrapper.MPJLambdaWrapper; import com.example.demo.entity.User; import com.example.demo.entity.Order; import org.springframework.stereotype.Repository; import javax.annotation.Resource; import java.util.List; @Repository public class UserRepository { @Resource private MPJBaseMapper<User> userMapper; public List<User> getUserWithOrders() { // 使用MPJLambdaWrapper进行多表查询 MPJLambdaWrapper<User> wrapper = new MPJLambdaWrapper<User>() .
TL;DR; 今天,官方宣布今后将不再以开源项目的形式维护 Spring Cloud Data Flow、Spring Cloud Deployer 或 Spring Statemachine。Spring Cloud Data Flow 2.11.x、Spring Cloud Deployer 2.9.x 和 Spring Statemachine 4.0.x 将是最后一批开源项目,今后的版本将仅提供给 Tanzu Spring 客户。这一变更不会影响 Spring 开源产品组合的其他部分,也不会影响现有用户对当前可用开放源码软件版本的支持义务。
Spring Cloud Data Flow 起源于八年前的 Spring XD,用于协调批处理和流工作负载,多年来在客户中取得了巨大成功。然而,为了让 Spring Cloud Data Flow 和相关生态系统项目在未来以可持续的方式发展下去,我们决定只将 Spring Cloud Data Flow 作为商业产品发布。
这一决定并不是轻易做出的,其主要原因是这些项目在我们的社区中得到了广泛采用。我们所看到的 Spring Cloud Data Flow 的绝大多数使用情况都发生在我们的 Tanzu 企业客户中。目前,开源应用只占总体应用的很小一部分,而社区提供的维护贡献同样很小。在过去的两年中,Spring Cloud Data Flow 的维护工作几乎完全由 Tanzu 的研发团队负责,而 Spring 产品组合中的绝大多数产品则由充满活力的社区以多种方式进行维护。Spring Statemachine 和 Spring Cloud Deployer 的采用模式与 Spring Cloud Data Flow 相似,它们的使用主要是由于 Spring Cloud Data Flow 中包含了 Spring Statemachine 和 Spring Cloud Deployer。
1、简介 WebClient 是 Spring WebFlux 中的一个 HTTP 客户端工具类,可以实现同步和异步 HTTP 请求。
本文将带你了解在 Spring WebClient 中设置 Header 的几种方式。
2、WebClient 如何处理 Header? 一般来说,HTTP 请求中的 Header 起到元数据的作用。它们包含认证详细信息、内容类型、版本等信息。
在 WebClient 中,HttpHeaders 类负责管理 Header。这是一个 Spring 框架类,专门用于表示请求和响应头。该类实现了 MultiValueMap<String, String>,允许一个 Header Key 有多个 Value。
这为需要多个值的 Header(如 Accept)提供了灵活性。
3、在 WebClient 中设置 Header 有几种方法可以为请求添加 Header。根据使用情况,我们可以为单个请求设置 Header,为整个 WebClient 实例定义全局 Header,或动态修改 Header。
3.1、为单个请求设置 Header 如果 Header 是针对单个请求的,并且因端点而异,那么直接的方法就是在请求中直接设置它们。
一个简单的示例如下。例化 WebClient,在请求中添加了两个 Header,并断言这些 Header 已通过请求发送。使用 okhttp3 库中的 MockWebServer 来模拟服务器响应并验证 WebClient 的行为:
@Test public void givenRequestWithHeaders_whenSendingRequest_thenAssertHeadersAreSent() throws Exception { mockWebServer.
1、概览 默认情况下,Spring Boot 提供嵌入式 Tomcat 服务器,但在某些情况下,我们可能希望根据应用的需求来禁用/启用它。
对于不需要 Web 服务的 Spring Boot 应用,禁用 Tomcat 可以节省资源。
2、理解 Spring Boot 中的嵌入式 Tomcat Spring Boot 在应用的可执行 JAR 文件中捆绑了嵌入式 Tomcat 服务器,从而简化了应用的部署。这种方法消除了安装和配置外部 Tomcat 实例的需要,使开发和部署更加高效。
Spring Boot 使用 Spring Boot Starter 来包含嵌入式 Tomcat 的必要依赖。默认情况下, spring-boot-starter-web Starter 会在 Tomcat 出现在 classpath 中时自动配置和初始化 Tomcat。
2.1、嵌入式 Tomcat 的优势 Spring Boot 的嵌入式 Tomcat 服务器具有多种优势:
简化部署:无需安装外部 Tomcat 服务器。 独立的应用:应用程序可打包为 JAR 文件,在任何地方运行。 自动配置:Spring Boot 根据依赖自动配置 Tomcat。 灵活:可轻松替换为 Jetty 或 Undertow 等其他嵌入式服务器。 2.2、为什么要禁用 Tomcat 服务器? 虽然嵌入式 Tomcat 很有用,但在某些情况下,禁用它对我们也有好处:
1、概览 在 Java 中使用 java.time 包处理日期和时间非常高效,但有时我们可能会遇到 DateTimeParseException 异常,提示 “Unable to obtain LocalDateTime from TemporalAccessor(无法从 TemporalAccessor 获取 LocalDateTime)”。出现这种问题的原因通常是预期的日期时间格式与实际输入不兼容。
本文将带你了解出现该异常的原因以及解决办法。
2、理解异常 当 Java 的日期时间解析器无法从 TemporalAccessor(如 LocalDate、ZonedDateTime 或 OffsetDateTime)中提取有效的 LocalDateTime 对象时,就会出现 “Unable to obtain LocalDateTime from TemporalAccessor” 异常。根本原因通常是输入字符串格式不当或不完整。
LocalDateTime 需要 日期 和 时间 两个部分。如果输入字符串缺少所需的部分或不符合预期的格式,解析过程就会失败,从而产生此异常。很多人认为 Java 可以自动推断缺少的时间值,但事实并非如此。
示例如下,解析一个日期字符串为 LocalDateTime 的错误例子:
public static void main(String[] args) { String dateTimeStr = "20250327"; // 只有日期,没时间 DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd"); LocalDateTime localDateTime = LocalDateTime.parse(dateTimeStr, formatter); } 执行该代码时,会出现以下异常:
java.time.format.DateTimeParseException: Text '20250327' could not be parsed: Unable to obtain LocalDateTime from TemporalAccessor: {},ISO resolved to 2025-03-27 of type java.
1、简介 本文将带你了解 Maven 中的预定义属性。
2、Maven 预定义属性 Maven 有一些方便的内置属性,可以于简化配置。
如果需要,甚至可以直接在 pom.xml 文件中自定义它们。
这些属性也可以在任何由 Maven 资源插件的过滤功能处理的资源文件中使用,例如 application.properties 文件。在使用这些属性时,只需要将它们包装在 ${} 中。
2.1、使用示例 示例如下,使用输出目录位置属性:
<?xml version="1.0" encoding="UTF-8"?> <project> <modelVersion>4.0.0</modelVersion> <groupId>example.override.properties</groupId> <artifactId>parameter-maven-plugin</artifactId> <version>1.0-SNAPSHOT</version> <packaging>maven-plugin</packaging> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-plugin-plugin</artifactId> <version>3.2</version> <configuration> <outputDirectory>${project.build.directory}</outputDirectory> </configuration> </plugin> </plugins> </build> </project> 上面的示例展示了如何获取 ${project.build.directory} 的默认值,即 src/main/resources。
2.2、属性覆盖示例 自定义构建路径的属性:
<?xml version="1.0" encoding="UTF-8"?> <project> <modelVersion>4.0.0</modelVersion> <groupId>example.override.properties</groupId> <artifactId>parameter-maven-plugin</artifactId> <version>1.0-SNAPSHOT</version> <packaging>maven-plugin</packaging> <build> <directory>src/main/resources/custom</directory> </build> </project> 如上,通过在 <project> -> <build> -> <directory> 中指定自定义值,将 ${project.build.directory} 属性的默认值覆盖为 src/main/resources/custom。