1、简介 使用 Jackson 时,或多或少都会遇到需要从给定的类(Class)对象中生成 JavaType 的情况。
本文将带你了解如何借助 Jackson 库从类创建 JavaType。
2、JavaType 和 Class 在了解详情之前,先来了解一下 JavaType 和 Class。
2.1、Java 中的 JavaType 在 Jackson 中,JavaType 类代表 Java 类型。它是一种机制,可以处理泛型和数组等通用类型。
创建 JavaType 实例非常重要,尤其是当我们在处理 JSON 时处理泛型结构。
2.2、Java 中的 Class 在 Java 中,Class 类是反射 API 的成员,在运行时用来表示类或接口。
此外,它还提供类的信息,包括名称、字段、方法和构造函数。
3、使用 TypeFactory 创建 JavaType 要使用 Jackson 从提供的 Class 对象生成 JavaType 实例,需要利用 TypeFactory 类。
TypeFactory 提供了一个默认实例,可以构建不同的类型,无论是泛型还是参数化类型。
以使用 TypeFactory 从泛型类生成 JavaType 对象为例:
class MyGenericClass<T> { // Class 实现 } @Test void givenGenericClass_whenCreatingJavaType_thenJavaTypeNotNull() { Class<?
1、概览 消息确认是 MQ 系统中的一种标准机制,它向 Message Broker 发出信号,表明消息已被消费,不应再次传递。在亚马逊的 SQS(Simple Queue Servic)中,确认是通过删除队列中的信息来执行的。
本文将带你了解 Spring Cloud AWS SQS v3 中开箱即提供的三种确认模式: ON_SUCCESS、MANUAL 和 ALWAYS。
本文将利用 Spring Cloud AWS SQS V3 入门文章 中的环境和测试设置,使用事件驱动场景来说明用例。
2、依赖 首先,导入 Spring Cloud AWS BOM,以确保 pom.xml 中的所有依赖项相互兼容:
<dependencyManagement> <dependencies> <dependency> <groupId>io.awspring.cloud</groupId> <artifactId>spring-cloud-aws</artifactId> <version>3.1.0</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> 还要添加 Core 和 SQS starter 依赖:
<dependency> <groupId>io.awspring.cloud</groupId> <artifactId>spring-cloud-aws-starter-sqs</artifactId> </dependency> <dependency> <groupId>io.awspring.cloud</groupId> <artifactId>spring-cloud-aws-starter</artifactId> </dependency> 最后,添加测试所需的依赖,即带有 JUnit 5 的 LocalStack 和 TestContainers、用于验证异步消息消费的 awaitility 库,以及用于处理断言的 AssertJ:
1、概览 Spring Cloud AWS 是一个旨在简化与 AWS 服务交互的项目。SQS(Simple Queue Service)是 AWS 的一种解决方案,用于以可扩展的方式发送和接收异步消息。
本文将带你了解针对于 Spring Cloud AWS 3.0 完全重写的 Spring Cloud AWS SQS Integration。
该框架为处理 SQS 队列提供了熟悉的 Spring 抽象,如 SqsTemplate 和 @SqsListener 注解。
本文以一个事件驱动的场景为例,介绍了如何发送和接收消息。并展示使用 Testcontainers(一种管理一次性 Docker 容器的工具)和 LocalStack(可在本地模拟类似 AWS 的环境,用于测试)设置集成测试的策略。
2、依赖 Spring Cloud AWS BOM 可确保项目之间的版本兼容。它声明了包括 Spring Boot 在内的许多依赖项的版本。
添加 Spring Cloud AWS BOM 到 pom.xml:
<dependencyManagement> <dependencies> <dependency> <groupId>io.awspring.cloud</groupId> <artifactId>spring-cloud-aws</artifactId> <version>3.0.4</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> 我们需要的主要依赖关系是 SQS Starter,它包含项目中所有与 SQS 相关的类。SQS Integration 不依赖于 Spring Boot,可在任何标准 Java 应用中独立使用:
本文将带你了解如何为在 Kubernetes 上运行的 Spring Boot 应用配置 SSL 证书的热重载。我们将使用 Spring Boot 3.1 和 3.2 版本中引入的两个功能。第一个功能允许我们利用 SSL Bundle 在服务器端和客户端配置和使用自定义 SSL 配置。第二个功能使得在 Spring Boot 应用的嵌入式 Web 服务器中轻松进行 SSL 证书和密钥的热重载。
为了在 Kubernetes 上生成 SSL 证书,我们将使用 cert-manager。“cert-manager” 可以在指定期限后轮换证书,并将其保存为 Kubernetes Secret。之前的文章中介绍了如何在 Secret 更新时自动重启 Pod 的类似方案。我们使用 Stakater Reloader 工具在新版本的 Secret 上自动重启 pod。不过,这次我们使用 Spring Boot 新特性来避免重新启动应用(Pod)。
源码 你也可以克隆我的源代码,亲自尝试一下。首先克隆我的 GitHub 仓库。然后切换到 ssl 目录。你会发现两个 Spring Boot 应用:secure-callme-bundle 和 secure-caller-bundle。之后,你只需按照说明操作即可。
工作原理 在介绍技术细节之前,首先来了解我们的应用架构和面临的挑战。我们需要设计一个解决方案,在 Kubernetes 上运行的服务之间实现 SSL/TLS 通信。这个解决方案必须考虑到证书重载的情况。此外,服务器和客户端必须同时进行重载,以避免通信中出现错误。在服务器端,使用嵌入式 Tomcat 服务器。在客户端应用中,使用 Spring RestTemplate 对象。
1、简介 在将 Java 对象序列化和反序列化为 JSON 格式或从 JSON 格式反序列化 Java 对象时,Google 开发的 Gson 库是一个不错的选择。但是,在序列化对象时,我们通常会遇到 Gson 将整数显示为浮点数的问题。
本文将带你了解为什么在 Gson 的设计中整数被视为浮点数?以及如何解决这个问题。
2、问题的定义 Gson 可将 Java 对象序列化为 JSON。默认情况下,Gson 会将整数序列化为浮点数,以获得更精确的表示。下面是一个简单的例子:
public String jsonString= "[{\"id\":4077395,\"field_id\":242566,\"body\":\"\"}, " + "{\"id\":4077398,\"field_id\":242569,\"body\":[[273019,0],[273020,1],[273021,0]]}, " + "{\"id\":4077399,\"field_id\":242570,\"body\":[[273022,0],[273023,1],[273024,0]]}]"; 如上,我们声明了一个名为 jsonString 的 JSON 字符串,它代表一个对象数组。这个 JSON 数组有不同的字段,如 id、field_id 和 body。
现在,我们使用 Gson 库将 JSON 字符串反序列化为 Hashtable<String, Object> 对象列表。
ArrayList<Hashtable<String, Object>> responses; Type ResponseList = new TypeToken<ArrayList<Hashtable<String, Object>>>() {}.getType(); responses = new Gson().fromJson(jsonString, ResponseList); 如上,我们声明了一个名为 responses 的 ArrayList,其中包含 Key 为字符串、Value 为对象的 Hashtable 类型元素。此外,我们还利用 Gson 库将 jsonString 反序列化为 Hashtables 列表。
1、概览 Spring Batch 是一个强大的 Java 批处理框架,因此在数据处理活动和定时任务运行中被广泛选择。根据业务逻辑的复杂程度,作业可以依赖不同的配置值和动态参数。
本文将带你了解如何使用 JobParameter 以及如何从基本的批处理组件中访问它们。
2、Demo 项目 我们将为药房服务开发一个 Spring Batch。主要业务任务是查找即将过期的药品,根据销售情况计算新价格,并通知消费者即将过期的药品。此外,我们将从内存中的 H2 数据库读取数据,并将所有处理细节写入日志,以简化实现过程。
2.1、依赖 要开始演示应用,需要添加 Spring Batch 和 H2 依赖:
<dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <version>2.2.224</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-batch</artifactId> <version>3.2.0</version> </dependency> 可以在 Maven Central Repository 中找到最新的 H2 和 Spring Batch 版本。
2.2、准备测试数据 首先在 schema-all.sql 中定义 Schema:
DROP TABLE medicine IF EXISTS; CREATE TABLE medicine ( med_id VARCHAR(36) PRIMARY KEY, name VARCHAR(30), type VARCHAR(30), expiration_date TIMESTAMP, original_price DECIMAL, sale_price DECIMAL ); 初始测试数据定义在 data.
1、概览 Spring JPA 和 Hibernate 为无缝数据库通信提供了强大的工具。不过,由于客户端将更多控制权委托给了框架,因此生成的查询可能远非最佳。
本文将带你了解使用 Spring JPA 和 Hibernate 时常见的 N+1 问题,以及可能导致该问题的不同情况。
2、社交媒体平台 为了更好地将问题形象化,我们需要概述实体之间的关系。以一个简单的社交网络平台为例。这里只有用户(User)和帖子(Post):
我们在图表中使用了 Iterable,并且我们将为每个示例提供具体的实现:List 或 Set。
为了测试请求的数量,我们将使用一个专用库,而不是检查日志。不过,我们会参考日志,以便更好地了解请求的结构。
如果在每个示例中没有明确指定关系的获取类型(Fetch Type),则默认情况下假定为默认值。所有的一对一关系都使用急切加载(Eager Fetch),而一对多关系则使用延迟加载(Lazy)。此外,代码示例中使用了 Lombok 来减少代码中的冗余。
3、N+1 问题 N+1 问题指的是,对于单个请求(例如检索用户),会对每个用户发出额外请求,以获取其信息。虽然这个问题通常与懒加载有关,但并非总是如此。
任何类型的关系都可能出现这种问题。不过,它通常出现在多对多或一对多关系中。
3.1、延迟加载 首先,来看看懒加载是如何导致 N+1 问题的,示例如下:
@Entity public class User { @Id private Long id; private String username; private String email; @OneToMany(cascade = CascadeType.ALL, mappedBy = "author") protected List<Post> posts; // 构造函数/getter/setter } User 与 Post 之间是一对多的关系。这意味着每个 User 都有多个 Post。我们没有明确确定字段的 Fetch 策略。策略是从注解中推断出来的。如前所述,@OneToMany 默认采用 Lazy Fetch 策略:
1、概览 Aspect-Oriented Programming(面向切面编程,简称 AOP)是一种范式,它能让我们在整个应用中隔离事务管理或日志记录等交叉问题,而不会干扰业务逻辑。
本文将带你了解如何在 Spring 中使用 AOP 记录执行日志。
2、不使用 AOP 记录日志 通常,我们会在方法的开始和结束处输出日志。这样,就能跟踪应用的执行流程。此外,还可以捕获传递给特定方法的值及其返回值。
示例如下:
public String greet(String name) { logger.debug(">> greet() - {}", name); String result = String.format("Hello %s", name); logger.debug("<< greet() - {}", result); return result; } 尽管上面的实现看起来是一个标准的解决方案,但日志语句在代码中增加了复杂性。
如果没有日志记录,只需要一行代码就可以完成:
public String greet(String name) { return String.format("Hello %s", name); } 3、面向切面(AOP)编程 顾名思义,面向切面的编程(Aspect-Oriented Programming)侧重于切面,而不是对象和类。我们可以使用 AOP 编程为特定应用实现额外功能,而无需修改其当前实现。
3.1、AOP 概念 在深入学习之前,让我们先从高层次来了解一下 AOP 的基本概念。
切面(Aspect):横切关注点或我们希望在整个应用中应用的功能。 连接点(Join Point):应用流程中我们希望应用切面的点。 Advice:在特定连接点应执行的操作。 Pointcut:应在其中应用某一切面的连接点集合。 另外,Spring AOP 仅支持方法执行的连接点。可以考虑使用 AspectJ 等编译时库为字段、构造函数、静态初始化器等创建切面。
⚠️ 注意 此版本升级到 Hibernate 6.4.4.Final。虽然它包含了许多有价值的错误修复,但在原生镜像(Native Image)中无法正常工作。如果你正在使用 GraalVM,则应在 pom.xml 中使用 hibernate.version 属性将 Hibernate 暂时降级到 6.4.2.Final。
🐞 Bug 修复 如果路径中包含空格,无法解析嵌套的 JAR URL #39675 当使用较长的镜像名称且标记包含非法字符时,镜像构建会运行很长时间 #39638 Banner 打印不遵守设置的字符集 #39621 “micrometer.observations.” 配置属性应为 “management.observations.” #39600 配置类解析过程中的元数据读取使用默认资源加载器,而不是应用的资源加载器 #39598 当将一些 Gson 属性(包括 spring.gson.disable-html-escaping)设置为 false 时,它们的行为不正确 #39524 当配置属性绑定使用转换器从属性值创建一个 Map 时,属性占位符不会被解析 #39515 Gradle 插件允许使用 Gradle 7.4,但文档和测试的最低版本是 7.5 #39513 WebFlux 自动配置应仅在启用虚拟线程时配置 blocking executor #39469 TestcontainersPropertySource 断言有错别字 #39449 缺少参数时,Webflux actuator 端点的响应为 500 #39444 使用 non-shaded Pulsar 客户端和配置身份验证参数时出现 NoSuchMethod 错误 #39389 Jetty GracefulShutdown 会写入 System.
1、概览 Spring JPA 和 Hibernate 为不同据库通信提供了强大的工具。然而,随着开发者将更多的控制权(包括查询生成)委托给框架,结果可能与我们的预期相去甚远。
开发者通常会对在多对多关系中使用列表(List)还是集合(Set)产生困惑。而且,Hibernate 对其 bag、list 和 set 使用了类似的名称,但它们之间有稍微不同的含义,这进一步增加了混淆的可能性。
在大多数情况下,Set 更适用于一对多或多对多关系。不过,它们对性能有特殊的影响,需要注意。
本文将带你了解 JPA 实体关系中 List 和 Set 的区别,以及各自的优缺点。
2、测试 这里使用专门的测试库来测试请求数。检查日志不是一个好的解决方案,因为它不是自动化的,可能只适用于简单的示例。当请求产生数十或数百个查询时,使用日志是不够高效的。
首先,需要 io.hypersistence。注意,artifact ID 中的数字是 Hibernate 版本:
<dependency> <groupId>io.hypersistence</groupId> <artifactId>hypersistence-utils-hibernate-63</artifactId> <version>3.7.0</version> </dependency> 此外,还使用 util 库进行日志分析:
<dependency> <groupId>com.vladmihalcea</groupId> <artifactId>db-util</artifactId> <version>1.0.7</version> </dependency> 我们应该使用所提供的 Util 来 封装数据源,使其正常工作。这可以通过 BeanPostProcessor 来做实现:
@Component public class DataSourceWrapper implements BeanPostProcessor { public Object postProcessBeforeInitialization(Object bean, String beanName) { return bean; } public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof DataSource originalDataSource) { ChainListener listener = new ChainListener(); SLF4JQueryLoggingListener loggingListener = new SLF4JQueryLoggingListener(); loggingListener.