diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/FunctionTypeUtils.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/FunctionTypeUtils.java index 678d0ae85..63c4777da 100644 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/FunctionTypeUtils.java +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/FunctionTypeUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2019-2022 the original author or authors. + * Copyright 2019-2025 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. @@ -78,6 +78,7 @@ * * @author Oleg Zhurakousky * @author Andrey Shlykov + * @author Artem Bilan * * @since 3.0 */ @@ -444,6 +445,12 @@ else if (!(type instanceof ParameterizedType)) { } } } + else if (type instanceof ParameterizedType) { + ResolvableType resolvableType = ResolvableType.forType(type); + if (FactoryBean.class.isAssignableFrom(resolvableType.toClass())) { + return resolvableType.getGeneric(0).getType(); + } + } return type; } diff --git a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/catalog/BeanFactoryAwareFunctionRegistryTests.java b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/catalog/BeanFactoryAwareFunctionRegistryTests.java index 05594b8ae..24c07e4fc 100644 --- a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/catalog/BeanFactoryAwareFunctionRegistryTests.java +++ b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/catalog/BeanFactoryAwareFunctionRegistryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 the original author or authors. + * Copyright 2019-2025 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. @@ -54,6 +54,7 @@ import reactor.util.function.Tuple3; import reactor.util.function.Tuples; +import org.springframework.beans.factory.FactoryBean; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.builder.SpringApplicationBuilder; @@ -83,6 +84,7 @@ /** * * @author Oleg Zhurakousky + * @author Artem Bilan * */ public class BeanFactoryAwareFunctionRegistryTests { @@ -849,6 +851,14 @@ public void testArrayPayloadOnFluxFunction() throws Exception { assertThat(result.size()).isEqualTo(3); } + @Test + void functionFromFactoryBeanIsProperlyResolved() { + FunctionCatalog catalog = configureCatalog(); + Function numberToStringFactoryBean = catalog.lookup("numberToStringFactoryBean"); + assertThat(numberToStringFactoryBean).isNotNull(); + assertThat(numberToStringFactoryBean.apply(1)).isEqualTo("1"); + } + @Test // see GH-707 public void testConcurrencyOnLookup() throws Exception { @@ -1300,6 +1310,23 @@ public Consumer> reactiveConsumer() { public Consumer> reactivePojoConsumer() { return flux -> flux.subscribe(v -> consumerInputRef.set(v)); } + + @Bean + FactoryBean> numberToStringFactoryBean() { + return new FactoryBean<>() { + + @Override + public Function getObject() { + return Number::toString; + } + + @Override + public Class getObjectType() { + return Function.class; + } + + }; + } } @EnableAutoConfiguration