diff --git a/spring-aop/src/test/java/org/springframework/aop/scope/ScopedProxyBeanRegistrationAotProcessorTests.java b/spring-aop/src/test/java/org/springframework/aop/scope/ScopedProxyBeanRegistrationAotProcessorTests.java
index e6eeebe1878..a84de8b8b91 100644
--- a/spring-aop/src/test/java/org/springframework/aop/scope/ScopedProxyBeanRegistrationAotProcessorTests.java
+++ b/spring-aop/src/test/java/org/springframework/aop/scope/ScopedProxyBeanRegistrationAotProcessorTests.java
@@ -29,7 +29,7 @@ import org.springframework.aot.generate.MethodReference;
import org.springframework.aot.test.generator.compile.Compiled;
import org.springframework.aot.test.generator.compile.TestCompiler;
import org.springframework.beans.factory.BeanCreationException;
-import org.springframework.beans.factory.aot.AotFactoriesLoader;
+import org.springframework.beans.factory.aot.AotServices;
import org.springframework.beans.factory.aot.BeanFactoryInitializationAotContribution;
import org.springframework.beans.factory.aot.BeanRegistrationAotProcessor;
import org.springframework.beans.factory.aot.TestBeanRegistrationsAotProcessor;
@@ -77,7 +77,7 @@ class ScopedProxyBeanRegistrationAotProcessorTests {
@Test
void scopedProxyBeanRegistrationAotProcessorIsRegistered() {
- assertThat(new AotFactoriesLoader(this.beanFactory).load(BeanRegistrationAotProcessor.class))
+ assertThat(AotServices.factoriesAndBeans(this.beanFactory).load(BeanRegistrationAotProcessor.class))
.anyMatch(ScopedProxyBeanRegistrationAotProcessor.class::isInstance);
}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/aot/AotFactoriesLoader.java b/spring-beans/src/main/java/org/springframework/beans/factory/aot/AotFactoriesLoader.java
deleted file mode 100644
index 1b241d6eee1..00000000000
--- a/spring-beans/src/main/java/org/springframework/beans/factory/aot/AotFactoriesLoader.java
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright 2002-2022 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.beans.factory.aot;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-import org.springframework.beans.factory.BeanFactoryUtils;
-import org.springframework.beans.factory.ListableBeanFactory;
-import org.springframework.beans.factory.config.ConfigurableBeanFactory;
-import org.springframework.beans.factory.support.DefaultListableBeanFactory;
-import org.springframework.core.annotation.AnnotationAwareOrderComparator;
-import org.springframework.core.io.support.SpringFactoriesLoader;
-import org.springframework.util.Assert;
-
-/**
- * AOT specific factory loading mechanism for internal use within the framework.
- *
- *
Loads and instantiates factories of a given type from
- * {@value #FACTORIES_RESOURCE_LOCATION} and merges them with matching beans
- * from a {@link ListableBeanFactory}.
- *
- * @author Phillip Webb
- * @since 6.0
- * @see SpringFactoriesLoader
- */
-public class AotFactoriesLoader {
-
- /**
- * The location to look for AOT factories.
- */
- public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring/aot.factories";
-
-
- private final ListableBeanFactory beanFactory;
-
- private final SpringFactoriesLoader factoriesLoader;
-
-
- /**
- * Create a new {@link AotFactoriesLoader} instance backed by the given bean
- * factory.
- * @param beanFactory the bean factory to use
- */
- public AotFactoriesLoader(ListableBeanFactory beanFactory) {
- Assert.notNull(beanFactory, "'beanFactory' must not be null");
- ClassLoader classLoader = (beanFactory instanceof ConfigurableBeanFactory configurableBeanFactory)
- ? configurableBeanFactory.getBeanClassLoader() : null;
- this.beanFactory = beanFactory;
- this.factoriesLoader = SpringFactoriesLoader.forResourceLocation(FACTORIES_RESOURCE_LOCATION,
- classLoader);
- }
-
- /**
- * Create a new {@link AotFactoriesLoader} instance backed by the given bean
- * factory and loading items from the given {@link SpringFactoriesLoader}
- * rather than from {@value #FACTORIES_RESOURCE_LOCATION}.
- * @param beanFactory the bean factory to use
- * @param factoriesLoader the factories loader to use
- */
- public AotFactoriesLoader(ListableBeanFactory beanFactory,
- SpringFactoriesLoader factoriesLoader) {
-
- Assert.notNull(beanFactory, "'beanFactory' must not be null");
- Assert.notNull(factoriesLoader, "'factoriesLoader' must not be null");
- this.beanFactory = beanFactory;
- this.factoriesLoader = factoriesLoader;
- }
-
-
- /**
- * Load items from factories file and merge them with any beans defined in
- * the {@link DefaultListableBeanFactory}.
- * @param the item type
- * @param type the item type to load
- * @return a list of loaded instances
- */
- public List load(Class type) {
- List result = new ArrayList<>();
- result.addAll(BeanFactoryUtils
- .beansOfTypeIncludingAncestors(this.beanFactory, type, true, false)
- .values());
- result.addAll(this.factoriesLoader.load(type));
- AnnotationAwareOrderComparator.sort(result);
- return Collections.unmodifiableList(result);
- }
-
-}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/aot/AotServices.java b/spring-beans/src/main/java/org/springframework/beans/factory/aot/AotServices.java
new file mode 100644
index 00000000000..ab271011dfa
--- /dev/null
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/aot/AotServices.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright 2002-2022 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.beans.factory.aot;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Stream;
+
+import org.springframework.beans.factory.BeanFactoryUtils;
+import org.springframework.beans.factory.ListableBeanFactory;
+import org.springframework.beans.factory.config.ConfigurableBeanFactory;
+import org.springframework.core.annotation.AnnotationAwareOrderComparator;
+import org.springframework.core.io.support.SpringFactoriesLoader;
+import org.springframework.lang.Nullable;
+import org.springframework.util.Assert;
+
+/**
+ * A collection of AOT services that can be {@link Loader loaded} from
+ * a {@link SpringFactoriesLoader} or obtained from a {@link ListableBeanFactory}.
+ *
+ * @author Phillip Webb
+ * @since 6.0
+ * @param the service type
+ */
+public final class AotServices implements Iterable {
+
+ /**
+ * The location to look for AOT factories.
+ */
+ public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring/aot.factories";
+
+ private final List services;
+
+ private final Map beans;
+
+
+ private AotServices(List loaded, Map beans) {
+ List services = new ArrayList<>();
+ services.addAll(beans.values());
+ services.addAll(loaded);
+ AnnotationAwareOrderComparator.sort(services);
+ this.services = Collections.unmodifiableList(services);
+ this.beans = beans;
+ }
+
+
+ /**
+ * Return a new {@link Loader} that will obtain AOT services from
+ * {@value #FACTORIES_RESOURCE_LOCATION}.
+ * @return a new {@link Loader} instance
+ */
+ public static Loader factories() {
+ return factories((ClassLoader) null);
+ }
+
+ /**
+ * Return a new {@link Loader} that will obtain AOT services from
+ * {@value #FACTORIES_RESOURCE_LOCATION}.
+ * @param classLoader the class loader used to load the factories resource
+ * @return a new {@link Loader} instance
+ */
+ public static Loader factories(@Nullable ClassLoader classLoader) {
+ return factories(getSpringFactoriesLoader(classLoader));
+ }
+
+ /**
+ * Return a new {@link Loader} that will obtain AOT services from the given
+ * {@link SpringFactoriesLoader}.
+ * @param springFactoriesLoader the spring factories loader
+ * @return a new {@link Loader} instance
+ */
+ public static Loader factories(SpringFactoriesLoader springFactoriesLoader) {
+ Assert.notNull(springFactoriesLoader, "'springFactoriesLoader' must not be null");
+ return new Loader(springFactoriesLoader, null);
+ }
+
+ /**
+ * Return a new {@link Loader} that will obtain AOT services from
+ * {@value #FACTORIES_RESOURCE_LOCATION} as well as the given
+ * {@link ListableBeanFactory}.
+ * @param beanFactory the bean factory
+ * @return a new {@link Loader} instance
+ */
+ public static Loader factoriesAndBeans(ListableBeanFactory beanFactory) {
+ ClassLoader classLoader = (beanFactory instanceof ConfigurableBeanFactory configurableBeanFactory)
+ ? configurableBeanFactory.getBeanClassLoader() : null;
+ return factoriesAndBeans(getSpringFactoriesLoader(classLoader), beanFactory);
+ }
+
+ /**
+ * Return a new {@link Loader} that will obtain AOT services from the given
+ * {@link SpringFactoriesLoader} and {@link ListableBeanFactory}.
+ * @param springFactoriesLoader the spring factories loader
+ * @param beanFactory the bean factory
+ * @return a new {@link Loader} instance
+ */
+ public static Loader factoriesAndBeans(SpringFactoriesLoader springFactoriesLoader, ListableBeanFactory beanFactory) {
+ Assert.notNull(beanFactory, "'beanFactory' must not be null");
+ Assert.notNull(springFactoriesLoader, "'springFactoriesLoader' must not be null");
+ return new Loader(springFactoriesLoader, beanFactory);
+ }
+
+ private static SpringFactoriesLoader getSpringFactoriesLoader(
+ @Nullable ClassLoader classLoader) {
+ return SpringFactoriesLoader.forResourceLocation(FACTORIES_RESOURCE_LOCATION,
+ classLoader);
+ }
+
+ @Override
+ public Iterator iterator() {
+ return this.services.iterator();
+ }
+
+ /**
+ * Return a {@link Stream} of the AOT services.
+ * @return a stream of the services
+ */
+ public Stream stream() {
+ return this.services.stream();
+ }
+
+ /**
+ * Return the AOT services as a {@link List}.
+ * @return a list of the services
+ */
+ public List asList() {
+ return this.services;
+ }
+
+ /**
+ * Return the AOT services that was loaded for the given bean name.
+ * @param beanName the bean name
+ * @return the AOT service or {@code null}
+ */
+ @Nullable
+ public T findByBeanName(String beanName) {
+ return this.beans.get(beanName);
+ }
+
+
+ /**
+ * Loader class used to actually load the services.
+ */
+ public static class Loader {
+
+ private final SpringFactoriesLoader springFactoriesLoader;
+
+ private final ListableBeanFactory beanFactory;
+
+
+ Loader(SpringFactoriesLoader springFactoriesLoader, @Nullable ListableBeanFactory beanFactory) {
+ this.springFactoriesLoader = springFactoriesLoader;
+ this.beanFactory = beanFactory;
+ }
+
+
+ /**
+ * Load all AOT services of the given type.
+ * @param the service type
+ * @param type the service type
+ * @return a new {@link AotServices} instance
+ */
+ public AotServices load(Class type) {
+ return new AotServices(this.springFactoriesLoader.load(type), loadBeans(type));
+ }
+
+ private Map loadBeans(Class type) {
+ return (this.beanFactory != null) ? BeanFactoryUtils
+ .beansOfTypeIncludingAncestors(this.beanFactory, type, true, false)
+ : Collections.emptyMap();
+ }
+
+ }
+
+}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanDefinitionMethodGeneratorFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanDefinitionMethodGeneratorFactory.java
index 341b76366eb..d18d76b297d 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanDefinitionMethodGeneratorFactory.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanDefinitionMethodGeneratorFactory.java
@@ -43,9 +43,9 @@ class BeanDefinitionMethodGeneratorFactory {
.getLog(BeanDefinitionMethodGeneratorFactory.class);
- private final List aotProcessors;
+ private final AotServices aotProcessors;
- private final List excludeFilters;
+ private final AotServices excludeFilters;
/**
@@ -54,15 +54,15 @@ class BeanDefinitionMethodGeneratorFactory {
* @param beanFactory the bean factory use
*/
BeanDefinitionMethodGeneratorFactory(ConfigurableListableBeanFactory beanFactory) {
- this(new AotFactoriesLoader(beanFactory));
+ this(AotServices.factoriesAndBeans(beanFactory));
}
/**
* Create a new {@link BeanDefinitionMethodGeneratorFactory} backed by the
- * given {@link AotFactoriesLoader}.
- * @param loader the AOT factory loader to use
+ * given {@link AotServices.Loader}.
+ * @param loader the AOT services loader to use
*/
- BeanDefinitionMethodGeneratorFactory(AotFactoriesLoader loader) {
+ BeanDefinitionMethodGeneratorFactory(AotServices.Loader loader) {
this.aotProcessors = loader.load(BeanRegistrationAotProcessor.class);
this.excludeFilters = loader.load(BeanRegistrationExcludeFilter.class);
}
diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/annotation/JakartaAnnotationsRuntimeHintsTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/annotation/JakartaAnnotationsRuntimeHintsTests.java
index f8e604fa1bf..9e06e5a1176 100644
--- a/spring-beans/src/test/java/org/springframework/beans/factory/annotation/JakartaAnnotationsRuntimeHintsTests.java
+++ b/spring-beans/src/test/java/org/springframework/beans/factory/annotation/JakartaAnnotationsRuntimeHintsTests.java
@@ -26,8 +26,7 @@ import org.springframework.aot.hint.MemberCategory;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.RuntimeHintsRegistrar;
import org.springframework.aot.hint.predicate.RuntimeHintsPredicates;
-import org.springframework.beans.factory.aot.AotFactoriesLoader;
-import org.springframework.core.io.support.SpringFactoriesLoader;
+import org.springframework.beans.factory.aot.AotServices;
import org.springframework.util.ClassUtils;
import static org.assertj.core.api.Assertions.assertThat;
@@ -43,9 +42,9 @@ class JakartaAnnotationsRuntimeHintsTests {
@BeforeEach
void setup() {
- SpringFactoriesLoader.forResourceLocation(AotFactoriesLoader.FACTORIES_RESOURCE_LOCATION)
- .load(RuntimeHintsRegistrar.class).forEach(registrar -> registrar
- .registerHints(this.hints, ClassUtils.getDefaultClassLoader()));
+ AotServices.factories().load(RuntimeHintsRegistrar.class)
+ .forEach(registrar -> registrar.registerHints(this.hints,
+ ClassUtils.getDefaultClassLoader()));
}
@Test
diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/aot/AotFactoriesLoaderTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/aot/AotFactoriesLoaderTests.java
deleted file mode 100644
index 8d5d7359e33..00000000000
--- a/spring-beans/src/test/java/org/springframework/beans/factory/aot/AotFactoriesLoaderTests.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright 2002-2022 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.beans.factory.aot;
-
-import java.util.List;
-
-import org.junit.jupiter.api.Test;
-
-import org.springframework.beans.factory.ListableBeanFactory;
-import org.springframework.beans.factory.support.DefaultListableBeanFactory;
-import org.springframework.core.Ordered;
-import org.springframework.core.mock.MockSpringFactoriesLoader;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
-
-/**
- * Tests for {@link AotFactoriesLoader}.
- *
- * @author Phillip Webb
- */
-class AotFactoriesLoaderTests {
-
- @Test
- void createWhenBeanFactoryIsNullThrowsException() {
- assertThatIllegalArgumentException()
- .isThrownBy(() -> new AotFactoriesLoader(null))
- .withMessage("'beanFactory' must not be null");
- }
-
- @Test
- void createWhenSpringFactoriesLoaderIsNullThrowsException() {
- ListableBeanFactory beanFactory = new DefaultListableBeanFactory();
- assertThatIllegalArgumentException()
- .isThrownBy(() -> new AotFactoriesLoader(beanFactory, null))
- .withMessage("'factoriesLoader' must not be null");
- }
-
- @Test
- void loadLoadsFromBeanFactoryAndSpringFactoriesLoaderInOrder() {
- DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
- beanFactory.registerSingleton("b1", new TestFactoryImpl(0, "b1"));
- beanFactory.registerSingleton("b2", new TestFactoryImpl(2, "b2"));
- MockSpringFactoriesLoader springFactoriesLoader = new MockSpringFactoriesLoader();
- springFactoriesLoader.addInstance(TestFactory.class, new TestFactoryImpl(1, "l1"));
- springFactoriesLoader.addInstance(TestFactory.class, new TestFactoryImpl(3, "l2"));
- AotFactoriesLoader loader = new AotFactoriesLoader(beanFactory, springFactoriesLoader);
- List loaded = loader.load(TestFactory.class);
- assertThat(loaded).map(Object::toString).containsExactly("b1", "l1", "b2", "l2");
- }
-
- interface TestFactory {
- }
-
- static class TestFactoryImpl implements TestFactory, Ordered {
-
- private final int order;
-
- private final String name;
-
- TestFactoryImpl(int order, String name) {
- this.order = order;
- this.name = name;
- }
-
- @Override
- public int getOrder() {
- return this.order;
- }
-
- @Override
- public String toString() {
- return this.name;
- }
-
- }
-
-}
diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/aot/AotServicesTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/aot/AotServicesTests.java
new file mode 100644
index 00000000000..8d11204ed03
--- /dev/null
+++ b/spring-beans/src/test/java/org/springframework/beans/factory/aot/AotServicesTests.java
@@ -0,0 +1,227 @@
+/*
+ * Copyright 2002-2022 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.beans.factory.aot;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.Enumeration;
+
+import org.junit.jupiter.api.Test;
+
+import org.springframework.beans.factory.support.DefaultListableBeanFactory;
+import org.springframework.beans.factory.support.RootBeanDefinition;
+import org.springframework.core.Ordered;
+import org.springframework.core.io.support.SpringFactoriesLoader;
+import org.springframework.core.mock.MockSpringFactoriesLoader;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
+
+/**
+ * Tests for {@link AotServices}.
+ *
+ * @author Phillip Webb
+ */
+class AotServicesTests {
+
+ @Test
+ void factoriesLoadsFromAotFactoriesFiles() {
+ AotServices> loaded = AotServices.factories()
+ .load(BeanFactoryInitializationAotProcessor.class);
+ assertThat(loaded)
+ .anyMatch(BeanFactoryInitializationAotProcessor.class::isInstance);
+ }
+
+ @Test
+ void factoriesWithClassLoaderLoadsFromAotFactoriesFile() {
+ TestSpringFactoriesClassLoader classLoader = new TestSpringFactoriesClassLoader(
+ "aot-services.factories");
+ AotServices> loaded = AotServices.factories(classLoader)
+ .load(TestService.class);
+ assertThat(loaded).anyMatch(TestServiceImpl.class::isInstance);
+ }
+
+ @Test
+ void factoriesWithSpringFactoriesLoaderWhenSpringFactoriesLoaderIsNullThrowsException() {
+ assertThatIllegalArgumentException()
+ .isThrownBy(() -> AotServices.factories((SpringFactoriesLoader) null))
+ .withMessage("'springFactoriesLoader' must not be null");
+ }
+
+ @Test
+ void factoriesWithSpringFactoriesLoaderLoadsFromSpringFactoriesLoader() {
+ MockSpringFactoriesLoader loader = new MockSpringFactoriesLoader();
+ loader.addInstance(TestService.class, new TestServiceImpl());
+ AotServices> loaded = AotServices.factories(loader).load(TestService.class);
+ assertThat(loaded).anyMatch(TestServiceImpl.class::isInstance);
+ }
+
+ @Test
+ void factoriesAndBeansWhenBeanFactoryIsNullThrowsException() {
+ assertThatIllegalArgumentException()
+ .isThrownBy(() -> AotServices.factoriesAndBeans(null))
+ .withMessage("'beanFactory' must not be null");
+ }
+
+ @Test
+ void factoriesAndBeansLoadsFromFactoriesAndBeanFactory() {
+ DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
+ beanFactory.setBeanClassLoader(
+ new TestSpringFactoriesClassLoader("aot-services.factories"));
+ beanFactory.registerBeanDefinition("test", new RootBeanDefinition(TestBean.class));
+ AotServices> loaded = AotServices.factoriesAndBeans(beanFactory).load(TestService.class);
+ assertThat(loaded).anyMatch(TestServiceImpl.class::isInstance);
+ assertThat(loaded).anyMatch(TestBean.class::isInstance);
+ }
+
+ @Test
+ void factoriesAndBeansWithSpringFactoriesLoaderLoadsFromSpringFactoriesLoaderAndBeanFactory() {
+ MockSpringFactoriesLoader loader = new MockSpringFactoriesLoader();
+ loader.addInstance(TestService.class, new TestServiceImpl());
+ DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
+ beanFactory.registerBeanDefinition("test", new RootBeanDefinition(TestBean.class));
+ AotServices> loaded = AotServices.factoriesAndBeans(loader, beanFactory).load(TestService.class);
+ assertThat(loaded).anyMatch(TestServiceImpl.class::isInstance);
+ assertThat(loaded).anyMatch(TestBean.class::isInstance);
+ }
+
+ @Test
+ void factoriesAndBeansWithSpringFactoriesLoaderWhenSpringFactoriesLoaderIsNullThrowsException() {
+ DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
+ assertThatIllegalArgumentException()
+ .isThrownBy(() -> AotServices.factoriesAndBeans(null, beanFactory))
+ .withMessage("'springFactoriesLoader' must not be null");
+ }
+
+ @Test
+ void iteratorReturnsServicesIterator() {
+ AotServices> loaded = AotServices
+ .factories(new TestSpringFactoriesClassLoader("aot-services.factories"))
+ .load(TestService.class);
+ assertThat(loaded.iterator().next()).isInstanceOf(TestServiceImpl.class);
+ }
+
+ @Test
+ void streamReturnsServicesStream() {
+ AotServices> loaded = AotServices
+ .factories(new TestSpringFactoriesClassLoader("aot-services.factories"))
+ .load(TestService.class);
+ assertThat(loaded.stream()).anyMatch(TestServiceImpl.class::isInstance);
+ }
+
+ @Test
+ void asListReturnsServicesList() {
+ AotServices> loaded = AotServices
+ .factories(new TestSpringFactoriesClassLoader("aot-services.factories"))
+ .load(TestService.class);
+ assertThat(loaded.asList()).anyMatch(TestServiceImpl.class::isInstance);
+ }
+
+ @Test
+ void findByBeanNameWhenMatchReturnsService() {
+ DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
+ beanFactory.registerBeanDefinition("test", new RootBeanDefinition(TestBean.class));
+ AotServices> loaded = AotServices.factoriesAndBeans(beanFactory).load(TestService.class);
+ assertThat(loaded.findByBeanName("test")).isInstanceOf(TestBean.class);
+ }
+
+ @Test
+ void findByBeanNameWhenNoMatchReturnsNull() {
+ DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
+ beanFactory.registerBeanDefinition("test", new RootBeanDefinition(TestBean.class));
+ AotServices> loaded = AotServices.factoriesAndBeans(beanFactory).load(TestService.class);
+ assertThat(loaded.findByBeanName("missing")).isNull();
+ }
+
+ @Test
+ void loadLoadsFromBeanFactoryAndSpringFactoriesLoaderInOrder() {
+ DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
+ beanFactory.registerSingleton("b1", new TestServiceImpl(0, "b1"));
+ beanFactory.registerSingleton("b2", new TestServiceImpl(2, "b2"));
+ MockSpringFactoriesLoader springFactoriesLoader = new MockSpringFactoriesLoader();
+ springFactoriesLoader.addInstance(TestService.class,
+ new TestServiceImpl(1, "l1"));
+ springFactoriesLoader.addInstance(TestService.class,
+ new TestServiceImpl(3, "l2"));
+ Iterable loaded = AotServices
+ .factoriesAndBeans(springFactoriesLoader, beanFactory)
+ .load(TestService.class);
+ assertThat(loaded).map(Object::toString).containsExactly("b1", "l1", "b2", "l2");
+ }
+
+
+ interface TestService {
+ }
+
+
+ static class TestServiceImpl implements TestService, Ordered {
+
+ private final int order;
+
+ private final String name;
+
+
+ TestServiceImpl() {
+ this(0, "test");
+ }
+
+ TestServiceImpl(int order, String name) {
+ this.order = order;
+ this.name = name;
+ }
+
+
+ @Override
+ public int getOrder() {
+ return this.order;
+ }
+
+ @Override
+ public String toString() {
+ return this.name;
+ }
+
+ }
+
+
+ static class TestBean implements TestService {
+
+ }
+
+
+ static class TestSpringFactoriesClassLoader extends ClassLoader {
+
+ private final String factoriesName;
+
+
+ TestSpringFactoriesClassLoader(String factoriesName) {
+ super(Thread.currentThread().getContextClassLoader());
+ this.factoriesName = factoriesName;
+ }
+
+
+ @Override
+ public Enumeration getResources(String name) throws IOException {
+ return (!"META-INF/spring/aot.factories".equals(name))
+ ? super.getResources(name)
+ : super.getResources("org/springframework/beans/factory/aot/"
+ + this.factoriesName);
+ }
+
+ }
+
+}
diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanDefinitionMethodGeneratorFactoryTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanDefinitionMethodGeneratorFactoryTests.java
index ba58f2ec1da..95f1e0da753 100644
--- a/spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanDefinitionMethodGeneratorFactoryTests.java
+++ b/spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanDefinitionMethodGeneratorFactoryTests.java
@@ -43,7 +43,7 @@ class BeanDefinitionMethodGeneratorFactoryTests {
new MockBeanRegistrationExcludeFilter(true, 0));
RegisteredBean registeredBean = registerTestBean(beanFactory);
BeanDefinitionMethodGeneratorFactory methodGeneratorFactory = new BeanDefinitionMethodGeneratorFactory(
- new AotFactoriesLoader(beanFactory, springFactoriesLoader));
+ AotServices.factoriesAndBeans(springFactoriesLoader, beanFactory));
assertThat(methodGeneratorFactory.getBeanDefinitionMethodGenerator(registeredBean,
null)).isNull();
}
@@ -56,7 +56,7 @@ class BeanDefinitionMethodGeneratorFactoryTests {
beanFactory.registerSingleton("filter",
new MockBeanRegistrationExcludeFilter(true, 0));
BeanDefinitionMethodGeneratorFactory methodGeneratorFactory = new BeanDefinitionMethodGeneratorFactory(
- new AotFactoriesLoader(beanFactory, springFactoriesLoader));
+ AotServices.factoriesAndBeans(springFactoriesLoader, beanFactory));
assertThat(methodGeneratorFactory.getBeanDefinitionMethodGenerator(registeredBean,
null)).isNull();
}
@@ -77,7 +77,7 @@ class BeanDefinitionMethodGeneratorFactoryTests {
beanFactory.registerSingleton("filter6", filter6);
RegisteredBean registeredBean = registerTestBean(beanFactory);
BeanDefinitionMethodGeneratorFactory methodGeneratorFactory = new BeanDefinitionMethodGeneratorFactory(
- new AotFactoriesLoader(beanFactory, springFactoriesLoader));
+ AotServices.factoriesAndBeans(springFactoriesLoader, beanFactory));
assertThat(methodGeneratorFactory.getBeanDefinitionMethodGenerator(registeredBean,
null)).isNull();
assertThat(filter1.wasCalled()).isTrue();
@@ -103,7 +103,7 @@ class BeanDefinitionMethodGeneratorFactoryTests {
loaderProcessor);
RegisteredBean registeredBean = registerTestBean(beanFactory);
BeanDefinitionMethodGeneratorFactory methodGeneratorFactory = new BeanDefinitionMethodGeneratorFactory(
- new AotFactoriesLoader(beanFactory, springFactoriesLoader));
+ AotServices.factoriesAndBeans(springFactoriesLoader, beanFactory));
BeanDefinitionMethodGenerator methodGenerator = methodGeneratorFactory
.getBeanDefinitionMethodGenerator(registeredBean, null);
assertThat(methodGenerator).extracting("aotContributions").asList()
@@ -121,7 +121,7 @@ class BeanDefinitionMethodGeneratorFactoryTests {
.rootBeanDefinition(TestBeanRegistrationAotProcessorBean.class).getBeanDefinition());
RegisteredBean registeredBean2 = RegisteredBean.of(beanFactory, "test2");
BeanDefinitionMethodGeneratorFactory methodGeneratorFactory = new BeanDefinitionMethodGeneratorFactory(
- new AotFactoriesLoader(beanFactory, springFactoriesLoader));
+ AotServices.factoriesAndBeans(springFactoriesLoader, beanFactory));
assertThat(methodGeneratorFactory.getBeanDefinitionMethodGenerator(registeredBean1, null)).isNull();
assertThat(methodGeneratorFactory.getBeanDefinitionMethodGenerator(registeredBean2, null)).isNull();
}
@@ -134,7 +134,7 @@ class BeanDefinitionMethodGeneratorFactoryTests {
.rootBeanDefinition(TestBeanRegistrationAotProcessorAndFilterBean.class).getBeanDefinition());
RegisteredBean registeredBean1 = RegisteredBean.of(beanFactory, "test");
BeanDefinitionMethodGeneratorFactory methodGeneratorFactory = new BeanDefinitionMethodGeneratorFactory(
- new AotFactoriesLoader(beanFactory, springFactoriesLoader));
+ AotServices.factoriesAndBeans(springFactoriesLoader, beanFactory));
assertThat(methodGeneratorFactory.getBeanDefinitionMethodGenerator(registeredBean1, null)).isNotNull();
}
diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanDefinitionMethodGeneratorTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanDefinitionMethodGeneratorTests.java
index 2db44036c36..4b949f8838f 100644
--- a/spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanDefinitionMethodGeneratorTests.java
+++ b/spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanDefinitionMethodGeneratorTests.java
@@ -75,7 +75,7 @@ class BeanDefinitionMethodGeneratorTests {
this.generationContext = new TestGenerationContext();
this.beanFactory = new DefaultListableBeanFactory();
this.methodGeneratorFactory = new BeanDefinitionMethodGeneratorFactory(
- new AotFactoriesLoader(this.beanFactory, new MockSpringFactoriesLoader()));
+ AotServices.factoriesAndBeans( new MockSpringFactoriesLoader(), beanFactory));
this.beanRegistrationsCode = new MockBeanRegistrationsCode(this.generationContext);
}
diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanRegistrationsAotContributionTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanRegistrationsAotContributionTests.java
index b9604e5a639..f84239532f7 100644
--- a/spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanRegistrationsAotContributionTests.java
+++ b/spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanRegistrationsAotContributionTests.java
@@ -68,7 +68,7 @@ class BeanRegistrationsAotContributionTests {
MockSpringFactoriesLoader springFactoriesLoader = new MockSpringFactoriesLoader();
this.beanFactory = new DefaultListableBeanFactory();
this.methodGeneratorFactory = new BeanDefinitionMethodGeneratorFactory(
- new AotFactoriesLoader(this.beanFactory, springFactoriesLoader));
+ AotServices.factoriesAndBeans(springFactoriesLoader, this.beanFactory));
this.generationContext = new TestGenerationContext();
this.beanFactoryInitializationCode = new MockBeanFactoryInitializationCode(this.generationContext);
}
diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanRegistrationsAotProcessorTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanRegistrationsAotProcessorTests.java
index e716067f53c..efd8fcdbdae 100644
--- a/spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanRegistrationsAotProcessorTests.java
+++ b/spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanRegistrationsAotProcessorTests.java
@@ -35,7 +35,7 @@ class BeanRegistrationsAotProcessorTests {
@Test
void beanRegistrationsAotProcessorIsRegistered() {
- assertThat(new AotFactoriesLoader(new DefaultListableBeanFactory())
+ assertThat(AotServices.factoriesAndBeans(new DefaultListableBeanFactory())
.load(BeanFactoryInitializationAotProcessor.class))
.anyMatch(BeanRegistrationsAotProcessor.class::isInstance);
}
diff --git a/spring-beans/src/test/resources/org/springframework/beans/factory/aot/aot-services.factories b/spring-beans/src/test/resources/org/springframework/beans/factory/aot/aot-services.factories
new file mode 100644
index 00000000000..f7e22809638
--- /dev/null
+++ b/spring-beans/src/test/resources/org/springframework/beans/factory/aot/aot-services.factories
@@ -0,0 +1,2 @@
+org.springframework.beans.factory.aot.AotServicesTests$TestService=\
+org.springframework.beans.factory.aot.AotServicesTests$TestServiceImpl
\ No newline at end of file
diff --git a/spring-context/src/main/java/org/springframework/context/aot/BeanFactoryInitializationAotContributions.java b/spring-context/src/main/java/org/springframework/context/aot/BeanFactoryInitializationAotContributions.java
index a9e5d164ce5..878ed204c0b 100644
--- a/spring-context/src/main/java/org/springframework/context/aot/BeanFactoryInitializationAotContributions.java
+++ b/spring-context/src/main/java/org/springframework/context/aot/BeanFactoryInitializationAotContributions.java
@@ -21,7 +21,7 @@ import java.util.Collections;
import java.util.List;
import org.springframework.aot.generate.GenerationContext;
-import org.springframework.beans.factory.aot.AotFactoriesLoader;
+import org.springframework.beans.factory.aot.AotServices;
import org.springframework.beans.factory.aot.BeanFactoryInitializationAotContribution;
import org.springframework.beans.factory.aot.BeanFactoryInitializationAotProcessor;
import org.springframework.beans.factory.aot.BeanFactoryInitializationCode;
@@ -41,19 +41,19 @@ class BeanFactoryInitializationAotContributions {
BeanFactoryInitializationAotContributions(DefaultListableBeanFactory beanFactory) {
- this(beanFactory, new AotFactoriesLoader(beanFactory));
+ this(beanFactory, AotServices.factoriesAndBeans(beanFactory));
}
BeanFactoryInitializationAotContributions(DefaultListableBeanFactory beanFactory,
- AotFactoriesLoader loader) {
+ AotServices.Loader loader) {
this.contributions = getContributions(beanFactory, getProcessors(loader));
}
- private List getProcessors(
- AotFactoriesLoader loader) {
+ private static List getProcessors(
+ AotServices.Loader loader) {
List processors = new ArrayList<>(
- loader.load(BeanFactoryInitializationAotProcessor.class));
+ loader.load(BeanFactoryInitializationAotProcessor.class).asList());
processors.add(new RuntimeHintsBeanFactoryInitializationAotProcessor());
return Collections.unmodifiableList(processors);
}
diff --git a/spring-context/src/main/java/org/springframework/context/aot/RuntimeHintsBeanFactoryInitializationAotProcessor.java b/spring-context/src/main/java/org/springframework/context/aot/RuntimeHintsBeanFactoryInitializationAotProcessor.java
index e9d50e1a4a9..c83224f4b45 100644
--- a/spring-context/src/main/java/org/springframework/context/aot/RuntimeHintsBeanFactoryInitializationAotProcessor.java
+++ b/spring-context/src/main/java/org/springframework/context/aot/RuntimeHintsBeanFactoryInitializationAotProcessor.java
@@ -28,7 +28,7 @@ import org.springframework.aot.generate.GenerationContext;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.RuntimeHintsRegistrar;
import org.springframework.beans.BeanUtils;
-import org.springframework.beans.factory.aot.AotFactoriesLoader;
+import org.springframework.beans.factory.aot.AotServices;
import org.springframework.beans.factory.aot.BeanFactoryInitializationAotContribution;
import org.springframework.beans.factory.aot.BeanFactoryInitializationAotProcessor;
import org.springframework.beans.factory.aot.BeanFactoryInitializationCode;
@@ -56,9 +56,8 @@ class RuntimeHintsBeanFactoryInitializationAotProcessor
@Override
public BeanFactoryInitializationAotContribution processAheadOfTime(
ConfigurableListableBeanFactory beanFactory) {
- AotFactoriesLoader loader = new AotFactoriesLoader(beanFactory);
- Map, RuntimeHintsRegistrar> registrars = loader
- .load(RuntimeHintsRegistrar.class).stream()
+ Map, RuntimeHintsRegistrar> registrars = AotServices
+ .factoriesAndBeans(beanFactory).load(RuntimeHintsRegistrar.class).stream()
.collect(LinkedHashMap::new, (map, item) -> map.put(item.getClass(), item), Map::putAll);
extractFromBeanFactory(beanFactory).forEach(registrarClass ->
registrars.computeIfAbsent(registrarClass, BeanUtils::instantiateClass));