Lombok 中的 @ExtensionMethod 注解

1、概览 Lombok 是一个流行的 Java 库,它通过减少模板代码来简化代码编写。其强大功能之一是 @ExtensionMethod 注解,可以增强代码的可读性和简洁性。 本文将带你了解 Lombok 中的 @ExtensionMethod 注解有什么用,以及如何有效地使用它。 2、@ExtensionMethod 是什么? @ExtensionMethod 注解允许为现有类添加静态方法扩展。这意味着可以在原始类中调用在其他类中定义的方法。这有利于在不修改源代码的情况下增强第三方库或现有类的功能。 3、@ExtensionMethod 的原理 要使用 @ExtensionMethod,需要用 @ExtensionMethod 来注解一个类,并指定包含想要扩展的静态方法的类。然后,Lombok 会生成必要的代码,使这些方法可用,就好像它们是注解类的方法一样。 假设我们有一个工具类 StringUtils,其中的 reverse() 方法可以反转字符串。我们想把这个方法当作 String 类的一个方法来使用。Lombok 的 @ExtensionMethod 可以帮助我们实现这一目的。 首先,需要在项目中添加 Lombok 依赖。如果使用 Maven,可以在 pom.xml 中添加以下内容: <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.24</version> <scope>provided</scope> </dependency> Lombok 的最新版本可以在 Maven 中央仓库 找到。 3.1、String 示例 先创建 StringUtils 工具类,实现 reverse 方法: public static String reverse(String str) { return new StringBuilder(str).reverse().toString(); } 接下来,创建一个使用 @ExtensionMethod 注解的测试类: @ExtensionMethod(StringUtils.class) public class StringUtilsUnitTest { @Test public void givenString_whenUsingExtensionMethod_thenReverseString() { String original = "Lombok Extension Method"; String reversed = original.

使用 @Valid 注解校验嵌套对象

1、简介 本文将带你了解如何使用 @Valid 注解来验证对象及其嵌套的子对象。 当传入数据是基本数据类型,比如 Integer 或 String 时,验证数据可以很简单。然而,当传入信息是一个对象,特别是一个对象图时,验证可能会更加困难。幸运的是,@Valid 注解简化了对嵌套子对象的验证。 2、@Valid 注解是啥 @Valid 注解来自 Jakarta Bean Validation 规范,用于标记需要验证的特定参数。 使用该注解可确保传递给方法或存储在字段中的数据符合指定的验证规则。这有助于提高数据的完整性和一致性。 当在 JavaBean 的字段或方法上使用时,它会触发所有已定义的约束检查。Bean Validation API 中最常用的一些约束包括 @NotNull、@NotBlank、@NotEmpty、@Size、@Email、@Pattern 等。 3、在子对象上使用 @Valid 注解 首先,必须确定验证规则,并对字段应用前面提到的验证约束。 接下来,定义一个 Project 类,该类包含一个嵌套的 User 对象,并用 @Valid 注解来装饰该对象: public class Project { @NotBlank(message = "Project title must be present") @Size(min = 3, max = 20, message = "Project title size not valid") private String title; @Valid // 校验嵌套的对象 private User owner; // 构造函数、Getter、Setter 方法省略 } public class User { // 校验规则 @NotBlank(message = "User name must be present") @Size(min = 3, max = 50, message = "User name size not valid") private String name; // 校验规则 @NotBlank(message = "User email must be present") @Email(message = "User email format is incorrect") private String email; // 构造函数、Getter、Setter 方法省略 } 之后,通过 Validator 实例的 validate() 方法来验证对象:

Spring AI 对 Ollama Tool 的支持

本周早些时候,Ollama 推出 了一项令人兴奋的新功能:对大型语言模型(LLM)的工具支持。 今天,我们非常高兴地宣布 Spring AI(1.0.0-SNAPSHOT)已完全支持这一强大功能,将 Ollama 的函数调用功能引入 Spring 生态系统。 Ollama 的工具支持允许模型决定何时调用外部函数以及如何使用返回的数据。这为访问实时信息到执行复杂计算等各种可能性打开了大门。Spring AI 采用了这一概念,并将其与 Spring 生态系统无缝集成,使 Java 开发人员能够非常轻松地在其应用程序中利用这一功能。Spring AI 的 Ollama 函数调用支持的主要功能包括: 轻松集成:将 Java 函数注册为 Spring Bean,并与 Ollama 模型一起使用。 灵活配置:多种注册和配置函数的方法。 支持多种函数:在一个聊天会话中注册和使用多种函数。 运行时函数选择:动态选择为每个提示(Prompt)启用哪些函数。 代码可移植性:使用不同的 LLM 提供商(如 OpenAI、Mistral、VertexAI、Anthropic、Groq),无需修改代码即可重复使用你的应用代码。 工作原理 定义自定义 Java 函数并将其注册到 Spring AI。 执行可能需要调用函数才能完成回答的聊天请求。 当 AI 模型确定需要调用一个函数时,它会生成一个包含函数名称和参数的 JSON 对象。 Spring AI 会拦截该请求,调用你的 Java 函数,并将结果返回给模型。 该模型将函数的输出纳入其响应中。 入门 先决条件 首先需要在本地计算机上运行 Ollama(0.2.8 以上)。请参阅官方的 Ollama 项目 README,开始在本地计算机上运行模型。然后调出支持模型的工具,例如 Llama 3.1、Mistral、Firefunction v2、Command-R +。。。支持的模型列表可在 模型页面的工具类别 下找到。 ollama pull mistral 依赖 要开始在 Spring AI 中使用 Ollama 函数调用,请在项目中添加以下依赖:

PostConstruct 和 PreDestroy 注解

1、简介 Spring 允许我们在 Bean 的创建和销毁时执行自定义操作。例如,可以让 Bean 实现 InitializingBean 和 DisposableBean 接口,从而在创建和销毁时触发回调方法。 本文将带你了解第二种实现方式,即使用 @PostConstruct 和 @PreDestroy 注解。 2、@PostConstruct Spring 只会在 Bean 属性初始化后调用一次注解了 @PostConstruct 的方法。即使没有任何属性需要初始化,这些方法也会运行。 使用 @PostConstruct 注解的方法可以用任何访问级别,但不能是静态的。 @PostConstruct 一个可能的用途是填充数据。例如,在开发过程中,我们想创建一些默认用户: @Component public class DbInit { @Autowired private UserRepository userRepository; @PostConstruct private void postConstruct() { // 初始化默认用户 User admin = new User("admin", "admin password"); User normalUser = new User("user", "user password"); userRepository.save(admin, normalUser); } } 上述示例将首先初始化 UserRepository 属性,然后运行 @PostConstruct 方法。 3、@PreDestroy 在 Spring 将 Bean 从 Application Context 中删除之前,会运行一次注解了 @PreDestroy 的方法。

Java 和 Guava 中的线程池

1、概览 本文将带你了解 Java 中的线程池。首先介绍 Java 中的标准库,然后介绍 Google 的 Guava 库。 2、线程池 在 Java 中,线程被映射到系统级线程,而系统级线程是操作系统的资源。如果不加控制地创建线程,这些资源可能很快就会耗尽。 操作系统也会在线程之间进行上下文切换,以模拟并行处理。一个简单的观点是,创建的线程越多,每个线程实际工作的时间就越少。 线程池模式有助于在多线程应用中节省资源,并将并行性控制在某些预定义的范围内。 使用线程池时,我们将并发代码编写为并行任务的形式,并将它们提交给线程池实例执行。这个实例控制着多个可重复使用的线程来执行这些任务。 该模式允许我们控制应用创建的线程数量及其生命周期。还能调度任务的执行,并将接收到的任务保存在队列中。 3、Java 中的线程池 3.1、Executors、Executor 和 ExecutorService Executors 工具类包含多个用于创建预配置线程池实例的方法。这些类是一个很好的起点。如果不需要进行任何自定义微调,就可以使用它们。 在 Java 中,我们使用 Executor 和 ExecutorService 接口来处理不同的线程池实现。通常,应该使代码与线程池的实际实现解耦,并在整个应用中使用这些接口。 3.1.1、Executor Executor 接口有一个 execute 方法,用于提交实现了 Runnable 接口的实例以供执行。 来看一个快速示例,演示如何使用 Executors API 获取由单线程池和无界队列支持的 Executor 实例,以便按顺序执行任务。 如下,运行一个任务,只需在屏幕上打印 “Hello World” 即可。我们以 lambda(Java 8 的一个特性)的形式提交任务,它被推断为 Runnable: Executor executor = Executors.newSingleThreadExecutor(); executor.execute(() -> System.out.println("Hello World")); 3.1.2、ExecutorService ExecutorService 接口包含大量用于控制任务进度和管理服务终止的方法。使用该接口,我们可以提交任务以供执行,还可以使用返回的 Future 实例控制任务的执行。 现在,创建一个 ExecutorService,提交一项任务,然后使用返回的 Future 的 get 方法等待提交的任务完成并返回结果值:

处理 Spring Boot H2 Exception:“Schema not found”

1、概览 H2 是一个开源的 SQL 数据库,在 Java 中通常用于测试。它是一个内存数据库,不会将任何数据持久化到磁盘,因此速度非常快。 在与 Spring Boot 整合时,我们可能会遇到 “Schema not found” 异常,本文将带你了解出现此异常的原因,以及如何解决该异常。 2、理解异常的原因 H2 的默认 Schema 是 PUBLIC。如果我们映射的 JPA 实体类不使用 PUBLIC Schema,则必须确保在 H2 上创建了 Schema。当目标 Schema 不存在时,Spring Boot 会抛出异常 “Schema not found”。 模拟一下这个场景。在 Spring Boot 应用中创建以下实体类和 Repository。 @Entity @Table(name = "student", schema = "test") public class Student { @Id @Column(name = "student_id", length = 10) private String studentId; @Column(name = "name", length = 100) private String name; // 构造函数、Getter\Setter 方法 } 如上 @Table 注解指定了实体映射到 test Schema 下的 student 表的映射细节。

Spring Data MongoDB 构建多个条件的查询

1、简介 本文将带你了解如何使用 Spring Data JPA 在 MongoDB 中创建具有多个 Criteria(条件)的查询。 2、项目设置 首先,在 pom.xml 文件中添加 Spring Data MongoDB Starter 依赖: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-mongodb</artifactId> <version>3.3.1</version> </dependency> 通过该依赖,我们可以在 Spring Boot 项目中使用 Spring Data MongoDB 的功能。 2.1、定义 MongoDB Document 和 Repository 接下来,定义一个 MongoDB Document(文档),它是一个用 @Document 注解的 Java 类。该类映射到 MongoDB 中的一个 Collection(集合)。 例如,创建一个 Product Document: @Document(collection = "products") public class Product { @Id private String id; private String name; private String category; private double price; private boolean available; // Getter 和 Setter 方法 } 在 Spring Data MongoDB 中,我们可以创建 Repository 来定义自己的查询方法。通过注入 MongoTemplate,可以对 MongoDB 数据库执行高级操作。该类为执行查询、聚合数据和有效处理 CRUD 操作提供了丰富的方法集:

Spring Boot v3.3.2 发布

Spring Boot v3.3.2 正式发布。 🐞 Bug 修复 没有在 Spring Integration 6.2 中引入的 defaultTimeout 设置的配置属性 #41521 在 OnClassCondition.resolveOutcomesThreaded 中进行自动配置时出现 NPE,因为 firstHalf 为 null #41504 Spring 授权服务器现在将 multipleIssuersAllowed 默认为 false,并且不容易重新启用 #41355 ServiceConnection 无法与 @DataLdapTest 一起使用 #41325 PropertiesMigrationListener 错误地将属性报告为过时属性 #41252 @NestedConfigurationProperty 对 Record 不起作用 #41251 TestcontainersLifecycleBeanPostProcessor 无法与 Scope Bean 正常工作 #41238 如果 spring.config.import 无法解析,错误信息可能具有误导性 #41236 Docker desktop 更新后,构建镜像失败,提示 “Illegal char <:> at index 5: npipe:////” #41234 使用 Jetty 时,Filter、Listener 和 Servlet 未使用同一线程上下文 classloader 初始化 #41225 在 Webflux 中使用 DirtiesContext、随机端口和多个上下文的情况下,可能会导致多个上下文的行为异常 #41221 在原生镜像中使用 spring-boot-starter-activemq 时,org.

JPA、Hibernate 和 Spring Data JPA 中的数据库审计

1、概览 就 ORM 而言,数据库审计指的是跟踪和记录与实体相关的事件,或者简单地说是实体版本管理。受 SQL 触发器的启发,这些事件是对实体的插入、更新和删除操作。数据库审计的好处类似于源代码版本控制。 本文将带你了解在应用中使用审计的三种方法。首先介绍来自于 JPA 标准的审计实现、然后再介绍由 Hibernate 和 Spring Data 分别提供的审计的扩展实现。 下面是本文中使用的相关实体 Bar 和 Foo 示例: 2、JPA 审计 JPA 并没有明确包含审计 API,但我们可以通过使用实体生命周期事件来实现这一功能。 2.1、@PrePersist、@PreUpdate 和 @PreRemove 在 JPA 实体类中,我们可以使用 @PrePersist、@PreUpdate 和 @PreRemove 注解指定一个方法作为回调,以在相应的 DML 操作前执行它。 @Entity public class Bar { @PrePersist public void onPrePersist() { ... } @PreUpdate public void onPreUpdate() { ... } @PreRemove public void onPreRemove() { ... } } 实体内部的回调方法应是非 static 的,返回类型为 void,且不带参数。访问权限任意。 注意,JPA 中的 @Version 注解与本文的主题没有关系,它是一种乐观锁,与审计数据无关。

在 Spring Data JPA 中使用 Stream(流式)查询

简介 本文将带你了解在 Spring Data JPA 中使用 Stream(流式)查询的最佳方式。 当需要获取较大的结果集时,使用 Java Stream 的好处是可以逐步迭代查询结果集,避免一次性获取所有数据可能导致的内存溢出异常。 JPA Stream 方法 自 2.2 版起,你可以使用 JPA 的 getResultStream 方法以 Stream 的形式处理结果集。 getResultStream 使用 JDBC ResultSet 对给定查询返回的记录进行流式处理。特别是在处理大结果集的时候,这种方法很有效率。 Spring Data JPA Stream 查询方法 如果要对查询结果集进行流式处理,则需要在 Spring Data JPA 查询方法中返回 Java Stream 类型,如下例所示: @Repository public interface PostRepository extends BaseJpaRepository<Post, Long> { @Query(""" select p from Post p where date(p.createdOn) >= :sinceDate """ ) @QueryHints( @QueryHint(name = AvailableHints.HINT_FETCH_SIZE, value = "25") ) // 返回 Stream Stream<Post> streamByCreatedOnSince(@Param("sinceDate") LocalDate sinceDate); } 对于 PostgreSQL 和 MySQL,指定 FETCH_SIZE JPA QueryHint 是必要的,它指示 JDBC 驱动每次迭代的时候最多预取 25 条记录。否则,PostgreSQL 和 MySQL JDBC 驱动会在遍历底层 ResultSet 之前预取所有查询结果。