Browse Source

Polishing.

Simplify code and tests.

See #3459
Original pull request: #3461
pull/3465/head
Mark Paluch 1 month ago
parent
commit
b8a84756fc
No known key found for this signature in database
GPG Key ID: 55BC6374BAA9D973
  1. 8
      src/main/java/org/springframework/data/repository/config/DeferredRepositoryInitializationListener.java
  2. 98
      src/test/java/org/springframework/data/repository/config/DeferredRepositoryInitializationListenerUnitTests.java

8
src/main/java/org/springframework/data/repository/config/DeferredRepositoryInitializationListener.java

@ -48,18 +48,12 @@ class DeferredRepositoryInitializationListener implements ApplicationListener<Co @@ -48,18 +48,12 @@ class DeferredRepositoryInitializationListener implements ApplicationListener<Co
public void onApplicationEvent(ContextRefreshedEvent event) {
ApplicationContext context = event.getApplicationContext();
// Ignore events from child contexts (e.g., Spring Cloud OpenFeign, LoadBalancer)
// to avoid premature repository initialization.
// See https://github.com/spring-projects/spring-boot/commit/708cbd72942e65906ea32ab3204af6c4e58a7314
if (context instanceof ConfigurableApplicationContext cac && cac.getBeanFactory() != beanFactory) {
if (context instanceof ConfigurableApplicationContext cac && !cac.getBeanFactory().equals(beanFactory)) {
return;
}
logger.info("Triggering deferred initialization of Spring Data repositories…");
beanFactory.getBeansOfType(Repository.class);
logger.info("Spring Data repositories initialized");
}

98
src/test/java/org/springframework/data/repository/config/DeferredRepositoryInitializationListenerUnitTests.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2018-present the original author or authors.
* Copyright 2026-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -19,11 +19,11 @@ import static org.mockito.Mockito.*; @@ -19,11 +19,11 @@ import static org.mockito.Mockito.*;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.support.GenericApplicationContext;
@ -32,105 +32,47 @@ import org.springframework.data.repository.Repository; @@ -32,105 +32,47 @@ import org.springframework.data.repository.Repository;
/**
* Unit tests for {@link DeferredRepositoryInitializationListener}.
*
* @author Oliver Drotbohm
* @author seongjun-rpls
* @author Seongjun Ha
* @author Mark Paluch
*/
@ExtendWith(MockitoExtension.class)
class DeferredRepositoryInitializationListenerUnitTests {
@Mock ListableBeanFactory beanFactory;
@Test // GH-3459
void triggersInitializationOnMatchingContextEvent() {
var context = new GenericApplicationContext();
context.refresh();
void triggersInitializationCorrectly() {
// Create listener with the real context's BeanFactory
var listener = new DeferredRepositoryInitializationListener(context.getBeanFactory());
var beanFactory = mock(DefaultListableBeanFactory.class);
var context = mock(ApplicationContext.class);
var listener = new DeferredRepositoryInitializationListener(beanFactory);
// Fire event from the same context
listener.onApplicationEvent(new ContextRefreshedEvent(context));
// The test verifies no exception is thrown and listener completes normally
// Since we're using real context, we can't easily mock getBeansOfType
context.close();
verify(beanFactory).getBeansOfType(Repository.class);
}
@Test // GH-3459
void ignoresEventsFromChildContext() {
void triggersInitializationForConfigurableApplicationContext() {
// Mock beanFactory that should NOT be called
ListableBeanFactory mockBeanFactory = mock(ListableBeanFactory.class);
var listener = new DeferredRepositoryInitializationListener(mockBeanFactory);
var beanFactory = mock(DefaultListableBeanFactory.class);
var context = mock(ConfigurableApplicationContext.class);
var listener = new DeferredRepositoryInitializationListener(beanFactory);
when(context.getBeanFactory()).thenReturn(beanFactory);
// Create a real context (will have different beanFactory)
var context = new GenericApplicationContext();
context.refresh();
// Fire event - should be ignored because context.getBeanFactory() != mockBeanFactory
listener.onApplicationEvent(new ContextRefreshedEvent(context));
// Verify the mock was never called
verify(mockBeanFactory, never()).getBeansOfType(Repository.class);
context.close();
verify(beanFactory).getBeansOfType(Repository.class);
}
@Test // GH-3459
void triggersInitializationOnParentContextEvent() {
// Create a mock beanFactory
DefaultListableBeanFactory mockBeanFactory = mock(DefaultListableBeanFactory.class);
// Create a mock ConfigurableApplicationContext
ConfigurableApplicationContext mockContext = mock(ConfigurableApplicationContext.class);
when(mockContext.getBeanFactory()).thenReturn(mockBeanFactory);
void ignoresEventsFromChildContext() {
// Create listener with the same mock beanFactory
var listener = new DeferredRepositoryInitializationListener(mockBeanFactory);
var beanFactory = mock(ListableBeanFactory.class);
var listener = new DeferredRepositoryInitializationListener(beanFactory);
// Fire event from the mock context
listener.onApplicationEvent(new ContextRefreshedEvent(mockContext));
listener.onApplicationEvent(new ContextRefreshedEvent(new GenericApplicationContext()));
// Verify getBeansOfType WAS called
verify(mockBeanFactory).getBeansOfType(Repository.class);
verify(beanFactory, never()).getBeansOfType(Repository.class);
}
@Test // GH-3459
void ignoresEventsFromSiblingChildContexts() {
// Create a mock beanFactory for the "parent" listener
DefaultListableBeanFactory mockParentBeanFactory = mock(DefaultListableBeanFactory.class);
// Create listener as if registered in a parent context
var listener = new DeferredRepositoryInitializationListener(mockParentBeanFactory);
// Create real child contexts (simulates Feign-like scenario)
var childContext1 = new GenericApplicationContext();
childContext1.refresh();
var childContext2 = new GenericApplicationContext();
childContext2.refresh();
// Fire events from child contexts - should be ignored
listener.onApplicationEvent(new ContextRefreshedEvent(childContext1));
listener.onApplicationEvent(new ContextRefreshedEvent(childContext2));
// Verify that getBeansOfType was NOT called for child context events
verify(mockParentBeanFactory, never()).getBeansOfType(Repository.class);
// Now create a mock parent context that returns our mock beanFactory
ConfigurableApplicationContext mockParentContext = mock(ConfigurableApplicationContext.class);
when(mockParentContext.getBeanFactory()).thenReturn(mockParentBeanFactory);
// Fire event from "parent" context
listener.onApplicationEvent(new ContextRefreshedEvent(mockParentContext));
// Verify that getBeansOfType WAS called exactly once for parent context
verify(mockParentBeanFactory, times(1)).getBeansOfType(Repository.class);
childContext1.close();
childContext2.close();
}
}

Loading…
Cancel
Save