Browse Source
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-28833pull/30304/head
14 changed files with 451 additions and 227 deletions
@ -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(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -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); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,2 @@ |
|||||||
|
org.springframework.beans.factory.aot.AotServicesTests$TestService=\ |
||||||
|
org.springframework.beans.factory.aot.AotServicesTests$TestServiceImpl |
||||||
Loading…
Reference in new issue