Avoid multiple DeferredRepositoryInitializationListener registrations in RepositoryConfigurationDelegate.

Closes #3287
Original pull request: #3219
This commit is contained in:
xeounxzxu
2024-12-09 21:28:08 +09:00
committed by Mark Paluch
parent 4ea94056cc
commit f9d5369ff1
2 changed files with 39 additions and 32 deletions

View File

@@ -15,28 +15,13 @@
*/
package org.springframework.data.repository.config;
import java.lang.reflect.TypeVariable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.beans.factory.config.DependencyDescriptor;
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
import org.springframework.beans.factory.support.AutowireCandidateResolver;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.support.*;
import org.springframework.context.annotation.ContextAnnotationAutowireCandidateResolver;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.ResolvableType;
@@ -57,6 +42,10 @@ import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.StopWatch;
import java.lang.reflect.TypeVariable;
import java.util.*;
import java.util.stream.Collectors;
/**
* Delegate for configuration integration to reuse the general way of detecting repositories. Customization is done by
* providing a configuration format specific {@link RepositoryConfigurationSource} (currently either XML or annotations
@@ -92,7 +81,7 @@ public class RepositoryConfigurationDelegate {
* @param environment must not be {@literal null}.
*/
public RepositoryConfigurationDelegate(RepositoryConfigurationSource configurationSource,
ResourceLoader resourceLoader, Environment environment) {
ResourceLoader resourceLoader, Environment environment) {
this.isXml = configurationSource instanceof XmlRepositoryConfigurationSource;
boolean isAnnotation = configurationSource instanceof AnnotationRepositoryConfigurationSource;
@@ -117,7 +106,7 @@ public class RepositoryConfigurationDelegate {
* {@link Environment}.
*/
private static Environment defaultEnvironment(@Nullable Environment environment,
@Nullable ResourceLoader resourceLoader) {
@Nullable ResourceLoader resourceLoader) {
if (environment != null) {
return environment;
@@ -136,7 +125,7 @@ public class RepositoryConfigurationDelegate {
* @see org.springframework.beans.factory.support.BeanDefinitionRegistry
*/
public List<BeanComponentDefinition> registerRepositoriesIn(BeanDefinitionRegistry registry,
RepositoryConfigurationExtension extension) {
RepositoryConfigurationExtension extension) {
if (logger.isInfoEnabled()) {
logger.info(LogMessage.format("Bootstrapping Spring Data %s repositories in %s mode.", //
@@ -222,7 +211,7 @@ public class RepositoryConfigurationDelegate {
}
private void registerAotComponents(BeanDefinitionRegistry registry, RepositoryConfigurationExtension extension,
Map<String, RepositoryConfigurationAdapter<?>> metadataByRepositoryBeanName) {
Map<String, RepositoryConfigurationAdapter<?>> metadataByRepositoryBeanName) {
BeanDefinitionBuilder repositoryAotProcessor = BeanDefinitionBuilder
.rootBeanDefinition(extension.getRepositoryAotProcessor()).setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
@@ -245,7 +234,7 @@ public class RepositoryConfigurationDelegate {
* @param registry must not be {@literal null}.
*/
private static void potentiallyLazifyRepositories(Map<String, RepositoryConfiguration<?>> configurations,
BeanDefinitionRegistry registry, BootstrapMode mode) {
BeanDefinitionRegistry registry, BootstrapMode mode) {
if (!DefaultListableBeanFactory.class.isInstance(registry) || BootstrapMode.DEFAULT.equals(mode)) {
return;
@@ -272,8 +261,10 @@ public class RepositoryConfigurationDelegate {
logger.debug("Registering deferred repository initialization listener.");
beanFactory.registerSingleton(DeferredRepositoryInitializationListener.class.getName(),
new DeferredRepositoryInitializationListener(beanFactory));
if (!beanFactory.containsBean(DeferredRepositoryInitializationListener.class.getName())) {
beanFactory.registerSingleton(DeferredRepositoryInitializationListener.class.getName(),
new DeferredRepositoryInitializationListener(beanFactory));
}
}
}

View File

@@ -15,13 +15,6 @@
*/
package org.springframework.data.repository.config;
import static org.assertj.core.api.Assertions.*;
import java.lang.reflect.TypeVariable;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mockito;
@@ -59,11 +52,19 @@ import org.springframework.data.repository.sample.AddressRepository;
import org.springframework.data.repository.sample.AddressRepositoryClient;
import org.springframework.data.repository.sample.ProductRepository;
import java.lang.reflect.TypeVariable;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Unit tests for {@link RepositoryConfigurationDelegate}.
*
* @author Oliver Gierke
* @author Mark Paluch
* @author xeounxzxu
* @soundtrack Richard Spaven - Tribute (Whole Other*)
*/
@ExtendWith(MockitoExtension.class)
@@ -109,7 +110,16 @@ class RepositoryConfigurationDelegateUnitTests {
var beanFactory = assertLazyRepositoryBeanSetup(DeferredConfig.class);
assertThat(beanFactory.getBeanNamesForType(DeferredRepositoryInitializationListener.class)).isNotEmpty();
}
@Test
void registersMultiDeferredRepositoryInitializationListener() {
var beanFactory = assertLazyRepositoryBeanSetup(DeferredConfig.class, OtherDeferredConfig.class);
assertThat(beanFactory.getBeanNamesForType(DeferredRepositoryInitializationListener.class)).isNotEmpty();
assertThat(beanFactory.getBeanNamesForType(AddressRepository.class)).isNotEmpty();
assertThat(beanFactory.getBeanNamesForType(ProductRepository.class)).isNotEmpty();
}
@Test // DATACMNS-1832
@@ -276,9 +286,9 @@ class RepositoryConfigurationDelegateUnitTests {
assertThat(it.getGeneric(1).resolve()).isEqualTo(Person.class);
}
private static ListableBeanFactory assertLazyRepositoryBeanSetup(Class<?> configClass) {
private static ListableBeanFactory assertLazyRepositoryBeanSetup(Class<?>... componentClasses) {
var context = new AnnotationConfigApplicationContext(configClass);
var context = new AnnotationConfigApplicationContext(componentClasses);
assertThat(context.getDefaultListableBeanFactory().getAutowireCandidateResolver())
.isInstanceOf(LazyRepositoryInjectionPointResolver.class);
@@ -309,6 +319,12 @@ class RepositoryConfigurationDelegateUnitTests {
bootstrapMode = BootstrapMode.DEFERRED)
static class DeferredConfig {}
@ComponentScan(basePackageClasses = ProductRepository.class)
@EnableRepositories(basePackageClasses = ProductRepository.class,
includeFilters = @Filter(type = FilterType.ASSIGNABLE_TYPE, classes = AddressRepository.class),
bootstrapMode = BootstrapMode.DEFERRED)
static class OtherDeferredConfig {}
@EnableRepositories(basePackageClasses = MyOtherRepository.class,
includeFilters = @Filter(type = FilterType.ASSIGNABLE_TYPE, classes = MyOtherRepository.class),
excludeFilters = @Filter(type = FilterType.ASSIGNABLE_TYPE, classes = MyOtherRepositoryImpl.class))