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
This commit is contained in:
parent
498668fc57
commit
472c23455a
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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 {
|
|||
* @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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,2 @@
|
|||
org.springframework.beans.factory.aot.AotServicesTests$TestService=\
|
||||
org.springframework.beans.factory.aot.AotServicesTests$TestServiceImpl
|
||||
|
|
@ -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<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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<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…
Reference in New Issue