diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/AbstractComposableFunctionRegistry.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/AbstractComposableFunctionRegistry.java index e26b7f3a0..624f0ad66 100644 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/AbstractComposableFunctionRegistry.java +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/AbstractComposableFunctionRegistry.java @@ -77,9 +77,12 @@ public abstract class AbstractComposableFunctionRegistry implements FunctionRegi protected ApplicationEventPublisher applicationEventPublisher; + private AtomicBoolean initialized = new AtomicBoolean(); + @SuppressWarnings("unchecked") @Override public T lookup(Class type, String name) { + initializeIfNecessary(); String functionDefinitionName = !StringUtils.hasText(name) && this.environment.containsProperty("spring.cloud.function.definition") ? this.environment.getProperty("spring.cloud.function.definition") @@ -90,6 +93,7 @@ public abstract class AbstractComposableFunctionRegistry implements FunctionRegi @SuppressWarnings("serial") @Override public Set getNames(Class type) { + initializeIfNecessary(); if (type == null) { return new HashSet(getSupplierNames()) { { @@ -111,6 +115,7 @@ public abstract class AbstractComposableFunctionRegistry implements FunctionRegi * @return immutable {@link Set} of available {@link Supplier} names. */ public Set getSupplierNames() { + initializeIfNecessary(); return this.functions.entrySet().stream() .filter(entry -> entry.getValue() instanceof Supplier) .map(entry -> entry.getKey()) @@ -122,6 +127,7 @@ public abstract class AbstractComposableFunctionRegistry implements FunctionRegi * @return immutable {@link Set} of available {@link Function} names. */ public Set getFunctionNames() { + initializeIfNecessary(); return this.functions.entrySet().stream() .filter(entry -> !(entry.getValue() instanceof Supplier)) .map(entry -> entry.getKey()) @@ -129,10 +135,12 @@ public abstract class AbstractComposableFunctionRegistry implements FunctionRegi } public boolean hasSuppliers() { + initializeIfNecessary(); return !CollectionUtils.isEmpty(getSupplierNames()); } public boolean hasFunctions() { + initializeIfNecessary(); return !CollectionUtils.isEmpty(getFunctionNames()); } @@ -144,10 +152,12 @@ public abstract class AbstractComposableFunctionRegistry implements FunctionRegi */ @Override public int size() { + initializeIfNecessary(); return this.functions.size(); } public FunctionType getFunctionType(String name) { + initializeIfNecessary(); return this.types.get(name); } @@ -158,6 +168,7 @@ public abstract class AbstractComposableFunctionRegistry implements FunctionRegi * @return the name of the function or null. */ public String lookupFunctionName(Object function) { + initializeIfNecessary(); return this.names.containsKey(function) ? this.names.get(function) : null; } @@ -174,6 +185,7 @@ public abstract class AbstractComposableFunctionRegistry implements FunctionRegi @Override public FunctionRegistration getRegistration(Object function) { + initializeIfNecessary(); String functionName = function == null ? null : this.lookupFunctionName(function); if (StringUtils.hasText(functionName)) { @@ -189,10 +201,19 @@ public abstract class AbstractComposableFunctionRegistry implements FunctionRegi public void register(FunctionRegistration functionRegistration) { Assert.notEmpty(functionRegistration.getNames(), "'registration' must contain at least one name before it is registered in catalog."); + initializeIfNecessary(); register(functionRegistration, functionRegistration.getNames().iterator().next()); } + private void initializeIfNecessary() { + if (initialized.compareAndSet(false, true)) { + doInitialize(); + } + } + protected void doInitialize() { + + } /** * Registers function wrapped by the provided FunctionRegistration with diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/ContextFunctionCatalogAutoConfiguration.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/ContextFunctionCatalogAutoConfiguration.java index 43fafdf8d..1b79386cd 100644 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/ContextFunctionCatalogAutoConfiguration.java +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/ContextFunctionCatalogAutoConfiguration.java @@ -36,7 +36,6 @@ import com.google.gson.Gson; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; -import org.springframework.beans.factory.SmartInitializingSingleton; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; @@ -86,7 +85,7 @@ public class ContextFunctionCatalogAutoConfiguration { protected static class BeanFactoryFunctionCatalog extends AbstractComposableFunctionRegistry - implements SmartInitializingSingleton, BeanFactoryAware { + implements BeanFactoryAware { private ApplicationEventPublisher applicationEventPublisher; @@ -96,18 +95,21 @@ public class ContextFunctionCatalogAutoConfiguration { * Will collect all suppliers, functions, consumers and function registration as * late as possible in the lifecycle. */ + @SuppressWarnings("rawtypes") @Override - public void afterSingletonsInstantiated() { - Map supplierBeans = this.beanFactory - .getBeansOfType(Supplier.class); - Map functionBeans = this.beanFactory - .getBeansOfType(Function.class); - Map consumerBeans = this.beanFactory - .getBeansOfType(Consumer.class); - Map functionRegistrationBeans = this.beanFactory - .getBeansOfType(FunctionRegistration.class); - this.doMerge(functionRegistrationBeans, consumerBeans, supplierBeans, - functionBeans); + protected void doInitialize() { + if (this.beanFactory != null) { + Map supplierBeans = this.beanFactory + .getBeansOfType(Supplier.class); + Map functionBeans = this.beanFactory + .getBeansOfType(Function.class); + Map consumerBeans = this.beanFactory + .getBeansOfType(Consumer.class); + Map functionRegistrationBeans = this.beanFactory + .getBeansOfType(FunctionRegistration.class); + this.doMerge(functionRegistrationBeans, consumerBeans, supplierBeans, + functionBeans); + } } @Override diff --git a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/config/ContextFunctionCatalogAutoConfigurationTests.java b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/config/ContextFunctionCatalogAutoConfigurationTests.java index 7a4b97e48..6b0224f61 100644 --- a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/config/ContextFunctionCatalogAutoConfigurationTests.java +++ b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/config/ContextFunctionCatalogAutoConfigurationTests.java @@ -297,6 +297,7 @@ public class ContextFunctionCatalogAutoConfigurationTests { @Test(expected = IllegalArgumentException.class) public void monoToMonoNonVoidFunction() { create(MonoToMonoNonVoidConfiguration.class); + this.catalog.lookup("anything-doesn't-matter"); } @Test