使用 Spring Validator 验证 Map

1、简介 Spring 的 Validation 框架主要设计用于 JavaBean,其中每个字段都可以通过注解来定义验证约束。 本文将带你了解如何使用 Spring 的 Validator 接口验证 Map<String,String>。 2、理解问题 - Hibernate Validator 和 Map 在实现自定义验证器之前,我们会自然地尝试直接在 Map 结构上使用标准约束注解,通过 Hibernate Validator 和 Spring 内置的 @Valid 和 @Validated 等验证机制来进行校验。不幸的是,这种方法并不像我们想象的那样有效。 来看一个示例: Map<@Length(min = 10) String, @NotBlank String> givenMap = new HashMap<>(); givenMap.put("tooShort", ""); Validator validator = Validation.buildDefaultValidatorFactory().getValidator(); Set<ConstraintViolation<Map<String, String>>> violations = validator.validate(givenMap); Assertions.assertThat(violations).isNotEmpty(); // 测试失败 尽管在 Map 泛型上添加了约束注解,但校验结果 violations 仍是空的,即未检测到违反约束的情况。 2.1、为什么会校验失败? Hibernate Validator 或一般的 Bean Validation 是根据 JavaBean 惯例运行的,这意味着它会验证通过 getter 访问的对象属性。由于 Map 不会将键和值作为属性公开,因此 像 @Length 或 @NotBlank 这样的约束注解并不直接适用,在验证过程中会被忽略。

解决 Spring Boot 异常:“Cannot Load Driver Class: com.mysql.jdbc.driver”

1、简介 在 Spring Boot 中整合 MySQL 时,我们通常依赖 Spring Boot 的自动配置和 JDBC 支持来连接数据库。不过,遇到配置问题的情况并不少见,尤其是在使用 过时的依赖 时。其中一个异常就是: Cannot load driver class: com.mysql.jdbc.Driver 本文将带你了解出现 “Cannot load driver class: com.mysql.jdbc.Driver” 异常的原因以及解决办法。 2、理解原因 这个问题的根源在于使用了过时的 MySQL 驱动程序类。在早期版本的 MySQL JDBC 驱动程序 (Connector/J) 中,通常使用以下驱动程序类: spring.datasource.driver-class-name=com.mysql.jdbc.Driver 不过,从 MySQL Connector/J 8.0 开始,该类已被弃用并删除。更新后的驱动程序类是 com.mysql.cj.jdbc.Driver 如果在新版本的 MySQL 驱动程序中继续使用旧的类名,应用将无法启动,并出现 ClassNotFoundException 异常。 3、处理问题 要解决这个问题,需要更新 Spring Boot 配置和项目中使用的依赖。 3.1、使用正确的依赖 第一步是在 application.properties 或 application.yml 文件中新的驱动程序类替换旧的驱动程序类。 spring.datasource.url=jdbc:mysql://localhost:3306/mydb spring.datasource.username=root spring.datasource.password=secret # 新的驱动类 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver 或者 application.yml 文件: spring: datasource: url: jdbc:mysql://localhost:3306/mydb username: root password: secret driver-class-name: com.

使用 Java Compiler API 编译 Java 代码

1、简介 在 Java 开发中,编译是防止语法错误、类型不匹配和其他可能导致项目失败的问题的第一道防线。传统的工作流程依赖于手动编译,而现代应用程序则需要动态编译检查。例如: 实时验证学生提交的代码的教育平台 在部署前对生成的代码片段进行编译的 CI/CD 流水线 动态编译用户自定义逻辑的低代码工具 热代码重载系统可即时重载开发人员所做的更改 创建 Java 插件 Java Compiler API 允许在 Java 应用程序中以编程式编译代码,从而实现上述应用场景。像 LeetCode 或 Codecademy 等平台可即时验证用户提交的代码。当用户点击 “运行” 时,后台会使用编译器 API 等工具编译代码段、检查错误并在沙盒环境中执行。程序化编译为这一即时反馈循环提供了动力。 本文将带你了解 Java Compiler 这一强大的工具。 2、Java Compiler API 概览 Java Compiler API 位于 javax.tools 包内,提供对 Java 编译器的编程式访问。该 API 对于需要在运行时验证或执行代码的动态编译任务至关重要。 Compiler API 的主要组件包括: JavaCompiler:启动编译任务的主编译器实例 JavaFileObject:代表 Java 源文件或类文件,可以是内存文件,也可以是基于文件的文件 StandardJavaFileManager:编译过程中管理输入和输出文件 DiagnosticCollector:捕获错误和警告等编译诊断信息 这些组件协同工作,可在 Java 应用程序中实现灵活高效的动态编译。 3、实现编译 JDK 环境默认提供 Compiler API,无需任何外部依赖。 现在,来看看如何编译内存中的 Java 代码。 3.1、在内存中创建源码 首先在内存中创建一个 Java 源码类: // 继承自 SimpleJavaFileObject public class InMemoryJavaFile extends SimpleJavaFileObject { private final String code; protected InMemoryJavaFile(String name, String code) { super(URI.

使用 Spring AI 从图片中提取结构化数据

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。

如何在后台运行 Java 程序?

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.

Spring Boot v3.4.5 发布

🐞 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.

EnableEurekaClient 与 EnableDiscoveryClient,该用哪个?

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-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>() .

Spring Cloud Data Flow 停止开源

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。

Spring WebClient 设置 Header

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.