From eb2ad714711b3088891c81ae5ec80b59cff02b4b Mon Sep 17 00:00:00 2001 From: John Blum Date: Wed, 8 Apr 2020 13:10:07 -0700 Subject: [PATCH] DATAGEODE-326 - Add additional 'getOrderedStreamOfBeansByType(..)' utility method in SpringUtils. This methods also returns beans in order based on bean's class type or bean definitions annotated with the @Order annotation in addition to beans implementing the Ordered interface. The difference between this method and the 'getBeansOfTypeOrdered(..)' method is that this method uses BeanFactory.getBeanProvider(:Class).orderedStream() internally whereas 'getBeansOfTypeOrdered(..)' uses ListableBeanFactory.getBeansOfType(..) with additional magic. --- .../data/gemfire/util/SpringUtils.java | 69 +++++++++++++++++-- ...onContextBeanOrderingIntegrationTests.java | 27 ++++++-- 2 files changed, 86 insertions(+), 10 deletions(-) diff --git a/spring-data-geode/src/main/java/org/springframework/data/gemfire/util/SpringUtils.java b/spring-data-geode/src/main/java/org/springframework/data/gemfire/util/SpringUtils.java index 82558b42..833e5731 100644 --- a/spring-data-geode/src/main/java/org/springframework/data/gemfire/util/SpringUtils.java +++ b/spring-data-geode/src/main/java/org/springframework/data/gemfire/util/SpringUtils.java @@ -30,9 +30,11 @@ import java.util.Set; import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Collectors; +import java.util.stream.Stream; import org.springframework.beans.PropertyValue; import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; @@ -70,7 +72,7 @@ public abstract class SpringUtils { * @param beanName {@link String name} of the bean. * @param beanType {@link Class type} of the bean. * @return a boolean value indicating whether the {@link BeanFactory Spring container} contains a bean - * matching by both {@link String name} as well as {@link Class type}. + * matching by both {@link String name} and {@link Class type}. * @see org.springframework.beans.factory.BeanFactory * @see java.lang.Class * @see java.lang.String @@ -79,7 +81,16 @@ public abstract class SpringUtils { return beanFactory.containsBean(beanName) && beanFactory.isTypeMatch(beanName, beanType); } - public static BeanDefinition addDependsOn(BeanDefinition beanDefinition, String... beanNames) { + /** + * Adds an array of bean dependencies (by name) to the given {@link BeanDefinition}. + * + * @param beanDefinition {@link BeanDefinition} to add the bean dependencies to. + * @param beanNames {@link String} array containing names of beans to which the {@link BeanDefinition} + * has a dependency. + * @return the given {@link BeanDefinition}. + * @see org.springframework.beans.factory.config.BeanDefinition + */ + public static BeanDefinition addDependsOn(@NonNull BeanDefinition beanDefinition, @Nullable String... beanNames) { List dependsOnList = new ArrayList<>(); @@ -90,6 +101,18 @@ public abstract class SpringUtils { return beanDefinition; } + /** + * Returns a {@link List} of beans by the given {@link Class type} in order. + * + * @param {@link Class type} of the bean. + * @param beanFactory {@link ConfigurableListableBeanFactory Spring container} used to acquire the ordered beans. + * @param beanType {@link Class type} of beans to acquire. + * @return a {@link List} of beans of the given {@link Class type} in order. + * @see #getBeansOfTypeOrdered(ConfigurableListableBeanFactory, Class, boolean, boolean) + * @see org.springframework.beans.factory.config.ConfigurableListableBeanFactory + * @see java.lang.Class + * @see java.util.List + */ @NonNull public static List getBeansOfTypeOrdered(@NonNull ConfigurableListableBeanFactory beanFactory, @NonNull Class beanType) { @@ -97,6 +120,19 @@ public abstract class SpringUtils { return getBeansOfTypeOrdered(beanFactory, beanType, true, true); } + /** + * Returns a {@link List} of beans by the given {@link Class type} in order. + * + * @param {@link Class type} of the bean. + * @param beanFactory {@link ConfigurableListableBeanFactory Spring container} used to acquire the ordered beans. + * @param beanType {@link Class type} of beans to acquire. + * @param includeNonSingletons boolean indicating whether to include non-Singleton beans from the Spring container. + * @param allowEagerInit boolean indicating whether to eagerly initialize {@link FactoryBean FactoryBeans}. + * @return a {@link List} of beans of the given {@link Class type} in order. + * @see org.springframework.beans.factory.config.ConfigurableListableBeanFactory + * @see java.lang.Class + * @see java.util.List + */ @NonNull public static List getBeansOfTypeOrdered(@NonNull ConfigurableListableBeanFactory beanFactory, @NonNull Class beanType, boolean includeNonSingletons, boolean allowEagerInit) { @@ -104,13 +140,13 @@ public abstract class SpringUtils { Assert.notNull(beanFactory, "BeanFactory must not be null"); Assert.notNull(beanType, "Bean type must not be null"); - Map beansOfType = beanFactory.getBeansOfType(beanType, includeNonSingletons, allowEagerInit); + Map beansOfType = + CollectionUtils.nullSafeMap(beanFactory.getBeansOfType(beanType, includeNonSingletons, allowEagerInit)); Set beanNamesOfType = new HashSet<>(beansOfType.keySet()); // Handles @Order annotated beans and beans implementing the Ordered interface - List> orderedBeansOfType = - CollectionUtils.nullSafeMap(beansOfType).entrySet().stream() + List> orderedBeansOfType = beansOfType.entrySet().stream() .map(SpringUtils::toOrderedBeanWrapper) .filter(Objects::nonNull) .collect(Collectors.toList()); @@ -132,6 +168,7 @@ public abstract class SpringUtils { .collect(Collectors.toList()); } + // No Javadoc private static List> orderUnorderedBeans(@NonNull ConfigurableListableBeanFactory beanFactory, @NonNull Map beansOfType, @NonNull Set unorderedBeanNames) { @@ -157,6 +194,7 @@ public abstract class SpringUtils { return orderedBeanWrappers; } + // No Javadoc @Nullable private static OrderedBeanWrapper toOrderedBeanWrapper(@NonNull Map.Entry beanEntry) { @@ -174,6 +212,25 @@ public abstract class SpringUtils { : null; } + /** + * Returns bean of the given {@link Class type} in an ordered {@link Stream}. + * + * @param {@link Class type} of the beans. + * @param beanFactory {@link BeanFactory} from which to acquire the beans. + * @param beanType {@link Class type} of the beans. + * @return an ordered {@link Stream} of beans from the {@link BeanFactory} of the given {@link Class type}. + * @see org.springframework.beans.factory.BeanFactory + * @see java.util.stream.Stream + * @see java.lang.Class + */ + public static Stream getOrderedStreamOfBeansByType(BeanFactory beanFactory, Class beanType) { + + Assert.notNull(beanFactory, "BeanFactory must not be null"); + Assert.notNull(beanType,"Bean type must not be null"); + + return beanFactory.getBeanProvider(beanType).orderedStream(); + } + public static Optional getPropertyValue(BeanDefinition beanDefinition, String propertyName) { return Optional.ofNullable(beanDefinition) @@ -308,9 +365,9 @@ public abstract class SpringUtils { Assert.notNull(bean, "Bean must not be null"); Assert.hasText(beanName, "Bean name is required"); - this.order = order; this.bean = bean; this.beanName = beanName; + this.order = order; } @Override diff --git a/spring-data-geode/src/test/java/org/springframework/context/ApplicationContextBeanOrderingIntegrationTests.java b/spring-data-geode/src/test/java/org/springframework/context/ApplicationContextBeanOrderingIntegrationTests.java index 0fe23d94..ff920d22 100644 --- a/spring-data-geode/src/test/java/org/springframework/context/ApplicationContextBeanOrderingIntegrationTests.java +++ b/spring-data-geode/src/test/java/org/springframework/context/ApplicationContextBeanOrderingIntegrationTests.java @@ -25,6 +25,7 @@ import java.util.Objects; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; +import java.util.stream.Stream; import org.junit.Test; import org.junit.runner.RunWith; @@ -42,7 +43,9 @@ import org.springframework.core.Ordered; import org.springframework.core.ResolvableType; import org.springframework.core.annotation.Order; import org.springframework.core.annotation.OrderUtils; +import org.springframework.data.gemfire.util.CollectionUtils; import org.springframework.data.gemfire.util.SpringUtils; +import org.springframework.data.gemfire.util.StreamUtils; import org.springframework.lang.NonNull; import org.springframework.lang.Nullable; import org.springframework.test.context.ContextConfiguration; @@ -128,6 +131,18 @@ public class ApplicationContextBeanOrderingIntegrationTests { .containsExactly("A", "B", "C", "D", "U", "X", "Y", "Z"); } + @Test + public void expectBeansToBeOrderedByOrderAnnotationAndOrderedInterfaceUsingBeanProviderOrderedStream() { + + List beanNames = this.applicationContext.getBeanProvider(NamedBean.class).orderedStream() + .map(Object::toString) + .collect(Collectors.toList()); + + assertThat(beanNames) + .describedAs("Expected [Y, X, Z, B, C, A, D, U]; but was %s", beanNames) + .containsExactly("Y", "X", "Z", "B", "C", "A", "D", "U"); + } + /** * 4. Test Fails (of course)! * @@ -215,10 +230,14 @@ public class ApplicationContextBeanOrderingIntegrationTests { @Test public void expectBeansToBeOrderedByOrderAnnotationAndOrderedInterfaceUsingSpringUtils() { - List beanNames = - SpringUtils.getBeansOfTypeOrdered(this.applicationContext.getBeanFactory(), NamedBean.class).stream() - .map(Object::toString) - .collect(Collectors.toList()); + List orderedBeans = CollectionUtils + .nullSafeList(SpringUtils.getBeansOfTypeOrdered(this.applicationContext.getBeanFactory(), NamedBean.class)); + + Stream orderedBeanStream = StreamUtils.nullSafeStream(orderedBeans.stream()); + + List beanNames = orderedBeanStream + .map(Object::toString) + .collect(Collectors.toList()); assertThat(beanNames) .describedAs("Expected [Y, X, Z, B, C, A, D, U]; but was %s", beanNames)