From 666bc100e862b08ef818490e99374216bffdb247 Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Fri, 12 Oct 2018 18:00:41 +0100 Subject: [PATCH] Extract functional bean definition concerns into a common class SpringApplication looks like the class with the same name in Spring Boot (and is a subclass of it), but it checks to see if the user is asking for functional bean registrations and only used those if it can. --- .../aws/SpringFunctionInitializer.java | 42 +---- .../azure/AzureSpringFunctionInitializer.java | 42 +---- .../function/context/SpringApplication.java | 153 ++++++++++++++++++ 3 files changed, 157 insertions(+), 80 deletions(-) create mode 100644 spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/SpringApplication.java diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/SpringFunctionInitializer.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/SpringFunctionInitializer.java index 861ba51ed..e05f8e10f 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/SpringFunctionInitializer.java +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/SpringFunctionInitializer.java @@ -30,18 +30,12 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.reactivestreams.Publisher; -import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.WebApplicationType; import org.springframework.cloud.function.context.FunctionCatalog; -import org.springframework.cloud.function.context.FunctionRegistration; -import org.springframework.cloud.function.context.FunctionType; import org.springframework.cloud.function.context.catalog.FunctionInspector; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.context.support.GenericApplicationContext; import org.springframework.util.ClassUtils; import reactor.core.publisher.Flux; @@ -115,41 +109,9 @@ public class SpringFunctionInitializer implements Closeable { } private SpringApplication springApplication() { - ApplicationContextInitializer initializer = null; Class sourceClass = configurationClass; - if (ApplicationContextInitializer.class.isAssignableFrom(sourceClass)) { - @SuppressWarnings("unchecked") - ApplicationContextInitializer instance = BeanUtils - .instantiateClass(configurationClass, - ApplicationContextInitializer.class); - initializer = instance; - sourceClass = Object.class; - } - else if (Function.class.isAssignableFrom(sourceClass)) { - @SuppressWarnings("unchecked") - final Class> type = (Class>) sourceClass; - initializer = context -> { - context.registerBean(FunctionRegistration.class, - () -> new FunctionRegistration<>( - context.getAutowireCapableBeanFactory().createBean(type)) - .type(FunctionType.of(type))); - }; - sourceClass = Object.class; - } - SpringApplication application; - if (initializer != null) { - application = new SpringApplication(sourceClass) { - @Override - protected void load(ApplicationContext context, Object[] sources) { - } - }; - application.addInitializers(initializer); - application.setDefaultProperties( - Collections.singletonMap("spring.functional.enabled", "true")); - } - else { - application = new SpringApplication(sourceClass); - } + SpringApplication application = new org.springframework.cloud.function.context.SpringApplication( + sourceClass); application.setWebApplicationType(WebApplicationType.NONE); return application; } diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/main/java/org/springframework/cloud/function/adapter/azure/AzureSpringFunctionInitializer.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/main/java/org/springframework/cloud/function/adapter/azure/AzureSpringFunctionInitializer.java index 4deab5f8c..c752171b2 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/main/java/org/springframework/cloud/function/adapter/azure/AzureSpringFunctionInitializer.java +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/main/java/org/springframework/cloud/function/adapter/azure/AzureSpringFunctionInitializer.java @@ -32,19 +32,13 @@ import com.microsoft.azure.functions.ExecutionContext; import org.reactivestreams.Publisher; -import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.function.context.FunctionCatalog; -import org.springframework.cloud.function.context.FunctionRegistration; -import org.springframework.cloud.function.context.FunctionType; import org.springframework.cloud.function.context.catalog.FunctionInspector; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.context.support.GenericApplicationContext; import org.springframework.util.ClassUtils; /** @@ -138,41 +132,9 @@ public class AzureSpringFunctionInitializer implements Closeable { } private SpringApplication springApplication() { - ApplicationContextInitializer initializer = null; Class sourceClass = configurationClass; - if (ApplicationContextInitializer.class.isAssignableFrom(sourceClass)) { - @SuppressWarnings("unchecked") - ApplicationContextInitializer instance = BeanUtils - .instantiateClass(configurationClass, - ApplicationContextInitializer.class); - initializer = instance; - sourceClass = Object.class; - } - else if (Function.class.isAssignableFrom(sourceClass)) { - @SuppressWarnings("unchecked") - final Class> type = (Class>) sourceClass; - initializer = context -> { - context.registerBean(FunctionRegistration.class, - () -> new FunctionRegistration<>( - context.getAutowireCapableBeanFactory().createBean(type)) - .type(FunctionType.of(type))); - }; - sourceClass = Object.class; - } - SpringApplication application; - if (initializer != null) { - application = new SpringApplication(sourceClass) { - @Override - protected void load(ApplicationContext context, Object[] sources) { - } - }; - application.addInitializers(initializer); - application.setDefaultProperties( - Collections.singletonMap("spring.functional.enabled", "true")); - } - else { - application = new SpringApplication(sourceClass); - } + SpringApplication application = new org.springframework.cloud.function.context.SpringApplication( + sourceClass); application.setWebApplicationType(WebApplicationType.NONE); return application; } diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/SpringApplication.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/SpringApplication.java new file mode 100644 index 000000000..c778005fb --- /dev/null +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/SpringApplication.java @@ -0,0 +1,153 @@ +/* + * Copyright 2016-2017 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 + * + * http://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.cloud.function.context; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Supplier; + +import org.springframework.beans.BeanUtils; +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.web.reactive.context.ReactiveWebServerApplicationContext; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextInitializer; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.support.GenericApplicationContext; +import org.springframework.core.env.MapPropertySource; +import org.springframework.core.env.MutablePropertySources; +import org.springframework.util.ClassUtils; + +/** + * @author Dave Syer + * + */ +public class SpringApplication extends org.springframework.boot.SpringApplication { + + /** + * Name of default propery source. + */ + private static final String DEFAULT_PROPERTIES = "defaultProperties"; + + /** + * Flag to say that context is functional beans. + */ + private static final String SPRING_FUNCTIONAL_ENABLED = "spring.functional.enabled"; + + public static ConfigurableApplicationContext run(Class primarySource, + String... args) { + return run(new Class[] { primarySource }, args); + } + + public static ConfigurableApplicationContext run(Class[] primarySources, + String[] args) { + return new SpringApplication(primarySources).run(args); + } + + public SpringApplication(Class... primarySources) { + super(primarySources); + if (ClassUtils.isPresent("org.springframework.web.reactive.DispatcherHandler", + null)) { + // prefer reactive if it is present + setWebApplicationType(WebApplicationType.REACTIVE); + } + } + + @Override + public ConfigurableApplicationContext run(String... args) { + if (getWebApplicationType() == WebApplicationType.REACTIVE) { + setApplicationContextClass(ReactiveWebServerApplicationContext.class); + } + return super.run(args); + } + + @Override + protected void postProcessApplicationContext(ConfigurableApplicationContext context) { + super.postProcessApplicationContext(context); + boolean functional = false; + if (context instanceof GenericApplicationContext) { + GenericApplicationContext generic = (GenericApplicationContext) context; + for (Object source : getAllSources()) { + if (source instanceof Class) { + Class type = (Class) source; + if (ApplicationContextInitializer.class.isAssignableFrom(type)) { + @SuppressWarnings("unchecked") + ApplicationContextInitializer initializer = BeanUtils + .instantiateClass(type, + ApplicationContextInitializer.class); + initializer.initialize(generic); + functional = true; + } + else if (Function.class.isAssignableFrom(type) + || Consumer.class.isAssignableFrom(type) + || Supplier.class.isAssignableFrom(type)) { + generic.registerBean("function", FunctionRegistration.class, + () -> new FunctionRegistration<>(context + .getAutowireCapableBeanFactory().createBean(type)) + .type(FunctionType.of(type))); + functional = true; + } + } + else { + Class type = source.getClass(); + if (ApplicationContextInitializer.class.isAssignableFrom(type)) { + @SuppressWarnings("unchecked") + ApplicationContextInitializer initializer = (ApplicationContextInitializer) source; + initializer.initialize(generic); + functional = true; + } + else if (Function.class.isAssignableFrom(type) + || Consumer.class.isAssignableFrom(type) + || Supplier.class.isAssignableFrom(type)) { + generic.registerBean("function", FunctionRegistration.class, + () -> new FunctionRegistration<>(source) + .type(FunctionType.of(type))); + functional = true; + } + } + } + if (functional) { + defaultProperties(generic); + } + } + } + + @Override + protected void load(ApplicationContext context, Object[] sources) { + if (!context.getEnvironment().getProperty(SPRING_FUNCTIONAL_ENABLED, + Boolean.class, false)) { + super.load(context, sources); + } + } + + private void defaultProperties(ConfigurableApplicationContext context) { + MutablePropertySources sources = context.getEnvironment().getPropertySources(); + if (!sources.contains(DEFAULT_PROPERTIES)) { + sources.addLast( + new MapPropertySource(DEFAULT_PROPERTIES, Collections.emptyMap())); + } + @SuppressWarnings("unchecked") + Map source = (Map) sources.get(DEFAULT_PROPERTIES) + .getSource(); + Map map = new HashMap<>(source); + map.put(SPRING_FUNCTIONAL_ENABLED, "true"); + sources.replace(DEFAULT_PROPERTIES, + new MapPropertySource(DEFAULT_PROPERTIES, map)); + } + +}