Browse Source

Add AotServices class to replace AotFactoriesLoader

Add an `AotServices` class to replace `AotFactoriesLoader`. The
replacement class allow instances to be loaded from just the
`aot.factories` file if required. It also retains a link to the bean
names so that a `findByBeanName(...)` method can be provided.

See gh-28833
pull/30304/head
Phillip Webb 2 years ago
parent
commit
472c23455a
  1. 4
      spring-aop/src/test/java/org/springframework/aop/scope/ScopedProxyBeanRegistrationAotProcessorTests.java
  2. 103
      spring-beans/src/main/java/org/springframework/beans/factory/aot/AotFactoriesLoader.java
  3. 192
      spring-beans/src/main/java/org/springframework/beans/factory/aot/AotServices.java
  4. 12
      spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanDefinitionMethodGeneratorFactory.java
  5. 9
      spring-beans/src/test/java/org/springframework/beans/factory/annotation/JakartaAnnotationsRuntimeHintsTests.java
  6. 92
      spring-beans/src/test/java/org/springframework/beans/factory/aot/AotFactoriesLoaderTests.java
  7. 227
      spring-beans/src/test/java/org/springframework/beans/factory/aot/AotServicesTests.java
  8. 12
      spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanDefinitionMethodGeneratorFactoryTests.java
  9. 2
      spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanDefinitionMethodGeneratorTests.java
  10. 2
      spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanRegistrationsAotContributionTests.java
  11. 2
      spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanRegistrationsAotProcessorTests.java
  12. 2
      spring-beans/src/test/resources/org/springframework/beans/factory/aot/aot-services.factories
  13. 12
      spring-context/src/main/java/org/springframework/context/aot/BeanFactoryInitializationAotContributions.java
  14. 7
      spring-context/src/main/java/org/springframework/context/aot/RuntimeHintsBeanFactoryInitializationAotProcessor.java

4
spring-aop/src/test/java/org/springframework/aop/scope/ScopedProxyBeanRegistrationAotProcessorTests.java

@ -29,7 +29,7 @@ import org.springframework.aot.generate.MethodReference; @@ -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 { @@ -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);
}

103
spring-beans/src/main/java/org/springframework/beans/factory/aot/AotFactoriesLoader.java

@ -1,103 +0,0 @@ @@ -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.
*
* <p>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 <T> the item type
* @param type the item type to load
* @return a list of loaded instances
*/
public <T> List<T> load(Class<T> type) {
List<T> 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);
}
}

192
spring-beans/src/main/java/org/springframework/beans/factory/aot/AotServices.java

@ -0,0 +1,192 @@ @@ -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 <T> the service type
*/
public final class AotServices<T> implements Iterable<T> {
/**
* The location to look for AOT factories.
*/
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring/aot.factories";
private final List<T> services;
private final Map<String, T> beans;
private AotServices(List<T> loaded, Map<String, T> beans) {
List<T> 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<T> iterator() {
return this.services.iterator();
}
/**
* Return a {@link Stream} of the AOT services.
* @return a stream of the services
*/
public Stream<T> stream() {
return this.services.stream();
}
/**
* Return the AOT services as a {@link List}.
* @return a list of the services
*/
public List<T> 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 <T> the service type
* @param type the service type
* @return a new {@link AotServices} instance
*/
public <T> AotServices<T> load(Class<T> type) {
return new AotServices<T>(this.springFactoriesLoader.load(type), loadBeans(type));
}
private <T> Map<String, T> loadBeans(Class<T> type) {
return (this.beanFactory != null) ? BeanFactoryUtils
.beansOfTypeIncludingAncestors(this.beanFactory, type, true, false)
: Collections.emptyMap();
}
}
}

12
spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanDefinitionMethodGeneratorFactory.java

@ -43,9 +43,9 @@ class BeanDefinitionMethodGeneratorFactory { @@ -43,9 +43,9 @@ class BeanDefinitionMethodGeneratorFactory {
.getLog(BeanDefinitionMethodGeneratorFactory.class);
private final List<BeanRegistrationAotProcessor> aotProcessors;
private final AotServices<BeanRegistrationAotProcessor> aotProcessors;
private final List<BeanRegistrationExcludeFilter> excludeFilters;
private final AotServices<BeanRegistrationExcludeFilter> excludeFilters;
/**
@ -54,15 +54,15 @@ class BeanDefinitionMethodGeneratorFactory { @@ -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);
}

9
spring-beans/src/test/java/org/springframework/beans/factory/annotation/JakartaAnnotationsRuntimeHintsTests.java

@ -26,8 +26,7 @@ import org.springframework.aot.hint.MemberCategory; @@ -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 { @@ -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

92
spring-beans/src/test/java/org/springframework/beans/factory/aot/AotFactoriesLoaderTests.java

@ -1,92 +0,0 @@ @@ -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<TestFactory> 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;
}
}
}

227
spring-beans/src/test/java/org/springframework/beans/factory/aot/AotServicesTests.java

@ -0,0 +1,227 @@ @@ -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<TestService> 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<URL> 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);
}
}
}

12
spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanDefinitionMethodGeneratorFactoryTests.java

@ -43,7 +43,7 @@ class BeanDefinitionMethodGeneratorFactoryTests { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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();
}

2
spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanDefinitionMethodGeneratorTests.java

@ -75,7 +75,7 @@ class BeanDefinitionMethodGeneratorTests { @@ -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);
}

2
spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanRegistrationsAotContributionTests.java

@ -68,7 +68,7 @@ class BeanRegistrationsAotContributionTests { @@ -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);
}

2
spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanRegistrationsAotProcessorTests.java

@ -35,7 +35,7 @@ class BeanRegistrationsAotProcessorTests { @@ -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);
}

2
spring-beans/src/test/resources/org/springframework/beans/factory/aot/aot-services.factories

@ -0,0 +1,2 @@ @@ -0,0 +1,2 @@
org.springframework.beans.factory.aot.AotServicesTests$TestService=\
org.springframework.beans.factory.aot.AotServicesTests$TestServiceImpl

12
spring-context/src/main/java/org/springframework/context/aot/BeanFactoryInitializationAotContributions.java

@ -21,7 +21,7 @@ import java.util.Collections; @@ -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 { @@ -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<BeanFactoryInitializationAotProcessor> getProcessors(
AotFactoriesLoader loader) {
private static List<BeanFactoryInitializationAotProcessor> getProcessors(
AotServices.Loader loader) {
List<BeanFactoryInitializationAotProcessor> processors = new ArrayList<>(
loader.load(BeanFactoryInitializationAotProcessor.class));
loader.load(BeanFactoryInitializationAotProcessor.class).asList());
processors.add(new RuntimeHintsBeanFactoryInitializationAotProcessor());
return Collections.unmodifiableList(processors);
}

7
spring-context/src/main/java/org/springframework/context/aot/RuntimeHintsBeanFactoryInitializationAotProcessor.java

@ -28,7 +28,7 @@ import org.springframework.aot.generate.GenerationContext; @@ -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 @@ -56,9 +56,8 @@ class RuntimeHintsBeanFactoryInitializationAotProcessor
@Override
public BeanFactoryInitializationAotContribution processAheadOfTime(
ConfigurableListableBeanFactory beanFactory) {
AotFactoriesLoader loader = new AotFactoriesLoader(beanFactory);
Map<Class<? extends RuntimeHintsRegistrar>, RuntimeHintsRegistrar> registrars = loader
.load(RuntimeHintsRegistrar.class).stream()
Map<Class<? extends RuntimeHintsRegistrar>, 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));

Loading…
Cancel
Save