mirror of
https://github.com/spring-projects/spring-framework.git
synced 2026-10-04 05:59:12 +00:00
Fix @Import with multiple bean registrars
This commit uses a MultiValueMap instead of a Map to store bean registrars, allowing to support multiple bean registrars imported by the same configuration class. Closes gh-35653
This commit is contained in:
+5
-3
@@ -36,6 +36,8 @@ import org.springframework.core.type.MethodMetadata;
|
||||
import org.springframework.core.type.classreading.MetadataReader;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
/**
|
||||
* Represents a user-defined {@link Configuration @Configuration} class.
|
||||
@@ -66,7 +68,7 @@ final class ConfigurationClass {
|
||||
private final Map<String, Class<? extends BeanDefinitionReader>> importedResources =
|
||||
new LinkedHashMap<>();
|
||||
|
||||
private final Map<String, BeanRegistrar> beanRegistrars = new LinkedHashMap<>();
|
||||
private final MultiValueMap<String, BeanRegistrar> beanRegistrars = new LinkedMultiValueMap<>();
|
||||
|
||||
private final Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> importBeanDefinitionRegistrars =
|
||||
new LinkedHashMap<>();
|
||||
@@ -224,10 +226,10 @@ final class ConfigurationClass {
|
||||
}
|
||||
|
||||
void addBeanRegistrar(String sourceClassName, BeanRegistrar beanRegistrar) {
|
||||
this.beanRegistrars.put(sourceClassName, beanRegistrar);
|
||||
this.beanRegistrars.add(sourceClassName, beanRegistrar);
|
||||
}
|
||||
|
||||
public Map<String, BeanRegistrar> getBeanRegistrars() {
|
||||
public MultiValueMap<String, BeanRegistrar> getBeanRegistrars() {
|
||||
return this.beanRegistrars;
|
||||
}
|
||||
|
||||
|
||||
+4
-3
@@ -56,6 +56,7 @@ import org.springframework.core.type.StandardAnnotationMetadata;
|
||||
import org.springframework.core.type.StandardMethodMetadata;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
@@ -421,13 +422,13 @@ class ConfigurationClassBeanDefinitionReader {
|
||||
registrar.registerBeanDefinitions(metadata, this.registry, this.importBeanNameGenerator));
|
||||
}
|
||||
|
||||
private void loadBeanDefinitionsFromBeanRegistrars(Map<String, BeanRegistrar> registrars) {
|
||||
private void loadBeanDefinitionsFromBeanRegistrars(MultiValueMap<String, BeanRegistrar> registrars) {
|
||||
if (!(this.registry instanceof ListableBeanFactory beanFactory)) {
|
||||
throw new IllegalStateException("Cannot support bean registrars since " +
|
||||
this.registry.getClass().getName() + " does not implement ListableBeanFactory");
|
||||
}
|
||||
registrars.values().forEach(registrar -> registrar.register(new BeanRegistryAdapter(
|
||||
this.registry, beanFactory, this.environment, registrar.getClass()), this.environment));
|
||||
registrars.values().forEach(registrarList -> registrarList.forEach(registrar -> registrar.register(new BeanRegistryAdapter(
|
||||
this.registry, beanFactory, this.environment, registrar.getClass()), this.environment)));
|
||||
}
|
||||
|
||||
|
||||
|
||||
+25
-24
@@ -199,7 +199,7 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
||||
|
||||
private List<PropertySourceDescriptor> propertySourceDescriptors = Collections.emptyList();
|
||||
|
||||
private final Map<String, BeanRegistrar> beanRegistrars = new LinkedHashMap<>();
|
||||
private final MultiValueMap<String, BeanRegistrar> beanRegistrars = new LinkedMultiValueMap<>();
|
||||
|
||||
|
||||
@Override
|
||||
@@ -453,7 +453,7 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
||||
}
|
||||
this.reader.loadBeanDefinitions(configClasses);
|
||||
for (ConfigurationClass configClass : configClasses) {
|
||||
this.beanRegistrars.putAll(configClass.getBeanRegistrars());
|
||||
this.beanRegistrars.addAll(configClass.getBeanRegistrars());
|
||||
}
|
||||
alreadyParsed.addAll(configClasses);
|
||||
processConfig.tag("classCount", () -> String.valueOf(configClasses.size())).end();
|
||||
@@ -857,13 +857,13 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
||||
|
||||
private static final String ENVIRONMENT_VARIABLE = "environment";
|
||||
|
||||
private final Map<String, BeanRegistrar> beanRegistrars;
|
||||
private final MultiValueMap<String, BeanRegistrar> beanRegistrars;
|
||||
|
||||
private final ConfigurableListableBeanFactory beanFactory;
|
||||
|
||||
private final AotServices<BeanRegistrationAotProcessor> aotProcessors;
|
||||
|
||||
public BeanRegistrarAotContribution(Map<String, BeanRegistrar> beanRegistrars, ConfigurableListableBeanFactory beanFactory) {
|
||||
public BeanRegistrarAotContribution(MultiValueMap<String, BeanRegistrar> beanRegistrars, ConfigurableListableBeanFactory beanFactory) {
|
||||
this.beanRegistrars = beanRegistrars;
|
||||
this.beanFactory = beanFactory;
|
||||
this.aotProcessors = AotServices.factoriesAndBeans(this.beanFactory).load(BeanRegistrationAotProcessor.class);
|
||||
@@ -948,28 +948,29 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
||||
Builder code = CodeBlock.builder();
|
||||
Builder metadataReaderFactoryCode = null;
|
||||
NameAllocator nameAllocator = new NameAllocator();
|
||||
for (Map.Entry<String, BeanRegistrar> beanRegistrarEntry : this.beanRegistrars.entrySet()) {
|
||||
BeanRegistrar beanRegistrar = beanRegistrarEntry.getValue();
|
||||
String beanRegistrarName = nameAllocator.newName(StringUtils.uncapitalize(beanRegistrar.getClass().getSimpleName()));
|
||||
code.addStatement("$T $L = new $T()", beanRegistrar.getClass(), beanRegistrarName, beanRegistrar.getClass());
|
||||
if (beanRegistrar instanceof ImportAware) {
|
||||
if (metadataReaderFactoryCode == null) {
|
||||
metadataReaderFactoryCode = CodeBlock.builder();
|
||||
metadataReaderFactoryCode.addStatement("$T metadataReaderFactory = new $T()",
|
||||
MetadataReaderFactory.class, CachingMetadataReaderFactory.class);
|
||||
for (Map.Entry<String, List<BeanRegistrar>> beanRegistrarEntry : this.beanRegistrars.entrySet()) {
|
||||
for (BeanRegistrar beanRegistrar : beanRegistrarEntry.getValue()) {
|
||||
String beanRegistrarName = nameAllocator.newName(StringUtils.uncapitalize(beanRegistrar.getClass().getSimpleName()));
|
||||
code.addStatement("$T $L = new $T()", beanRegistrar.getClass(), beanRegistrarName, beanRegistrar.getClass());
|
||||
if (beanRegistrar instanceof ImportAware) {
|
||||
if (metadataReaderFactoryCode == null) {
|
||||
metadataReaderFactoryCode = CodeBlock.builder();
|
||||
metadataReaderFactoryCode.addStatement("$T metadataReaderFactory = new $T()",
|
||||
MetadataReaderFactory.class, CachingMetadataReaderFactory.class);
|
||||
}
|
||||
code.beginControlFlow("try")
|
||||
.addStatement("$L.setImportMetadata(metadataReaderFactory.getMetadataReader($S).getAnnotationMetadata())",
|
||||
beanRegistrarName, beanRegistrarEntry.getKey())
|
||||
.nextControlFlow("catch ($T ex)", IOException.class)
|
||||
.addStatement("throw new $T(\"Failed to read metadata for '$L'\", ex)",
|
||||
IllegalStateException.class, beanRegistrarEntry.getKey())
|
||||
.endControlFlow();
|
||||
}
|
||||
code.beginControlFlow("try")
|
||||
.addStatement("$L.setImportMetadata(metadataReaderFactory.getMetadataReader($S).getAnnotationMetadata())",
|
||||
beanRegistrarName, beanRegistrarEntry.getKey())
|
||||
.nextControlFlow("catch ($T ex)", IOException.class)
|
||||
.addStatement("throw new $T(\"Failed to read metadata for '$L'\", ex)",
|
||||
IllegalStateException.class, beanRegistrarEntry.getKey())
|
||||
.endControlFlow();
|
||||
code.addStatement("$L.register(new $T(($T)$L, $L, $L, $T.class, $L), $L)", beanRegistrarName,
|
||||
BeanRegistryAdapter.class, BeanDefinitionRegistry.class, BeanFactoryInitializationCode.BEAN_FACTORY_VARIABLE,
|
||||
BeanFactoryInitializationCode.BEAN_FACTORY_VARIABLE, ENVIRONMENT_VARIABLE, beanRegistrar.getClass(),
|
||||
CUSTOMIZER_MAP_VARIABLE, ENVIRONMENT_VARIABLE);
|
||||
}
|
||||
code.addStatement("$L.register(new $T(($T)$L, $L, $L, $T.class, $L), $L)", beanRegistrarName,
|
||||
BeanRegistryAdapter.class, BeanDefinitionRegistry.class, BeanFactoryInitializationCode.BEAN_FACTORY_VARIABLE,
|
||||
BeanFactoryInitializationCode.BEAN_FACTORY_VARIABLE, ENVIRONMENT_VARIABLE, beanRegistrar.getClass(),
|
||||
CUSTOMIZER_MAP_VARIABLE, ENVIRONMENT_VARIABLE);
|
||||
}
|
||||
return (metadataReaderFactoryCode == null ? code.build() : metadataReaderFactoryCode.add(code.build()).build());
|
||||
}
|
||||
|
||||
+12
@@ -23,6 +23,8 @@ import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.testfixture.beans.factory.BarRegistrar;
|
||||
import org.springframework.context.testfixture.beans.factory.FooRegistrar;
|
||||
import org.springframework.context.testfixture.beans.factory.GenericBeanRegistrar;
|
||||
import org.springframework.context.testfixture.beans.factory.ImportAwareBeanRegistrar;
|
||||
import org.springframework.context.testfixture.beans.factory.SampleBeanRegistrar.Bar;
|
||||
@@ -32,6 +34,7 @@ import org.springframework.context.testfixture.beans.factory.SampleBeanRegistrar
|
||||
import org.springframework.context.testfixture.context.annotation.registrar.BeanRegistrarConfiguration;
|
||||
import org.springframework.context.testfixture.context.annotation.registrar.GenericBeanRegistrarConfiguration;
|
||||
import org.springframework.context.testfixture.context.annotation.registrar.ImportAwareBeanRegistrarConfiguration;
|
||||
import org.springframework.context.testfixture.context.annotation.registrar.MultipleBeanRegistrarsConfiguration;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
@@ -93,4 +96,13 @@ public class BeanRegistrarConfigurationTests {
|
||||
.isEqualTo(ImportAwareBeanRegistrarConfiguration.class.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
void multipleBeanRegistrars() {
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
|
||||
context.register(MultipleBeanRegistrarsConfiguration.class);
|
||||
context.refresh();
|
||||
assertThat(context.getBean(FooRegistrar.Foo.class)).isNotNull();
|
||||
assertThat(context.getBean(BarRegistrar.Bar.class)).isNotNull();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2002-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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.context.testfixture.beans.factory;
|
||||
|
||||
import org.springframework.beans.factory.BeanRegistrar;
|
||||
import org.springframework.beans.factory.BeanRegistry;
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
public class BarRegistrar implements BeanRegistrar {
|
||||
|
||||
@Override
|
||||
public void register(BeanRegistry registry, Environment env) {
|
||||
registry.registerBean(Bar.class);
|
||||
}
|
||||
|
||||
public record Bar() {}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2002-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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.context.testfixture.beans.factory;
|
||||
|
||||
import org.springframework.beans.factory.BeanRegistrar;
|
||||
import org.springframework.beans.factory.BeanRegistry;
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
public class FooRegistrar implements BeanRegistrar {
|
||||
|
||||
@Override
|
||||
public void register(BeanRegistry registry, Environment env) {
|
||||
registry.registerBean(Foo.class);
|
||||
}
|
||||
|
||||
public record Foo() {}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright 2002-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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.context.testfixture.context.annotation.registrar;
|
||||
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.context.testfixture.beans.factory.BarRegistrar;
|
||||
import org.springframework.context.testfixture.beans.factory.FooRegistrar;
|
||||
|
||||
@Import({FooRegistrar.class, BarRegistrar.class})
|
||||
public class MultipleBeanRegistrarsConfiguration {
|
||||
}
|
||||
Reference in New Issue
Block a user