Skip to content

Commit 97bea81

Browse files
olegzartembilan
authored andcommitted
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()`
1 parent bdd4320 commit 97bea81

File tree

6 files changed

+84
-9
lines changed

6 files changed

+84
-9
lines changed

spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/BeanFactoryAwareFunctionRegistry.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
import org.springframework.aop.support.AopUtils;
4848
import org.springframework.beans.BeansException;
4949
import org.springframework.beans.factory.BeanFactory;
50+
import org.springframework.beans.factory.FactoryBean;
5051
import org.springframework.beans.factory.annotation.Qualifier;
5152
import org.springframework.beans.factory.config.BeanDefinition;
5253
import org.springframework.cloud.function.context.FunctionCatalog;
@@ -188,6 +189,10 @@ private Type discoverFunctionType(Object function, String... names) {
188189
boolean beanDefinitionExists = false;
189190
for (int i = 0; i < names.length && !beanDefinitionExists; i++) {
190191
beanDefinitionExists = this.applicationContext.getBeanFactory().containsBeanDefinition(names[i]);
192+
if (this.applicationContext.containsBean("&" + names[i])) {
193+
Class<?> objectType = this.applicationContext.getBean("&" + names[i], FactoryBean.class).getObjectType();
194+
return FunctionTypeUtils.discoverFunctionTypeFromClass(objectType);
195+
}
191196
}
192197
if (!beanDefinitionExists) {
193198
logger.info("BeanDefinition for function name(s) '" + Arrays.asList(names) +
@@ -430,7 +435,9 @@ public class FunctionInvocationWrapper implements Function<Object, Object>, Cons
430435

431436
FunctionInvocationWrapper(Object target, Type functionType, String functionDefinition, String... acceptedOutputMimeTypes) {
432437
this.target = target;
433-
this.composed = !target.getClass().getName().contains("EnhancerBySpringCGLIB") && target.getClass().getDeclaredFields().length > 1;
438+
this.composed = !target.getClass().getName().contains("$$EnhancerBySpringCGLIB")
439+
&& !AopUtils.isAopProxy(target) && !AopUtils.isJdkDynamicProxy(target)
440+
&& target.getClass().getDeclaredFields().length > 1;
434441
this.functionType = functionType;
435442
this.acceptedOutputMimeTypes = acceptedOutputMimeTypes;
436443
this.functionDefinition = functionDefinition;

spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/FunctionTypeUtils.java

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,34 @@ else if (Function.class.isAssignableFrom(pojoFunctionClass) || BiFunction.class.
105105
return methods.get(0);
106106
}
107107

108-
public static Type getFunctionTypeFromFunctionMethod(Method functionMethod) {
108+
public static Type discoverFunctionTypeFromClass(Class<?> functionalClass) {
109+
Assert.isTrue(isFunctional(functionalClass), "Type must be one of Supplier, Function or Consumer");
110+
Type[] generics = functionalClass.getGenericInterfaces();
111+
if (ObjectUtils.isEmpty(generics)) {
112+
return functionalClass;
113+
}
114+
else {
115+
for (Type generic : generics) {
116+
if (generic instanceof ParameterizedType) {
117+
Class<?> rawClsss = (Class<?>) ((ParameterizedType) generic).getRawType();
118+
if (rawClsss.isAssignableFrom(Function.class)
119+
|| rawClsss.isAssignableFrom(Consumer.class)
120+
|| rawClsss.isAssignableFrom(Supplier.class)) {
121+
return generic;
122+
}
123+
else {
124+
return discoverFunctionTypeFromClass(rawClsss);
125+
}
126+
}
127+
else {
128+
return discoverFunctionTypeFromClass((Class<?>) generic);
129+
}
130+
}
131+
}
132+
return null;
133+
}
134+
135+
public static Type discoverFunctionTypeFromFunctionMethod(Method functionMethod) {
109136
Assert.isTrue(
110137
functionMethod.getName().equals("apply") ||
111138
functionMethod.getName().equals("accept") ||

spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/catalog/FunctionTypeUtilsTests.java

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919

2020
import java.lang.reflect.Type;
21+
import java.sql.Date;
2122
import java.util.List;
2223
import java.util.Map;
2324
import java.util.function.Consumer;
@@ -30,6 +31,9 @@
3031
import reactor.util.function.Tuple2;
3132
import reactor.util.function.Tuple3;
3233

34+
import org.springframework.cloud.function.context.FunctionType;
35+
import org.springframework.messaging.Message;
36+
3337
import static org.assertj.core.api.Assertions.assertThat;
3438

3539
/**
@@ -90,6 +94,26 @@ public void testOutputCount() throws Exception {
9094
assertThat(outputCount).isEqualTo(2);
9195
}
9296

97+
@Test
98+
public void testFunctionTypeByClassDiscovery() {
99+
FunctionType type = FunctionType.of(FunctionTypeUtils.discoverFunctionTypeFromClass(Function.class));
100+
assertThat(type.getInputType()).isAssignableFrom(Object.class);
101+
102+
type = FunctionType.of(FunctionTypeUtils.discoverFunctionTypeFromClass(MessageFunction.class));
103+
assertThat(type.getInputType()).isAssignableFrom(String.class);
104+
assertThat(type.getOutputType()).isAssignableFrom(String.class);
105+
106+
type = FunctionType.of(FunctionTypeUtils.discoverFunctionTypeFromClass(MyMessageFunction.class));
107+
assertThat(type.getInputType()).isAssignableFrom(String.class);
108+
assertThat(type.getOutputType()).isAssignableFrom(String.class);
109+
110+
type = FunctionType.of(FunctionTypeUtils.discoverFunctionTypeFromClass(MessageConsumer.class));
111+
assertThat(type.getInputType()).isAssignableFrom(String.class);
112+
113+
type = FunctionType.of(FunctionTypeUtils.discoverFunctionTypeFromClass(MyMessageConsumer.class));
114+
assertThat(type.getInputType()).isAssignableFrom(String.class);
115+
}
116+
93117
// @Test
94118
// public void testInputTypeByIndex() throws Exception {
95119
// Type functionType = getReturnType("function");
@@ -163,4 +187,22 @@ private static Supplier<Tuple2<String, String>> multiOutputSupplier() {
163187
private Type getReturnType(String methodName) throws Exception {
164188
return FunctionTypeUtilsTests.class.getDeclaredMethod(methodName).getGenericReturnType();
165189
}
190+
191+
//============
192+
193+
private interface MessageFunction<T> extends Function<Message<String>, Message<String>> {
194+
195+
}
196+
197+
private interface MyMessageFunction extends MessageFunction<Date> {
198+
199+
}
200+
201+
private interface MessageConsumer<T> extends Consumer<Message<String>> {
202+
203+
}
204+
205+
private interface MyMessageConsumer extends MessageConsumer<Date> {
206+
207+
}
166208
}

spring-cloud-function-deployer/src/main/java/org/springframework/cloud/function/deployer/FunctionArchiveDeployer.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ private FunctionRegistration<?> discovereAndLoadFunctionFromClassName(String fun
204204
ReflectionUtils.doWithMethods(functionClass, new MethodCallback() {
205205
@Override
206206
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
207-
typeRef.set(FunctionTypeUtils.getFunctionTypeFromFunctionMethod(method));
207+
typeRef.set(FunctionTypeUtils.discoverFunctionTypeFromFunctionMethod(method));
208208
}
209209
}, new MethodFilter() {
210210
@Override

spring-cloud-function-samples/function-sample-spring-integration/src/main/java/example/FunctionSampleSpringIntegrationApplication.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,14 @@
2626
import org.springframework.integration.handler.LoggingHandler;
2727
import org.springframework.messaging.Message;
2828

29-
//@SpringBootApplication
29+
@SpringBootApplication
3030
public class FunctionSampleSpringIntegrationApplication {
3131

3232
public static void main(String[] args) {
3333
SpringApplication.run(FunctionSampleSpringIntegrationApplication.class, args);
3434
}
3535

36-
//@Bean
36+
@Bean
3737
public IntegrationFlow uppercaseFlow() {
3838
return IntegrationFlows.from(MessageFunction.class, "uppercase")
3939
.<String, String>transform(String::toUpperCase)
@@ -43,5 +43,4 @@ public IntegrationFlow uppercaseFlow() {
4343
public interface MessageFunction extends Function<Message<String>, Message<String>> {
4444

4545
}
46-
4746
}

spring-cloud-function-samples/function-sample-spring-integration/src/test/java/example/FunctionSampleSpringIntegrationApplicationTests.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,14 @@
2929
import org.springframework.http.MediaType;
3030
import org.springframework.test.context.junit4.SpringRunner;
3131

32-
//@RunWith(SpringRunner.class)
33-
//@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
32+
@RunWith(SpringRunner.class)
33+
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
3434
public class FunctionSampleSpringIntegrationApplicationTests {
3535

3636
@Autowired
3737
private TestRestTemplate restTemplate;
3838

39-
//@Test
39+
@Test
4040
public void upperCase() {
4141
HttpHeaders httpHeaders = new HttpHeaders();
4242
httpHeaders.setContentType(MediaType.APPLICATION_JSON);

0 commit comments

Comments
 (0)