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:
Walliee
2019-04-10 02:02:50 -04:00
committed by Oleg Zhurakousky
parent 9df7455a30
commit 5256ee177c
2 changed files with 55 additions and 12 deletions

View File

@@ -36,7 +36,7 @@ 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.InitializingBean;
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;
@@ -68,6 +68,7 @@ import org.springframework.core.type.StandardMethodMetadata;
* @author Mark Fisher
* @author Oleg Zhurakousky
* @author Artem Bilan
* @author Anshul Mehra
*/
@Configuration
@ConditionalOnMissingBean(FunctionCatalog.class)
@@ -85,7 +86,7 @@ public class ContextFunctionCatalogAutoConfiguration {
protected static class BeanFactoryFunctionCatalog
extends AbstractComposableFunctionRegistry
implements InitializingBean, BeanFactoryAware {
implements SmartInitializingSingleton, BeanFactoryAware {
private ApplicationEventPublisher applicationEventPublisher;
@@ -96,18 +97,17 @@ public class ContextFunctionCatalogAutoConfiguration {
* late as possible in the lifecycle.
*/
@Override
@SuppressWarnings("rawtypes")
public void afterPropertiesSet() throws Exception {
public void afterSingletonsInstantiated() {
Map<String, Supplier> supplierBeans = this.beanFactory
.getBeansOfType(Supplier.class);
.getBeansOfType(Supplier.class);
Map<String, Function> functionBeans = this.beanFactory
.getBeansOfType(Function.class);
.getBeansOfType(Function.class);
Map<String, Consumer> consumerBeans = this.beanFactory
.getBeansOfType(Consumer.class);
.getBeansOfType(Consumer.class);
Map<String, FunctionRegistration> functionRegistrationBeans = this.beanFactory
.getBeansOfType(FunctionRegistration.class);
.getBeansOfType(FunctionRegistration.class);
this.doMerge(functionRegistrationBeans, consumerBeans, supplierBeans,
functionBeans);
functionBeans);
}
@Override
@@ -227,7 +227,6 @@ public class ContextFunctionCatalogAutoConfiguration {
registrations.forEach(registration -> register(registration,
targets.get(registration.getTarget())));
}
}
private static class PreferGsonOrMissingJacksonCondition extends AnyNestedCondition {

View File

@@ -35,7 +35,6 @@ import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.config.AbstractFactoryBean;
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.context.FunctionCatalog;
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.inject.FooConfiguration;
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.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.DescriptiveResource;
import org.springframework.core.io.FileSystemResource;
import org.springframework.lang.Nullable;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Component;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StreamUtils;
@@ -73,6 +76,7 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author Dave Syer
* @author Artem Bilan
* @author Oleg Zhurakousky
* @author Anshul Mehra
*/
public class ContextFunctionCatalogAutoConfigurationTests {
@@ -290,7 +294,7 @@ public class ContextFunctionCatalogAutoConfigurationTests {
.isAssignableFrom(Mono.class);
}
@Test(expected = BeanCreationException.class)
@Test(expected = IllegalArgumentException.class)
public void monoToMonoNonVoidFunction() {
create(MonoToMonoNonVoidConfiguration.class);
}
@@ -589,6 +593,17 @@ public class ContextFunctionCatalogAutoConfigurationTests {
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) {
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
@Configuration
protected static class DependencyInjectionConfiguration {