Fix BeanFactoryFunctionCatalog initialization when a
BeanFactoryPostProcessor that depends on FunctionCatalog is present. On application context refresh, BeanFactoryPostProcessors are invoked before registering BeanPostProcessor(s). If a BeanFactoryPostProcessor that depends on FunctionCatalog is present, then when ContextFunctionCatalogAutoConfiguration tries to fetch all functional beans (Function/Supplier/Consumer), the creation of beans where no default constructor exists fails as AutowiredAnnotationBeanPostProcessor hasn't been registered yet. Initialing BeanFactoryFunctionCatalog on ApplicationReadyEvent delays the collection of functional beans to an even later point in the lifecycle. fixes #352 Fix test name Switch to use SmartInitializingSingleton Resolves #353
This commit is contained in:
committed by
Oleg Zhurakousky
parent
9df7455a30
commit
5256ee177c
@@ -36,7 +36,7 @@ import com.google.gson.Gson;
|
|||||||
import org.springframework.beans.BeansException;
|
import org.springframework.beans.BeansException;
|
||||||
import org.springframework.beans.factory.BeanFactory;
|
import org.springframework.beans.factory.BeanFactory;
|
||||||
import org.springframework.beans.factory.BeanFactoryAware;
|
import org.springframework.beans.factory.BeanFactoryAware;
|
||||||
import org.springframework.beans.factory.InitializingBean;
|
import org.springframework.beans.factory.SmartInitializingSingleton;
|
||||||
import org.springframework.beans.factory.annotation.Qualifier;
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
import org.springframework.beans.factory.config.BeanDefinition;
|
import org.springframework.beans.factory.config.BeanDefinition;
|
||||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||||
@@ -68,6 +68,7 @@ import org.springframework.core.type.StandardMethodMetadata;
|
|||||||
* @author Mark Fisher
|
* @author Mark Fisher
|
||||||
* @author Oleg Zhurakousky
|
* @author Oleg Zhurakousky
|
||||||
* @author Artem Bilan
|
* @author Artem Bilan
|
||||||
|
* @author Anshul Mehra
|
||||||
*/
|
*/
|
||||||
@Configuration
|
@Configuration
|
||||||
@ConditionalOnMissingBean(FunctionCatalog.class)
|
@ConditionalOnMissingBean(FunctionCatalog.class)
|
||||||
@@ -85,7 +86,7 @@ public class ContextFunctionCatalogAutoConfiguration {
|
|||||||
|
|
||||||
protected static class BeanFactoryFunctionCatalog
|
protected static class BeanFactoryFunctionCatalog
|
||||||
extends AbstractComposableFunctionRegistry
|
extends AbstractComposableFunctionRegistry
|
||||||
implements InitializingBean, BeanFactoryAware {
|
implements SmartInitializingSingleton, BeanFactoryAware {
|
||||||
|
|
||||||
private ApplicationEventPublisher applicationEventPublisher;
|
private ApplicationEventPublisher applicationEventPublisher;
|
||||||
|
|
||||||
@@ -96,18 +97,17 @@ public class ContextFunctionCatalogAutoConfiguration {
|
|||||||
* late as possible in the lifecycle.
|
* late as possible in the lifecycle.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("rawtypes")
|
public void afterSingletonsInstantiated() {
|
||||||
public void afterPropertiesSet() throws Exception {
|
|
||||||
Map<String, Supplier> supplierBeans = this.beanFactory
|
Map<String, Supplier> supplierBeans = this.beanFactory
|
||||||
.getBeansOfType(Supplier.class);
|
.getBeansOfType(Supplier.class);
|
||||||
Map<String, Function> functionBeans = this.beanFactory
|
Map<String, Function> functionBeans = this.beanFactory
|
||||||
.getBeansOfType(Function.class);
|
.getBeansOfType(Function.class);
|
||||||
Map<String, Consumer> consumerBeans = this.beanFactory
|
Map<String, Consumer> consumerBeans = this.beanFactory
|
||||||
.getBeansOfType(Consumer.class);
|
.getBeansOfType(Consumer.class);
|
||||||
Map<String, FunctionRegistration> functionRegistrationBeans = this.beanFactory
|
Map<String, FunctionRegistration> functionRegistrationBeans = this.beanFactory
|
||||||
.getBeansOfType(FunctionRegistration.class);
|
.getBeansOfType(FunctionRegistration.class);
|
||||||
this.doMerge(functionRegistrationBeans, consumerBeans, supplierBeans,
|
this.doMerge(functionRegistrationBeans, consumerBeans, supplierBeans,
|
||||||
functionBeans);
|
functionBeans);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -227,7 +227,6 @@ public class ContextFunctionCatalogAutoConfiguration {
|
|||||||
registrations.forEach(registration -> register(registration,
|
registrations.forEach(registration -> register(registration,
|
||||||
targets.get(registration.getTarget())));
|
targets.get(registration.getTarget())));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class PreferGsonOrMissingJacksonCondition extends AnyNestedCondition {
|
private static class PreferGsonOrMissingJacksonCondition extends AnyNestedCondition {
|
||||||
|
|||||||
@@ -35,7 +35,6 @@ import reactor.core.publisher.Flux;
|
|||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
import org.springframework.beans.BeansException;
|
import org.springframework.beans.BeansException;
|
||||||
import org.springframework.beans.factory.BeanCreationException;
|
|
||||||
import org.springframework.beans.factory.annotation.Qualifier;
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
import org.springframework.beans.factory.config.AbstractFactoryBean;
|
import org.springframework.beans.factory.config.AbstractFactoryBean;
|
||||||
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
||||||
@@ -49,6 +48,7 @@ import org.springframework.cloud.function.compiler.CompiledFunctionFactory;
|
|||||||
import org.springframework.cloud.function.compiler.FunctionCompiler;
|
import org.springframework.cloud.function.compiler.FunctionCompiler;
|
||||||
import org.springframework.cloud.function.context.FunctionCatalog;
|
import org.springframework.cloud.function.context.FunctionCatalog;
|
||||||
import org.springframework.cloud.function.context.FunctionRegistration;
|
import org.springframework.cloud.function.context.FunctionRegistration;
|
||||||
|
import org.springframework.cloud.function.context.FunctionRegistry;
|
||||||
import org.springframework.cloud.function.context.catalog.FunctionInspector;
|
import org.springframework.cloud.function.context.catalog.FunctionInspector;
|
||||||
import org.springframework.cloud.function.inject.FooConfiguration;
|
import org.springframework.cloud.function.inject.FooConfiguration;
|
||||||
import org.springframework.cloud.function.scan.ScannedFunction;
|
import org.springframework.cloud.function.scan.ScannedFunction;
|
||||||
@@ -58,11 +58,14 @@ import org.springframework.context.annotation.Bean;
|
|||||||
import org.springframework.context.annotation.ComponentScan;
|
import org.springframework.context.annotation.ComponentScan;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.context.annotation.Import;
|
import org.springframework.context.annotation.Import;
|
||||||
|
import org.springframework.core.env.Environment;
|
||||||
import org.springframework.core.io.ClassPathResource;
|
import org.springframework.core.io.ClassPathResource;
|
||||||
import org.springframework.core.io.DescriptiveResource;
|
import org.springframework.core.io.DescriptiveResource;
|
||||||
import org.springframework.core.io.FileSystemResource;
|
import org.springframework.core.io.FileSystemResource;
|
||||||
|
import org.springframework.lang.Nullable;
|
||||||
import org.springframework.messaging.Message;
|
import org.springframework.messaging.Message;
|
||||||
import org.springframework.messaging.support.MessageBuilder;
|
import org.springframework.messaging.support.MessageBuilder;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.util.ClassUtils;
|
import org.springframework.util.ClassUtils;
|
||||||
import org.springframework.util.ReflectionUtils;
|
import org.springframework.util.ReflectionUtils;
|
||||||
import org.springframework.util.StreamUtils;
|
import org.springframework.util.StreamUtils;
|
||||||
@@ -73,6 +76,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
|||||||
* @author Dave Syer
|
* @author Dave Syer
|
||||||
* @author Artem Bilan
|
* @author Artem Bilan
|
||||||
* @author Oleg Zhurakousky
|
* @author Oleg Zhurakousky
|
||||||
|
* @author Anshul Mehra
|
||||||
*/
|
*/
|
||||||
public class ContextFunctionCatalogAutoConfigurationTests {
|
public class ContextFunctionCatalogAutoConfigurationTests {
|
||||||
|
|
||||||
@@ -290,7 +294,7 @@ public class ContextFunctionCatalogAutoConfigurationTests {
|
|||||||
.isAssignableFrom(Mono.class);
|
.isAssignableFrom(Mono.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = BeanCreationException.class)
|
@Test(expected = IllegalArgumentException.class)
|
||||||
public void monoToMonoNonVoidFunction() {
|
public void monoToMonoNonVoidFunction() {
|
||||||
create(MonoToMonoNonVoidConfiguration.class);
|
create(MonoToMonoNonVoidConfiguration.class);
|
||||||
}
|
}
|
||||||
@@ -589,6 +593,17 @@ public class ContextFunctionCatalogAutoConfigurationTests {
|
|||||||
assertThat(f.apply(Flux.just("foo")).blockFirst()).isEqualTo("FOO-bar");
|
assertThat(f.apply(Flux.just("foo")).blockFirst()).isEqualTo("FOO-bar");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void functionCatalogDependentBeanFactoryPostProcessor() {
|
||||||
|
create(new Class[]{ComponentFunctionConfiguration.class, AppendFunction.class});
|
||||||
|
assertThat(this.context.getBean("appendFunction")).isInstanceOf(Function.class);
|
||||||
|
assertThat((Function<?, ?>) this.catalog.lookup(Function.class, "appendFunction"))
|
||||||
|
.isInstanceOf(Function.class);
|
||||||
|
Function<Flux<String>, Flux<String>> f = this.catalog.lookup(Function.class,
|
||||||
|
"appendFunction");
|
||||||
|
assertThat(f.apply(Flux.just("World")).blockFirst()).isEqualTo("Hello World");
|
||||||
|
}
|
||||||
|
|
||||||
private void create(Class<?> type, String... props) {
|
private void create(Class<?> type, String... props) {
|
||||||
create(new Class<?>[] { type }, props);
|
create(new Class<?>[] { type }, props);
|
||||||
}
|
}
|
||||||
@@ -643,6 +658,35 @@ public class ContextFunctionCatalogAutoConfigurationTests {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@EnableAutoConfiguration
|
||||||
|
@Configuration
|
||||||
|
protected static class ComponentFunctionConfiguration {
|
||||||
|
@Bean
|
||||||
|
public String value() {
|
||||||
|
return "Hello ";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public BeanFactoryPostProcessor someBeanFactoryPostProcessor(Environment environment,
|
||||||
|
@Nullable FunctionRegistry functionCatalog, @Nullable FunctionInspector inspector) {
|
||||||
|
return beanFactory -> { };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component("appendFunction")
|
||||||
|
public static class AppendFunction implements Function<String, String> {
|
||||||
|
private String value;
|
||||||
|
|
||||||
|
public AppendFunction(String value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String apply(String s) {
|
||||||
|
return this.value + s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@EnableAutoConfiguration
|
@EnableAutoConfiguration
|
||||||
@Configuration
|
@Configuration
|
||||||
protected static class DependencyInjectionConfiguration {
|
protected static class DependencyInjectionConfiguration {
|
||||||
|
|||||||
Reference in New Issue
Block a user