diff --git a/spring-context/src/main/java/org/springframework/context/annotation/BeanAnnotationHelper.java b/spring-context/src/main/java/org/springframework/context/annotation/BeanAnnotationHelper.java index 4985d68c6e8..808aa64d11a 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/BeanAnnotationHelper.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/BeanAnnotationHelper.java @@ -45,17 +45,15 @@ abstract class BeanAnnotationHelper { public static String determineBeanNameFor(Method beanMethod, ConfigurableBeanFactory beanFactory) { String beanName = retrieveBeanNameFor(beanMethod); - if (!beanName.isEmpty()) { - return beanName; + if (beanFactory.getSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR) + instanceof ConfigurationBeanNameGenerator cbng) { + return cbng.deriveBeanName(MethodMetadata.introspect(beanMethod), (!beanName.isEmpty() ? beanName : null)); } - return (beanFactory.getSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR) - instanceof ConfigurationBeanNameGenerator cbng ? - cbng.deriveBeanName(MethodMetadata.introspect(beanMethod)) : beanMethod.getName()); + return determineBeanNameFrom(beanName, beanMethod); } public static String determineBeanNameFor(Method beanMethod) { - String beanName = retrieveBeanNameFor(beanMethod); - return (!beanName.isEmpty() ? beanName : beanMethod.getName()); + return determineBeanNameFrom(retrieveBeanNameFor(beanMethod), beanMethod); } private static String retrieveBeanNameFor(Method beanMethod) { @@ -77,6 +75,10 @@ abstract class BeanAnnotationHelper { return beanName; } + private static String determineBeanNameFrom(String derivedBeanName, Method beanMethod) { + return (!derivedBeanName.isEmpty() ? derivedBeanName : beanMethod.getName()); + } + public static boolean isScopedProxy(Method beanMethod) { Boolean scopedProxy = scopedProxyCache.get(beanMethod); if (scopedProxy == null) { @@ -88,4 +90,9 @@ abstract class BeanAnnotationHelper { return scopedProxy; } + static void clearCaches() { + scopedProxyCache.clear(); + beanNameCache.clear(); + } + } diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationBeanNameGenerator.java b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationBeanNameGenerator.java index 52f51affd43..2787c2b8245 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationBeanNameGenerator.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationBeanNameGenerator.java @@ -16,6 +16,8 @@ package org.springframework.context.annotation; +import org.jspecify.annotations.Nullable; + import org.springframework.beans.factory.support.BeanNameGenerator; import org.springframework.core.type.MethodMetadata; @@ -23,10 +25,10 @@ import org.springframework.core.type.MethodMetadata; * Extended variant of {@link BeanNameGenerator} for * {@link Configuration @Configuration} class purposes, not only covering * bean name generation for component and configuration classes themselves - * but also for {@link Bean @Bean} methods without a {@link Bean#name() name} - * attribute specified on the annotation itself. + * but also for {@link Bean @Bean} methods. * * @author Juergen Hoeller + * @author Stephane Nicoll * @since 7.0 * @see AnnotationConfigApplicationContext#setBeanNameGenerator * @see AnnotationConfigUtils#CONFIGURATION_BEAN_NAME_GENERATOR @@ -35,10 +37,11 @@ public interface ConfigurationBeanNameGenerator extends BeanNameGenerator { /** * Derive a default bean name for the given {@link Bean @Bean} method, - * in the absence of a {@link Bean#name() name} attribute specified. + * providing the {@link Bean#name() name} attribute specified. * @param beanMethod the method metadata for the {@link Bean @Bean} method + * @param beanName the {@link Bean#name() name} attribute or {@code null} if non is specified * @return the default bean name to use */ - String deriveBeanName(MethodMetadata beanMethod); + String deriveBeanName(MethodMetadata beanMethod, @Nullable String beanName); } diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java index 09bdae4bb8a..fe9fbd44ed0 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java @@ -197,22 +197,16 @@ class ConfigurationClassBeanDefinitionReader { // Consider name and any aliases. String[] explicitNames = bean.getStringArray("name"); - String beanName; - String localBeanName; - if (explicitNames.length > 0 && StringUtils.hasText(explicitNames[0])) { - beanName = explicitNames[0]; - localBeanName = beanName; + String beanName = (explicitNames.length > 0 && StringUtils.hasText(explicitNames[0])) ? explicitNames[0] : null; + String localBeanName = defaultBeanName(beanName, methodName); + beanName = (this.importBeanNameGenerator instanceof ConfigurationBeanNameGenerator cbng ? + cbng.deriveBeanName(metadata, beanName) : defaultBeanName(beanName, methodName)); + if (explicitNames.length > 0) { // Register aliases even when overridden below. for (int i = 1; i < explicitNames.length; i++) { this.registry.registerAlias(beanName, explicitNames[i]); } } - else { - // Default bean name derived from method name. - beanName = (this.importBeanNameGenerator instanceof ConfigurationBeanNameGenerator cbng ? - cbng.deriveBeanName(metadata) : methodName); - localBeanName = methodName; - } ConfigurationClassBeanDefinition beanDef = new ConfigurationClassBeanDefinition(configClass, metadata, localBeanName); @@ -306,6 +300,10 @@ class ConfigurationClassBeanDefinitionReader { this.registry.registerBeanDefinition(beanName, beanDefToRegister); } + private static String defaultBeanName(@Nullable String beanName, String methodName) { + return (beanName != null ? beanName : methodName); + } + @SuppressWarnings("NullAway") // Reflection private boolean isOverriddenByExistingDefinition( BeanMethod beanMethod, String beanName, ConfigurationClassBeanDefinition newBeanDef) { diff --git a/spring-context/src/main/java/org/springframework/context/annotation/FullyQualifiedConfigurationBeanNameGenerator.java b/spring-context/src/main/java/org/springframework/context/annotation/FullyQualifiedConfigurationBeanNameGenerator.java index 677d74dc11c..68e88ebfab2 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/FullyQualifiedConfigurationBeanNameGenerator.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/FullyQualifiedConfigurationBeanNameGenerator.java @@ -16,6 +16,8 @@ package org.springframework.context.annotation; +import org.jspecify.annotations.Nullable; + import org.springframework.core.type.MethodMetadata; /** @@ -23,8 +25,8 @@ import org.springframework.core.type.MethodMetadata; * {@link Configuration @Configuration} class purposes, not only enforcing * fully-qualified names for component and configuration classes themselves * but also fully-qualified default bean names ("className.methodName") for - * {@link Bean @Bean} methods. This only affects methods without an explicit - * {@link Bean#name() name} attribute specified. + * {@link Bean @Bean} methods. By default, this only affects methods without + * an explicit {@link Bean#name() name} attribute specified. * *
This provides an alternative to the default bean name generation for
* {@code @Bean} methods (which uses the plain method name), primarily for use
@@ -54,8 +56,8 @@ public class FullyQualifiedConfigurationBeanNameGenerator extends FullyQualified
@Override
- public String deriveBeanName(MethodMetadata beanMethod) {
- return beanMethod.getDeclaringClassName() + "." + beanMethod.getMethodName();
+ public String deriveBeanName(MethodMetadata beanMethod, @Nullable String beanName) {
+ return (beanName != null ? beanName : beanMethod.getDeclaringClassName() + "." + beanMethod.getMethodName());
}
}
diff --git a/spring-context/src/test/java/org/springframework/context/annotation/BeanAnnotationHelperTests.java b/spring-context/src/test/java/org/springframework/context/annotation/BeanAnnotationHelperTests.java
new file mode 100644
index 00000000000..f6aa54ded51
--- /dev/null
+++ b/spring-context/src/test/java/org/springframework/context/annotation/BeanAnnotationHelperTests.java
@@ -0,0 +1,199 @@
+/*
+ * 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.annotation;
+
+import java.lang.reflect.Method;
+import java.util.Objects;
+
+import org.jspecify.annotations.Nullable;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+
+import org.springframework.beans.factory.config.ConfigurableBeanFactory;
+import org.springframework.beans.factory.support.BeanNameGenerator;
+import org.springframework.beans.factory.support.DefaultListableBeanFactory;
+import org.springframework.util.ReflectionUtils;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.BDDMockito.given;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoInteractions;
+import static org.mockito.Mockito.when;
+
+/**
+ * Tests for {@link BeanAnnotationHelper}.
+ *
+ * @author Stephane Nicoll
+ */
+class BeanAnnotationHelperTests {
+
+ @BeforeEach
+ void clearCache() {
+ BeanAnnotationHelper.clearCaches();
+ }
+
+ @Test
+ void determineBeanNameWhenNoGeneratorAndNoBeanName() {
+ String beanName = BeanAnnotationHelper.determineBeanNameFor(
+ sampleMethod("beanWithoutName"), createBeanFactoryWithBeanNameGenerator(null));
+ assertThat(beanName).isEqualTo("beanWithoutName");
+ }
+
+ @ParameterizedTest
+ @ValueSource(strings = { "beanWithName", "beanWithMultipleNames" })
+ void determineBeanNameWhenNoGeneratorAndBeanName(String methodName) {
+ String beanName = BeanAnnotationHelper.determineBeanNameFor(
+ sampleMethod(methodName), createBeanFactoryWithBeanNameGenerator(null));
+ assertThat(beanName).isEqualTo("specificName");
+ }
+
+ @Test
+ void determineBeanNameWhenBeanNameGeneratorAndNoBeanName() {
+ BeanNameGenerator beanNameGenerator = mock(BeanNameGenerator.class);
+ String beanName = BeanAnnotationHelper.determineBeanNameFor(
+ sampleMethod("beanWithoutName"), createBeanFactoryWithBeanNameGenerator(beanNameGenerator));
+ assertThat(beanName).isEqualTo("beanWithoutName");
+ verifyNoInteractions(beanNameGenerator);
+ }
+
+ @ParameterizedTest
+ @ValueSource(strings = { "beanWithName", "beanWithMultipleNames" })
+ void determineBeanNameWhenBeanNameGeneratorAndBeanName(String methodName) {
+ BeanNameGenerator beanNameGenerator = mock(BeanNameGenerator.class);
+ String beanName = BeanAnnotationHelper.determineBeanNameFor(
+ sampleMethod(methodName), createBeanFactoryWithBeanNameGenerator(beanNameGenerator));
+ assertThat(beanName).isEqualTo("specificName");
+ verifyNoInteractions(beanNameGenerator);
+ }
+
+ @Test
+ void determineBeanNameWhenConfigurationBeanNameGeneratorAndNoBeanName() {
+ ConfigurationBeanNameGenerator beanNameGenerator = mock(ConfigurationBeanNameGenerator.class);
+ when(beanNameGenerator.deriveBeanName(any(), isNull())).thenReturn("generatedBeanName");
+ String beanName = BeanAnnotationHelper.determineBeanNameFor(
+ sampleMethod("beanWithoutName"), createBeanFactoryWithBeanNameGenerator(beanNameGenerator));
+ assertThat(beanName).isEqualTo("generatedBeanName");
+ verify(beanNameGenerator).deriveBeanName(any(), isNull());
+ }
+
+ @ParameterizedTest
+ @ValueSource(strings = { "beanWithName", "beanWithMultipleNames" })
+ void determineBeanNameWhenConfigurationBeanNameGeneratorAndBeanName(String methodName) {
+ ConfigurationBeanNameGenerator beanNameGenerator = mock(ConfigurationBeanNameGenerator.class);
+ given(beanNameGenerator.deriveBeanName(any(), eq("specificName"))).willReturn("generatedBeanName");
+ String beanName = BeanAnnotationHelper.determineBeanNameFor(
+ sampleMethod(methodName), createBeanFactoryWithBeanNameGenerator(beanNameGenerator));
+ assertThat(beanName).isEqualTo("generatedBeanName");
+ verify(beanNameGenerator).deriveBeanName(any(), eq("specificName"));
+ }
+
+ @Test
+ void determineBeanNameInCacheWhenNoGeneratorAndNoBeanName() {
+ Method method = sampleMethod("beanWithoutName");
+ ConfigurableBeanFactory beanFactory = createBeanFactoryWithBeanNameGenerator(null);
+ String beanName = BeanAnnotationHelper.determineBeanNameFor(method, beanFactory);
+ assertThat(BeanAnnotationHelper.determineBeanNameFor(method, beanFactory)).isEqualTo(beanName);
+ }
+
+ @ParameterizedTest
+ @ValueSource(strings = { "beanWithName", "beanWithMultipleNames" })
+ void determineBeanNameInCacheWhenNoGeneratorAndBeanName(String methodName) {
+ Method method = sampleMethod(methodName);
+ ConfigurableBeanFactory beanFactory = createBeanFactoryWithBeanNameGenerator(null);
+ String beanName = BeanAnnotationHelper.determineBeanNameFor(method, beanFactory);
+ assertThat(BeanAnnotationHelper.determineBeanNameFor(method, beanFactory)).isEqualTo(beanName);
+ }
+
+ @Test
+ void determineBeanNameInCacheWhenBeanNameGeneratorAndNoBeanName() {
+ BeanNameGenerator beanNameGenerator = mock(BeanNameGenerator.class);
+ Method method = sampleMethod("beanWithoutName");
+ ConfigurableBeanFactory beanFactory = createBeanFactoryWithBeanNameGenerator(beanNameGenerator);
+ String beanName = BeanAnnotationHelper.determineBeanNameFor(method, beanFactory);
+ assertThat(BeanAnnotationHelper.determineBeanNameFor(method, beanFactory)).isEqualTo(beanName);
+ verifyNoInteractions(beanNameGenerator);
+ }
+
+ @ParameterizedTest
+ @ValueSource(strings = { "beanWithName", "beanWithMultipleNames" })
+ void determineBeanNameInCacheWhenBeanNameGeneratorAndBeanName(String methodName) {
+ BeanNameGenerator beanNameGenerator = mock(BeanNameGenerator.class);
+ Method method = sampleMethod(methodName);
+ ConfigurableBeanFactory beanFactory = createBeanFactoryWithBeanNameGenerator(beanNameGenerator);
+ String beanName = BeanAnnotationHelper.determineBeanNameFor(method, beanFactory);
+ assertThat(BeanAnnotationHelper.determineBeanNameFor(method, beanFactory)).isEqualTo(beanName);
+ verifyNoInteractions(beanNameGenerator);
+ }
+
+ @Test
+ void determineBeanNameInCacheWhenConfigurationBeanNameGeneratorAndNoBeanName() {
+ ConfigurationBeanNameGenerator beanNameGenerator = mock(ConfigurationBeanNameGenerator.class);
+ when(beanNameGenerator.deriveBeanName(any(), isNull()))
+ .thenReturn("generatedBeanName").thenReturn("generatedBeanName");
+ Method method = sampleMethod("beanWithoutName");
+ ConfigurableBeanFactory beanFactory = createBeanFactoryWithBeanNameGenerator(beanNameGenerator);
+ String beanName = BeanAnnotationHelper.determineBeanNameFor(method, beanFactory);
+ assertThat(BeanAnnotationHelper.determineBeanNameFor(method, beanFactory)).isEqualTo(beanName);
+ verify(beanNameGenerator, times(2)).deriveBeanName(any(), isNull());
+ }
+
+ @ParameterizedTest
+ @ValueSource(strings = { "beanWithName", "beanWithMultipleNames" })
+ void determineBeanNameInCacheWhenConfigurationBeanNameGeneratorAndBeanName(String methodName) {
+ ConfigurationBeanNameGenerator beanNameGenerator = mock(ConfigurationBeanNameGenerator.class);
+ given(beanNameGenerator.deriveBeanName(any(), eq("specificName")))
+ .willReturn("generatedBeanName").willReturn("generatedBeanName");
+ Method method = sampleMethod(methodName);
+ ConfigurableBeanFactory beanFactory = createBeanFactoryWithBeanNameGenerator(beanNameGenerator);
+ String beanName = BeanAnnotationHelper.determineBeanNameFor(method, beanFactory);
+ assertThat(BeanAnnotationHelper.determineBeanNameFor(method, beanFactory)).isEqualTo(beanName);
+ verify(beanNameGenerator, times(2)).deriveBeanName(any(), eq("specificName"));
+ }
+
+ private static Method sampleMethod(String name) {
+ return Objects.requireNonNull(ReflectionUtils.findMethod(Samples.class, name));
+ }
+
+ private static ConfigurableBeanFactory createBeanFactoryWithBeanNameGenerator(@Nullable BeanNameGenerator beanNameGenerator) {
+ ConfigurableBeanFactory beanFactory = new DefaultListableBeanFactory();
+ if (beanNameGenerator != null) {
+ beanFactory.registerSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR, beanNameGenerator);
+ }
+ return beanFactory;
+ }
+
+
+ static class Samples {
+
+ @Bean
+ private void beanWithoutName() {}
+
+ @Bean(name = "specificName")
+ private void beanWithName() {}
+
+ @Bean(name = { "specificName", "specificName2", "specificName3" })
+ private void beanWithMultipleNames() {}
+
+ }
+}
diff --git a/spring-context/src/test/java/org/springframework/context/annotation/ConfigurationClassPostProcessorTests.java b/spring-context/src/test/java/org/springframework/context/annotation/ConfigurationClassPostProcessorTests.java
index 353c1586957..0e0f877b735 100644
--- a/spring-context/src/test/java/org/springframework/context/annotation/ConfigurationClassPostProcessorTests.java
+++ b/spring-context/src/test/java/org/springframework/context/annotation/ConfigurationClassPostProcessorTests.java
@@ -26,6 +26,8 @@ import java.util.Map;
import jakarta.annotation.PostConstruct;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.stubbing.Answer;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.aop.interceptor.SimpleTraceInterceptor;
@@ -50,6 +52,7 @@ import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
+import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.beans.factory.support.ChildBeanDefinition;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.RootBeanDefinition;
@@ -65,6 +68,7 @@ import org.springframework.core.env.StandardEnvironment;
import org.springframework.core.io.DescriptiveResource;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.core.task.SyncTaskExecutor;
+import org.springframework.core.type.MethodMetadata;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
@@ -72,11 +76,20 @@ import org.springframework.util.ClassUtils;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.BDDMockito.given;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoInteractions;
/**
+ * Tests for {@link ConfigurationClassPostProcessor}.
+ *
* @author Chris Beams
* @author Juergen Hoeller
* @author Sam Brannen
+ * @author Stephane Nicoll
*/
class ConfigurationClassPostProcessorTests {
@@ -536,6 +549,67 @@ class ConfigurationClassPostProcessorTests {
pp.postProcessBeanFactory(bf2)); // second invocation for bf2 -- should throw
}
+ @Test
+ void beanDefinitionsFromBeanMethodWithoutBeanNameGenerator() {
+ beanFactory.registerBeanDefinition("config", new RootBeanDefinition(BeanNamesConfig.class));
+ ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
+ pp.postProcessBeanFactory(beanFactory);
+ assertThat(beanFactory.getBeanDefinitionNames())
+ .containsOnly("config", "beanWithoutName", "specificName", "specificNames");
+ assertThat(beanFactory.getBean("beanWithoutName")).isEqualTo("beanWithoutName");
+ assertThat(beanFactory.getBean("specificName")).isEqualTo("beanWithName");
+ assertThat(beanFactory.getBean("specificNames")).isEqualTo("beanWithMultipleNames");
+ assertThat(beanFactory.getBean("specificNames2")).isEqualTo("beanWithMultipleNames");
+ assertThat(beanFactory.getBean("specificNames3")).isEqualTo("beanWithMultipleNames");
+ }
+
+ @Test
+ void beanDefinitionsFromBeanMethodWithBeanNameGenerator() {
+ BeanNameGenerator beanNameGenerator = mock(BeanNameGenerator.class);
+ beanFactory.registerBeanDefinition("config", new RootBeanDefinition(BeanNamesConfig.class));
+ ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
+ pp.setBeanNameGenerator(beanNameGenerator);
+ pp.postProcessBeanFactory(beanFactory);
+ assertThat(beanFactory.getBeanDefinitionNames())
+ .containsOnly("config", "beanWithoutName", "specificName", "specificNames");
+ assertThat(beanFactory.getBean("beanWithoutName")).isEqualTo("beanWithoutName");
+ assertThat(beanFactory.getBean("specificName")).isEqualTo("beanWithName");
+ assertThat(beanFactory.getBean("specificNames")).isEqualTo("beanWithMultipleNames");
+ assertThat(beanFactory.getBean("specificNames2")).isEqualTo("beanWithMultipleNames");
+ assertThat(beanFactory.getBean("specificNames3")).isEqualTo("beanWithMultipleNames");
+ verifyNoInteractions(beanNameGenerator);
+ }
+
+ @Test
+ void beanDefinitionsFromBeanMethodWithConfigurationBeanNameGenerator() {
+ ConfigurationBeanNameGenerator beanNameGenerator = mock(ConfigurationBeanNameGenerator.class);
+ Answer> answer = invocation -> {
+ MethodMetadata methodMetadata = invocation.getArgument(0);
+ String providedBeanName = invocation.getArgument(1);
+ return (providedBeanName != null) ? "test.fromBean." + providedBeanName : "test." + methodMetadata.getMethodName();
+ };
+ given(beanNameGenerator.deriveBeanName(any(), any())).willAnswer(answer).willAnswer(answer).willAnswer(answer);
+ beanFactory.registerBeanDefinition("config", new RootBeanDefinition(BeanNamesConfig.class));
+ ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
+ pp.setBeanNameGenerator(beanNameGenerator);
+ pp.postProcessBeanFactory(beanFactory);
+ assertThat(beanFactory.getBeanDefinitionNames())
+ .containsOnly("config", "test.beanWithoutName", "test.fromBean.specificName", "test.fromBean.specificNames");
+ assertThat(beanFactory.getBean("test.beanWithoutName")).isEqualTo("beanWithoutName");
+ assertThat(beanFactory.getBean("test.fromBean.specificName")).isEqualTo("beanWithName");
+ assertThat(beanFactory.getBean("test.fromBean.specificNames")).isEqualTo("beanWithMultipleNames");
+ assertThat(beanFactory.getBean("specificNames2")).isEqualTo("beanWithMultipleNames");
+ assertThat(beanFactory.getBean("specificNames3")).isEqualTo("beanWithMultipleNames");
+ ArgumentCaptor