Spring

使用 JdbcTemplate 调用存储过程

1、概览 本文将带你了解如何使用 Spring JDBC 框架的 JdbcTemplate 来调用存储过程。数据库存储过程类似于函数。函数支持输入参数并有返回类型,而存储过程同时支持输入和输出参数。 2、先决条件 来看看 PostgreSQL 数据库中一个简单的存储过程: CREATE OR REPLACE PROCEDURE sum_two_numbers( IN num1 INTEGER, IN num2 INTEGER, OUT result INTEGER ) LANGUAGE plpgsql AS ' BEGIN sum_result := num1 + num2; END; '; 存储过程 sum_twoo_numbers 接收两个输入数字,并在输出参数 sum_result 中返回它们的和。一般来说,存储过程可以支持多个输入和输出参数。但在本例中,我们只考虑了一个输出参数。 3、使用 JdbcTemplate#call() 方法 来看看如何使用 JdbcTemplate#call() 方法调用数据库存储过程: void givenStoredProc_whenCallableStatement_thenExecProcUsingJdbcTemplateCallMethod() { List<SqlParameter> procedureParams = List.of(new SqlParameter("num1", Types.INTEGER), new SqlParameter("num2", Types.NUMERIC), new SqlOutParameter("result", Types.NUMERIC) ); Map<String, Object> resultMap = jdbcTemplate.call(new CallableStatementCreator() { @Override public CallableStatement createCallableStatement(Connection con) throws SQLException { CallableStatement callableStatement = con.

理解 Spring Reactive 中的 switchIfEmpty()

1、概览 本文将带你了解 Spring Reactive 中的 switchIfEmpty() 操作符及其在使用和不使用 defer() 操作符时的行为,了解这些操作符在不同场景中的交互方式,并通过实际示例来说明它们对响应式流(Reactive Stream)的影响。 2、switchIfEmpty() 和 Defer() 的使用 switchIfEmpty() 是 Mono 和 Flux 中的一个操作符,用于在源生产者为空时执行备用生产者流。如果主源 Publisher 没有发布数据,该操作符就会切换到替代源的数据发布。 考虑一个从大型文件中通过 ID 检索用户详细信息的场景。当请求文件中的用户详细信息时,遍历文件会消耗大量时间。因此,对于经常访问的 ID,将其详细信息缓存起来更有意义。 当端点收到请求时,首先搜索缓存。如果用户详细信息可用,返回响应。如果没有,则从文件中获取数据并缓存起来,以备后续请求。 在这种情况下,主数据提供者(Primary Data Provider)是检查缓存中 KEY 是否存在的流,而备用数据提供者是检查文件中的 KEY 并更新缓存的流。switchIfEmpty() 操作符可以根据缓存中数据的可用性高效地切换数据源提供者。 了解 defer() 操作符的使用也很重要,它可以推迟函数的求值,直到发生订阅。如果我们不在 switchIfEmpty() 中使用 defer() 操作符,表达式就会立即(急切地)求值,从而可能导致意想不到的副作用。 3、示例设置 通过示例来了解 switchIfEmpty() 操作符在不同情况下的行为。 3.1、Data Model 首先,定义一个 User 模型类,其中包含一些详细信息,如 id、email、username 和 roles: public class User { @JsonProperty("id") private String id; @JsonProperty("email") private String email; @JsonProperty("username") private String username; @JsonProperty("roles") private String roles; // Getter / Setter 省略 } 3.

Spring 中的 Fallback Bean

1、概览 本文将带你了解 Spring 中 Fallback Bean 的概念。 Fallback Bean 是在 Spring Framework 6.2.0-M1 中引入的,当另一个相同类型的 Bean 不可用或无法初始化时,它提供了一种替代实现。 2、Primary Bean 和 Fallback Bean 在 Spring 中,我们可以定义多个相同类型的 Bean。默认情况下,Spring 使用 Bean 名称和类型来标识 Bean。当有多个名称和类型相同的 Bean 时,可以使用 @Primary 注解将其中一个标记为 Primary(主要)Bean,使其优先于其他 Bean。如果在 Application Context 初始化时创建了多个相同类型的 Bean,而我们又想指定默认使用哪个 Bean,那么这就非常有用了。 同样,我们可以定义一个 Fallback Bean,以便在没有其他合格 Bean 时提供替代实现。我们可以使用 @Fallback 注解将一个 Bean 标记为 Fallback(后备) Bean。只有当没有其他同名的 Bean 可用时,才会将后备 Bean 注入到 Application Context。 3、示例代码 来看一个示例,演示如何在 Spring 应用中使用 Primary Bean 和 Fallback Bean。 我们要创建一个使用不同 MQ 服务发送消息的小应用。假设我们在生产环境和非生产环境中拥有多个 MQ 服务,并且需要在它们之间切换以优化性能和成本。

通过 Spring 5 中 Supplier 来获取 Bean

今天遇到了一个面试题:Spring 中 Bean 的实例化有哪些方式? 大家知道,Spring 中 Bean 的配置方式有很多种,但是正常来说,无论你是 XML 文件配置,还是用类似 @Service 注解这种配置,本质上最终都是通过反射去完成 Bean 的初始化的;@Bean 注解则稍微特殊一点,往往我们在 @Bean 注解中是自己 new 出来目标 Bean,但是 @Bean 注解所标记的方法也是通过反射调用的。 似乎 Bean 的实例化离不开反射。 那么除了上面这些方案,还有没有其他方案呢?本文和大家探讨一下这个问题。 以下内容基于 Spring6.0.4。 总所周知,当使用 Spring 容器的时候,如果遇到一些特殊的 Bean,一般来说可以通过如下三种方式进行配置: 静态工厂方法 实例工厂方法 FactoryBean 不过从 Spring5 开始,在 AbstractBeandefinition 类中多了一个属性,对于特殊的 Bean 我们有了更多的选择: /** * Specify a callback for creating an instance of the bean, * as an alternative to a declaratively specified factory method. * <p>If such a callback is set, it will override any other constructor * or factory method metadata.

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 的方法。

使用 Spring Validator 接口进行数据校验

1、概览 Spring Validator 接口为验证对象提供了一种灵活且可定制的方式。本文将带你了解如何在 Spring 应用中使用 Validator 接口验证对象。 2、Spring Validator 接口 Validator 接口是 Spring 框架的一部分,它提供了一种验证对象的方法。 这是一个简单的接口,定义了两个方法:supports() 和 validate(),用于确定 Validator 是否能验证对象并执行验证逻辑。 2.1、supports(Class clazz) 方法 Validator 接口中的 supports() 方法决定 Validator 能否验证特定类的实例。该方法的参数 Class<?> clazz 代表被验证对象的类。它是一个泛型类(Class<?>),可以灵活用于不同的对象类型。 具体来说,Spring 利用 isAssignableFrom() 方法来检查对象是否可以合法地转换为 Validator 支持类的对象。因此,如果 Validator 能处理所提供的 clazz 对象,它就会返回 true,否则就会返回 false,以表示应使用另一个 Validator。 @Override public boolean supports(Class<?> clazz) { return User.class.isAssignableFrom(clazz); } 在上例中,Validator 配置为仅支持验证类型为 User 或其子类的对象。isAssignableFrom() 方法通过继来承验证兼容性 - 对于 User 及其子类,它返回 true,对于任何其他类类型,它返回 false。 2.2、validate(Object target, Errors errors) 方法 validate() 方法则用于为 Validator 支持的对象定义自定义验证逻辑。

Spring Prototype Bean 需要手动销毁吗?

1、简介 本文将带你了解 Spring 如何处理 Prototype Bean 并管理其生命周期,主要介绍是否有必要手动销毁 Prototype Bean、何时销毁以及如何销毁。 Spring 提供了多种 Bean Scope,本文主要聚焦 Prototype。 2、Prototype Bean 及其生命周期 Scope(作用域)确定了 Bean 在其存在的上下文中的生命周期和可见性。根据 Scope 的定义 ,IoC 容器负责管理 Bean 的生命周期。Prototype(原型)Scope 指示容器在每次使用 getBean() 请求或注入到另一个 Bean 时创建一个新的 Bean 实例。在创建和初始化方面,可以放心地依赖于 Spring。然而,销毁 Bean 的过程则不同。 在了解销毁 Bean 的必要性之前,先看看如何创建一个 Prototype Bean,如下: @Component // 指定 Bean 的 Scope 为 Prototype @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) public class PrototypeExample { } 3、Prototype Bean 需要手动销毁吗? Spring 不会自动销毁 Prototype Bean。与 Singleton Scope 不同,IoC 容器并不负责 Prototype Bean 整个的生命周期。容器会实例化、配置和组装 Prototype Bean,但之后将停止跟踪其状态。

Spring Framework v6.1.11 发布

Spring Framework v6.1.11 正式发布。 ⭐ 新特性 在 SpEL 的 ReflectionHelper 中确保 MethodHandle 的 varargs 组件类型不为 null#33193 在响应过程中出现 Reactor-Netty PrematureCloseException 时,WebClient 异常消息令人困惑 #33127 当发现无效的 factoryBeanObjectType 属性时,在异常中包含 Bean 名称 #33117 在响应式缓存切面使用 Error Handler #33073 getTypeForFactoryMethod 应捕获 NoClassDefFoundError 异常#33075 🐞 Bug 修复 SpEL 无法使用数组调用 varargs MethodHandle 函数 #33191 SpEL 无法调用变量参数为零的 varargs MethodHandle 函数 #33190 嵌套的 Bean 实例 Supplier 调用不会保留先前的工厂方法 #33180 DefaultErrorResponseBuilder 未实现 headers(Consumer) #33156 修复对 Set 方法参数的违规适配 #33150 使用 kotlinx-serialization 时,Web Controller 调用无效正文导致结果为 500,而不是 400 #33138 "file:.

在 Spring 中使用 Logbook 记录 HTTP 请求和响应

1、概览 Logbook 是一个可扩展的 Java 库,可为不同的客户端和服务器端提供完整的请求和响应日志。它允许开发人员记录应用接收或发送的任何 HTTP 流量。这可用于日志分析、审计或分析流量问题。 本文将带了解如何在 Spring Boot 中整合 Logbook,以及如何使用 Logbook 记录 HTTP 请求和响应。 2、依赖 在 Spring Boot 中添加 logbook-spring-boot-starter 依赖: <dependency> <groupId>org.zalando</groupId> <artifactId>logbook-spring-boot-starter</artifactId> <version>3.9.0</version> </dependency> 你可以在 Maven Central 中找到最新版本的 Logbook。 3、配置 Logbook 与 Spring Boot 应用中的 logback 日志配合使用。 我们需要在 logback-spring.xml 和 application.properties 文件中添加配置。 在 pom.xml 中添加 Logbook 库后,Spring Boot 就会自动配置 Logbook 库,我们需要做的就是在 application.properties 文件中添加日志级别: logging.level.org.zalando.logbook.Logbook=TRACE 启用 TRACE 日志级别,即可记录 HTTP 请求和响应。 此外,还要在 logback-spring.xml 文件中添加了 Logbook 配置: <logger name="org.zalando.logbook" level="INFO" additivity="false"> <appender-ref ref="RollingFile"/> </logger> 添加完成后,就可运行应用了。每次 HTTP 请求调用后,Logbook 都会将请求和响应记录到 logback-spring.

在 Golang 中实现类似于 Spring 中的模板事务

事务(TRANSACTION),是指一组操作的集合,这些操作要么全部成功,要么全部失败。其目的是在出现错误、系统崩溃或其他意外情况下,保证数据的一致性和完整性。 事务通常具有以下四个重要的特性,这些特性被统称为 ACID 属性: Atomicity(原子性): 定义: 事务中的所有操作要么全部完成,要么全部不完成,任何一个操作失败都会导致整个事务的失败,并且事务的所有操作都会被回滚(撤销)。 示例: 银行转账操作,如果从一个账户扣款后无法在另一个账户中存款,那么整个操作将回滚,不会执行任何更改。 Consistency(一致性): 定义: 事务只能把数据库从一种一致状态转换到另一种一致状态。在事务开始之前和结束之后,数据库的完整性约束没有被破坏。 示例: 在一个事务中插入数据时,如果插入的数据违反了数据库的完整性约束(例如唯一约束),那么这个事务将失败,数据库将保持一致状态。 Isolation(隔离性): 定义: 事务的执行是隔离的,多个事务并发执行时,一个事务的执行不会受到其他事务的干扰。隔离性确保了并发事务的执行结果与按顺序执行的结果相同。 示例: 两个用户同时购买同一件商品,隔离性确保每个用户看到的库存是正确的,避免超卖的情况。 Durability(持久性): 定义: 一旦事务提交,其结果将永久保存在数据库中,即使系统崩溃也不会丢失。 示例: 即使在事务提交后立即发生系统崩溃,事务的结果也会保存在数据库中,重启系统后数据依然存在。 以 MYSQL 关系型数据库为例,事务的使用如下: -- 开始事务 BEGIN TRANSACTION; -- TODO 执行业务 1 -- TODO 执行业务 2 -- TODO 执行业务 3 -- .... -- 提交事务 COMMIT; -- 或者,回滚事务 ROLLBACK; 其中,BEGIN TRANSACTION、COMMIT 以及 ROLLBACK 都是事务固定的模板代码,当代的大多数框架都会自动帮我们进行处理。 Spring 对事务的支持 Spring 对关系型数据库中的事务提供强大的支持,包括声明式事务、TransactionTemplate 模板事务等等。 @Transactional 声明式事务 实际开发中,最常用的就是通过 @Transactional 注解来声明事务方法。事务方法会在执行开始前自动开始事务,在方法结束后自动提交事务,在执行过程中如果遇到异常则自动回滚事务。 import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service @Transactional // 为 public 方法开启事务 public class FooService { public void service () { // TODO 在事务中执行业务 } } 在类上注解 @Transactional,则会为当前类中所有 public 方法开启声明式事务。也可以单独注解在方法上,则会覆盖类上的 @Transactional 定义。