教程

解决 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.

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

在 Spring Boot 中通过 Profile 启用或禁用嵌入式 Tomcat

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 很有用,但在某些情况下,禁用它对我们也有好处:

解决 DateTimeParseException: “Unable to obtain LocalDateTime from TemporalAccessor” 异常

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.

Maven 预定义属性详解

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。