Discover function type from FactoryBean

* Fix spring-integration sample

* Added discoverFunctionTypeFromClass() method to FunctionTypeUtils

* A `FactoryBean` may produce a function instance as well.
Add a logic into `BeanFactoryAwareFunctionRegistry` to discover a function type from the `FactoryBean.getObjectType()`
This commit is contained in:
Oleg Zhurakousky
2019-09-25 10:24:07 -04:00
committed by Artem Bilan
parent bdd4320a59
commit 97bea81836
6 changed files with 84 additions and 9 deletions

View File

@@ -47,6 +47,7 @@ import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.cloud.function.context.FunctionCatalog;
@@ -188,6 +189,10 @@ public class BeanFactoryAwareFunctionRegistry
boolean beanDefinitionExists = false;
for (int i = 0; i < names.length && !beanDefinitionExists; i++) {
beanDefinitionExists = this.applicationContext.getBeanFactory().containsBeanDefinition(names[i]);
if (this.applicationContext.containsBean("&" + names[i])) {
Class<?> objectType = this.applicationContext.getBean("&" + names[i], FactoryBean.class).getObjectType();
return FunctionTypeUtils.discoverFunctionTypeFromClass(objectType);
}
}
if (!beanDefinitionExists) {
logger.info("BeanDefinition for function name(s) '" + Arrays.asList(names) +
@@ -430,7 +435,9 @@ public class BeanFactoryAwareFunctionRegistry
FunctionInvocationWrapper(Object target, Type functionType, String functionDefinition, String... acceptedOutputMimeTypes) {
this.target = target;
this.composed = !target.getClass().getName().contains("EnhancerBySpringCGLIB") && target.getClass().getDeclaredFields().length > 1;
this.composed = !target.getClass().getName().contains("$$EnhancerBySpringCGLIB")
&& !AopUtils.isAopProxy(target) && !AopUtils.isJdkDynamicProxy(target)
&& target.getClass().getDeclaredFields().length > 1;
this.functionType = functionType;
this.acceptedOutputMimeTypes = acceptedOutputMimeTypes;
this.functionDefinition = functionDefinition;

View File

@@ -105,7 +105,34 @@ public final class FunctionTypeUtils {
return methods.get(0);
}
public static Type getFunctionTypeFromFunctionMethod(Method functionMethod) {
public static Type discoverFunctionTypeFromClass(Class<?> functionalClass) {
Assert.isTrue(isFunctional(functionalClass), "Type must be one of Supplier, Function or Consumer");
Type[] generics = functionalClass.getGenericInterfaces();
if (ObjectUtils.isEmpty(generics)) {
return functionalClass;
}
else {
for (Type generic : generics) {
if (generic instanceof ParameterizedType) {
Class<?> rawClsss = (Class<?>) ((ParameterizedType) generic).getRawType();
if (rawClsss.isAssignableFrom(Function.class)
|| rawClsss.isAssignableFrom(Consumer.class)
|| rawClsss.isAssignableFrom(Supplier.class)) {
return generic;
}
else {
return discoverFunctionTypeFromClass(rawClsss);
}
}
else {
return discoverFunctionTypeFromClass((Class<?>) generic);
}
}
}
return null;
}
public static Type discoverFunctionTypeFromFunctionMethod(Method functionMethod) {
Assert.isTrue(
functionMethod.getName().equals("apply") ||
functionMethod.getName().equals("accept") ||

View File

@@ -18,6 +18,7 @@ package org.springframework.cloud.function.context.catalog;
import java.lang.reflect.Type;
import java.sql.Date;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
@@ -30,6 +31,9 @@ import reactor.core.publisher.Mono;
import reactor.util.function.Tuple2;
import reactor.util.function.Tuple3;
import org.springframework.cloud.function.context.FunctionType;
import org.springframework.messaging.Message;
import static org.assertj.core.api.Assertions.assertThat;
/**
@@ -90,6 +94,26 @@ public class FunctionTypeUtilsTests {
assertThat(outputCount).isEqualTo(2);
}
@Test
public void testFunctionTypeByClassDiscovery() {
FunctionType type = FunctionType.of(FunctionTypeUtils.discoverFunctionTypeFromClass(Function.class));
assertThat(type.getInputType()).isAssignableFrom(Object.class);
type = FunctionType.of(FunctionTypeUtils.discoverFunctionTypeFromClass(MessageFunction.class));
assertThat(type.getInputType()).isAssignableFrom(String.class);
assertThat(type.getOutputType()).isAssignableFrom(String.class);
type = FunctionType.of(FunctionTypeUtils.discoverFunctionTypeFromClass(MyMessageFunction.class));
assertThat(type.getInputType()).isAssignableFrom(String.class);
assertThat(type.getOutputType()).isAssignableFrom(String.class);
type = FunctionType.of(FunctionTypeUtils.discoverFunctionTypeFromClass(MessageConsumer.class));
assertThat(type.getInputType()).isAssignableFrom(String.class);
type = FunctionType.of(FunctionTypeUtils.discoverFunctionTypeFromClass(MyMessageConsumer.class));
assertThat(type.getInputType()).isAssignableFrom(String.class);
}
// @Test
// public void testInputTypeByIndex() throws Exception {
// Type functionType = getReturnType("function");
@@ -163,4 +187,22 @@ public class FunctionTypeUtilsTests {
private Type getReturnType(String methodName) throws Exception {
return FunctionTypeUtilsTests.class.getDeclaredMethod(methodName).getGenericReturnType();
}
//============
private interface MessageFunction<T> extends Function<Message<String>, Message<String>> {
}
private interface MyMessageFunction extends MessageFunction<Date> {
}
private interface MessageConsumer<T> extends Consumer<Message<String>> {
}
private interface MyMessageConsumer extends MessageConsumer<Date> {
}
}