在 MongoRepository 中使用 Skip 和 Limit

1、概览 Spring Data MongoDB 的 MongoRepository 接口提供了一种简单的方式与 MongoDB Collection 进行交互。 本文将带你了解如何在 MongoRepository 中使用 limit 和 skip。 2、初始设置 首先,创建一个名为 StudentRepository 的 Repository,用于存储 Student 信息: public interface StudentRepository extends MongoRepository<Student, String> {} 然后,向该 Repository 添加一些 Student 示例数据: @Before public void setUp() { Student student1 = new Student("A", "Abraham", 15L); Student student2 = new Student("B", "Usman", 30L); Student student3 = new Student("C", "David", 20L); Student student4 = new Student("D", "Tina", 45L); Student student5 = new Student("E", "Maria", 33L); studentList = Arrays.

如何在 Spring Boot 中指定 logback.xml 文件的位置?

1、概览 日志记录是任何软件应用的重要组件,用于监控、调试和维护系统的健康状况。在 Spring Boot 生态系统中,Logback 作为默认的日志记录框架,提供了灵活和强大的功能。虽然 Spring Boot 简化了应用的许多方面,但有时仍然需要通过 logback.xml 配置文件来配置 Logback 以满足特定要求。 本文将会带你了解如何在 Java、Spring Boot 应用中指定 logback.xml 配置文件的位置。 2、logback.xml logback.xml 文件是 Logback 的配置文件,用于定义日志记录规则、appender 和日志格式(log format)。 默认情况下,Logback 会在 classpath 根目录中搜索该文件。这意味着将 logback.xml 文件放在 Spring Boot 项目的 src/main/resources 目录中就足够了,因为 Logback 会在运行时自动检测到它。不过,在某些情况下,自定义其位置也是必要的。 3、指定 logback.xml 位置 3.1、使用 System Properties 如果 logback.xml 配置文件不在打包后的 Jar 中,可以使用 System Properties 指定其位置。例如,在运行 Spring Boot 应用时,可以使用 JVM 参数: java -Dlogback.configurationFile=/path/to/logback.xml -jar application.jar 命令 -Dlogback.configurationFile=/path/to/logback.xml 将系统属性 logback.configurationFile 设置为指定路径,指定 Logback 使用提供的配置文件。 3.2、编程式配置 logback.

在 WebFlux 中拦截请求并添加自定义请求头

1、概览 Filter(拦截器/过滤器)是 Spring 提供的一个机制,可以在 Controller 处理请求或向客户端返回响应之前拦截并处理请求。 本文将带你了解如何使用 WebFlux 拦截客户端请求以及如何添加自定义 Header。 2、Maven 依赖 添加 spring-boot-starter-webflux 响应式 Web 依赖: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> <version>3.1.5</version> </dependency> 3、拦截请求 Spring WebFlux Filter 可分为 WebFilter 和 HandlerFilterFunction 两类。可使用这些过滤器拦截 Web 请求,并添加新的自定义 Header 或修改现有 Header。 3.1、使用 WebFilter WebFilter 以链式拦截方式处理 Web 请求。WebFilter 在全局范围内生效,一旦启用,将拦截所有的请求和响应。 首先,定义基于注解的 Controller: @GetMapping(value= "/trace-annotated") public Mono<String> trace(@RequestHeader(name = "traceId") final String traceId) { return Mono.just("TraceId: ".concat(traceId)); } 然后,拦截 Web 请求,使用 TraceWebFilter 实现添加一个新的 Header traceId: @Component public class TraceWebFilter implements WebFilter { @Override public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) { exchange.

Spring Boot 3.2.5 发布

🐞 Bug 修复 BindValidationFailureAnalyzer 使用了错误的 target #40364 Log4j2LoggingSystem 通过一个永远不会被移除的 SpringEnvironmentPropertySource 污染 Log4j2 的环境 #40326 使用 Maven 时,配置 spring-boot.excludes 或 spring-boot-includes 用户属性会导致构建失败,提示:Cannot find default setter #40323 @ServletComponentScan 无法在模拟的 Web 环境中注册 Servlet 组件 #40321 在将 Log4j2 配置为使用单个 JVM 范围日志记录器上下文的情况下部署到 Tomcat 时,加载自定义的 deny-all 过滤器可能会导致 StackOverflowError 错误 #40312 Jetty 支持不设置虚拟线程名称 #40152 3.2.0 之后,当 Hibernate Scanner 未禁用时,可执行 JAR 应用的启动速度变慢 #40125 线程中断时,LaunchedClassLoader 可能会抛出 NoClassDefFoundError 错误 #40096 📔 文档 Producible 的 javadoc 中 @WriteOperation 和 @DeleteOperation 的链接文本有误 #40386 明确参数和构造函数绑定的要求 #40157 🔨 依赖升级 升级到 ActiveMQ 5.

在 Spring 测试中禁用 @EnableScheduling

1、简介 本文将带你了解如何测试启用了定时任务(@EnableScheduling)的 Spring 应用,以及如何在测试过程中禁用定时任务。 2、示例 首先来看一个示例,假设我们有一个系统,允许公司的代表向客户发送通知。其中一些通知是时间敏感的,应该立即发送,但有些通知应该等到下一个工作日再发送。因此,我们需要一个机制来定期尝试发送这些通知: public class DelayedNotificationScheduler { private NotificationService notificationService; @Scheduled(fixedDelayString = "${notification.send.out.delay}", initialDelayString = "${notification.send.out.initial.delay}") public void attemptSendingOutDelayedNotifications() { notificationService.sendOutDelayedNotifications(); } } attemptSendingOutDelayedNotifications() 方法上添加了 @Scheduled 注解。当 initialDelayString 配置的时间过去后,该方法将被首次调用。执行结束后,Spring 会在 fixedDelayString 参数配置的时间过后再次调用该方法。该方法本身将实际逻辑委托给了 NotificationService。 当然,我们还需要开启调度功能。为此,需要在 @Configuration 类上添加 @EnableScheduling 注解。 3、集成测试中定时任务的问题 首先,为通知应用编写一个基本的集成测试: @SpringBootTest( classes = { ApplicationConfig.class, SchedulerTestConfiguration.class }, properties = { "notification.send.out.delay: 10", "notification.send.out.initial.delay: 0" } ) public class DelayedNotificationSchedulerIntegrationTest { @Autowired private Clock testClock; @Autowired private NotificationRepository repository; @Autowired private DelayedNotificationScheduler scheduler; @Test public void whenTimeIsOverNotificationSendOutTime_thenItShouldBeSent() { ZonedDateTime fiveMinutesAgo = ZonedDateTime.

如何测试 Spring Application Event?

1、概览 本文将带你了解如何测试 Spring Application Event,以及如何使用 Spring Modulith 的测试库。 2、Application Event Spring 提供了 Application Event(观察者设计模式),允许组件在保持松散耦合的同时相互通信。我们可以使用 ApplicationEventPublisher Bean 发布内部事件,这些事件都是纯 Java 对象,所有已注册的监听器(Listener)都会收到通知。 例如,当成功创建 Order 时,OrderService 组件可以发布 OrderCompletedEvent: @Service public class OrderService { private final ApplicationEventPublisher eventPublisher; // 构造函数 public void placeOrder(String customerId, String... productIds) { Order order = new Order(customerId, Arrays.asList(productIds)); // 验证和下单的业务逻辑 OrderCompletedEvent event = new OrderCompletedEvent(savedOrder.id(), savedOrder.customerId(), savedOrder.timestamp()); eventPublisher.publishEvent(event); } } 把 OrderCompletedEvent 对象作为应用事件发布,不同模块中监听这些事件的组件会收到通知。 假设 LoyaltyPointsService 会对这些事件做出反应,以奖励下单客户积分。要实现这一点,可以使用 Spring 的 @EventListener 注解:

Spring Data JPA 出现异常后不回滚,继续处理事务

1、概览 JPA 中的事务机制是一个功能强大的工具,它通过提交所有更改或在出现异常时回滚更改来确保原子性和数据完整性。然而,在某些情况下,我们可能需要在遇到异常的情况下继续进行事务处理而不回滚数据更改。 2、出现异常后事务自动回滚 在事务中可能会出现两种主要的异常情况。 2.1、在 Service 层出现异常后回滚事务 我们可能遇到回滚(Rollback)的第一个地方是在 Service 层,外部异常可能会影响数据库更改。 让我们通过下面的示例更仔细地了解一下这种情况。首先,添加 InvoiceEntity,作为数据模型: @Entity @Table(uniqueConstraints = {@UniqueConstraint(columnNames = "serialNumber")}) public class InvoiceEntity { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Integer id; // 自增 ID private String serialNumber; // 序列号 private String description; // 说明 // Getter / Setter } 如上,包含了一个自增 ID、一个需要在整个系统中唯一的序列号和一个说明。 现在,创建负责发票事务操作的 InvoiceService: @Service public class InvoiceService { @Autowired private InvoiceRepository repository; @Transactional public void saveInvoice(InvoiceEntity invoice) { repository.save(invoice); sendNotification(); } private void sendNotification() { throw new NotificationSendingException("Notification sending is failed"); } } 在 saveInvoice() 方法中,我们添加了事务性保存发票(invoice)和发送相关通知的逻辑。但是,在发送通知的过程中,会抛出异常:

Spring Data JPA 查询 JSOB 类型的列

1、简介 Spring Data JPA 为与关系数据库交互提供了强大的抽象层。然而,传统的关系表可能并不适合存储复杂的、半结构化的数据,如产品详细信息或用户偏好。这就是 JSONB 数据类型的用武之地。 本文将带你学习使用 Spring Data JPA 查询 JSONB 列的各种方法。 2、JSONB 列 JSONB(JavaScript Object Notation for Databases)是一种数据类型,专门用于在 PostgreSQL 等关系数据库中存储 JSON 数据。它允许我们在单列中使用键值对和嵌套对象来表示复杂的数据结构。通过 JPA Provider(如 Hibernate),Spring Data JPA 允许我们将这些 JSONB 列映射到实体类中的属性。 3、映射 JSONB 列 我们可以使用 @Column 注解的 columnDefinition 属性,在实体类中明确定义列类型: @Column(columnDefinition = "jsonb") private String attributes; 这种方法主要与 PostgreSQL 相关,因为 PostgreSQL 本身支持 jsonb 数据类型。通过在实体类的相应属性中添加此注解,我们就能为数据库提供所需列类型的提示。Spring Data JPA 通常会根据数据库列定义自动检测 jsonb 数据类型,因此在很多情况下,此注解是可选的。 4、设置项目依赖和测试数据 创建一个基本的 Spring Boot 项目,其中包含测试 JSONB 查询所需的依赖和测试数据。 4.1、项目设置 首先,在 Maven pom.xml 文件中添加必要的依赖:

@Transactional 能和 @Async 一起用吗?

1、简介 本文将带你了解 Spring 中 @Transactional 和 @Async 注解之间的兼容性。 2、 了解 @Transactional 和 @Async @Transactional 注解是 Spring 提供的声明式事务注解。可以让多个业务方法在同一个事务中执行,只有所有方法都正常执行完毕后事务才会提交。如果任何一个方法在调用过程中抛出了异常,那么事务就会回滚。 @Async 注解用于执行异步任务,如果从一个线程调用 @Async 方法或类,Spring 会使用另一个线程来运行该方法,从而提高执行效率。 在有些情况下,我们需要在代码中同时使用 @Transactional 和 @Async 来保业务数据的一致性以及性能。 3、@Transactional 能和 @Async 一起用吗? 异步 和 事务 如果使用不当,可能会带来数据不一致等问题。 关于这一点,需要充分了解 Spring 的事务上下文和上下文之间的数据传播。 3.1、创建示例应用 本文使用银行的转账功能来说明事务和异步代码的使用。简而言之,是一个转账的场景,从一个账户中扣除资金并将其添加到另一个账户。 我们可以把它想象成数据库操作,比如 select 相关账户并 update 其资金余额: public void transfer(Long depositorId, Long favoredId, BigDecimal amount) { Account depositorAccount = accountRepository.findById(depositorId) .orElseThrow(IllegalArgumentException::new); Account favoredAccount = accountRepository.findById(favoredId) .orElseThrow(IllegalArgumentException::new); depositorAccount.setBalance(depositorAccount.getBalance().subtract(amount)); favoredAccount.setBalance(favoredAccount.getBalance().add(amount)); accountRepository.save(depositorAccount); accountRepository.save(favoredAccount); } 首先使用 findById() 查找相关账户,如果给定 ID 的账户不存在,则抛出 IllegalArgumentException 异常。

在 Spring Boot Filter 中获取响应体

1、简介 本文将带你了解如何在 Spring Boot Filter(过滤器)中获取 ServletResponse 的响应体。 2、场景 在使用 Spring Boot 中使用 Filter 时,从 ServletResponse 访问响应体非常麻烦。这是因为响应体不是随时可用的,它是在 Filter 链执行完毕后才写入输出流的。 但是,有些操作(如生成哈希签名)需要在发送给客户端之前读取完整的响应正文的内容。因此,需要找到读取响应体内容的方法。 3、使用 ContentCachingResponseWrapper 创建一个自定义 Filter,并使用 Spring 提供的 ContentCachingResponseWrapper 类进行包装: @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { ContentCachingResponseWrapper responseCacheWrapperObject = new ContentCachingResponseWrapper((HttpServletResponse) servletResponse); filterChain.doFilter(servletRequest, responseCacheWrapperObject); byte[] responseBody = responseCacheWrapperObject.getContentAsByteArray(); MessageDigest md5Digest = MessageDigest.getInstance("MD5"); byte[] md5Hash = md5Digest.digest(responseBody); String md5HashString = DatatypeConverter.printHexBinary(md5Hash); responseCacheWrapperObject.getResponse().setHeader("Response-Body-MD5", md5HashString); // ... } 简而言之,wrapper 类允许我们封装 HttpServletResponse 以缓存响应正文内容,并调用 doFilter() 将请求传递给下一个 Filter。