Spring

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 定义。

使用 Spring WebClient 和 WireMock 进行集成测试

1、简介 Spring WebClient 是一款非阻塞、响应式的 HTTP 客户端,而 WireMock 是一个强大的用于模拟基于 HTTP 的 API 的工具。 2、依赖和示例 首先,需要在 Spring Boot 项目中添加必要的依赖。 在 pom.xml 中添加 spring-boot-starter-flux(WebClient) 和 spring-cloud-starter-wiremock(WireMock Server)依赖: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-contract-wiremock</artifactId> <version>4.1.2</version> <scope>test</scope> </dependency> 假设,我们的应用需要调用外部天气 API,以获取给定城市的天气数据。 定义 WeatherData POJO: public class WeatherData { private String city; private int temperature; private String description; // 构造函数、Getter/Setter 方法省略 我们要使用 WebClient 和 WireMock 进行集成测试,以测试这个功能。 3、使用 WireMock API 进行集成测试 首先用 WireMock 和 WebClient 设置 Spring Boot 测试类:

如何控制 Spring Bean 的加载顺序?

先说结论,使用 @Order 注解或者是实现 Ordered 接口并不能控制 Bean 的加载顺序。 一、@Order 注解和 Ordered 接口 在 Spring 框架中,@Order 是一个非常实用的元注解,它位于 spring-core 包下,主要用于控制某些特定上下文(Context)中组件的执行顺序或排序,但它并不直接控制 Bean 的初始化顺序。 1.1、用途 @Order 注解或者是 Ordered 接口,常见的用途主要是两种: 定义执行顺序:当多个组件(如 Interceptor、Filter、Listrner 等)需要按照特定的顺序执行时,@Order 注解可以用来指定这些组件的执行优先级。数值越小,优先级越高,相应的组件会更早被执行或被放置在集合的前面(@Order 注解接受一个整数值,这个值可以是负数、零或正数。Spring 框架提供了 Ordered.HIGHEST_PRECEDENCE(默认最低优先级)和 Ordered.LOWEST_PRECEDENCE(默认最高优先级)常量,分别对应于 Integer.MIN_VALUE 和 Integer.MAX_VALUE,可以方便地设定优先级。 集合排序:当相同类型的组件被自动装配到一个集合中时,@Order 注解会影响它们在这个集合中的排列顺序。 1.2、使用场景 经典的使用场景如下。 拦截器的排序 在 Spring MVC 中,可以使用 @Order 来控制拦截器(Interceptor)的执行顺序。 Spring Security Filter 在 Spring Security 中,过滤器链(Filter Chain)的顺序通过 @Order 来定义,确保正确的安全处理流程。 // HttpSecurity 的 performBuild 方法 @Override protected DefaultSecurityFilterChain performBuild() { // 对 Filter 进行排序 this.

Spring 异常 “No Multipart Boundary Was Found”

1、简介 本文将带你了解在 Spring 中处理文件上传(Multipart)请求时出现异常:“No Multipart Boundary Was Found” 的原因,以及解决办法。 2、理解 Multipart 请求 简而言之,Multipart 请求是一种 HTTP 请求,它在一条消息的请求体中传输一种或多种不同的数据。请求体被分成多个部分,请求中的每个部分都可能代表不同的文件或数据。 通常使用它来传输或上传文件、交换电子邮件、流媒体或提交 HTML 表单,并使用 Content-Type 标头来指明在请求中发送的数据类型。 来看看 Multipart 请求需要设置哪些值。 2.1、主类型 主类型(顶级类型)指定了我们发送的内容的主要类别。如果我们在单个 HTTP 请求中提交多种数据类型,则需要将 Content-Type Header 值设置为 multipart。 2.2、子类型 除了顶级类型外,Content-Type Header 值还包含一个强制的子类型。子类型值提供了有关数据格式的附加信息。 在不同的 RFC 中定义了多种 multipart 子类型。例如 multipart/mixed、multipart/alternative、multipart/related 和 multipart/form-data(常用)。 由于我们在一个请求中封装了多种不同的数据类型,因此需要一个额外的参数来分隔 multipart 消息的不同部分:即,boundary 参数。 2.3、Boundary 参数 Boundary 指令(参数)是 multipart Content-Type 的强制值,它指定了封装边界。 根据 RFC 1341 的定义,封装(Boundary)边界是由两个连字符(“–”)后跟 Content-Type Header 中的 boundary 值组成的分隔线。它用于分隔 HTTP 请求体中的各个部分(即,子请求体)。 来看一个实际的案例,Web 浏览器请求包含两个 Part。通常情况下,Content-Type Header 信息如下:

在 Spring 中实现 Bulk 和 Batch API

1、概览 尽管标准的 REST API 可以满足大多数常见需求,但在处理批量(Bulk)或批处理(Batch)操作时,基于 REST 的架构风格存在一些限制。 本文将带你了解如何在微服务中应用 Bulk 和 Batch 操作,以及如何实现一些自定义的面向 “写” 的 Bulk 和 Batch API。 2、 Bulk 和 Batch API 简介 Bulk 和 Batch 操作这两个术语经常被互换使用。不过,两者之间还是有硬性区别的。 通常,Bulk(批量)操作是指对同一类型的多个条目执行相同的操作。一种简单的方法是为每个请求调用相同的 API 来执行批量操作。这种方法可能太慢,而且会浪费资源。相反,我们可以在一次往返中处理多个条目。 我们可以通过在一次调用中对同一类型的多个条目执行相同的操作来实现 Bulk(批量)操作。这种对条目集合进行操作的方式可减少整体延迟并提高应用性能。要实现 Bulk(批量)操作,我们可以重用用于于单个条目的现有端点,或者为 Bulk(批量)方法创建一个单独的路由。 Batch(批处理)操作通常是指对多个资源类型执行不同的操作。Batch API 是在一次调用中对多个资源执行多个操作的集合。这些资源操作可能没有任何连贯性。每个请求路由可以是独立的,互相之间没有依赖关系。 简而言之,“Batch” 一词是指批量处理不同的请求。 目前,对于实现 Bulk 或 Batch 操作来说,缺乏明确定义的标准或规范。此外,许多流行的框架,如 Spring,没有内置的 Bulk(批量)操作支持。 尽管如此,在本文中,我们将使用常见的 REST 架构来实现自定义 Bulk 和 Batch 操作的方法。 3、Spring 示例应用 假设我们需要构建一个简单的微服务,同时支持 Bulk 和 Batch 操作。 3.1、Maven 依赖 首先,添加 spring-boot-starter-web 和 spring-boot-starter-validation 依赖: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>3.