Support Bean Context for Operation Method Invocation
Description
While migrating our gateway service from spring-cloud-gateway-server-webflux to spring-cloud-gateway-server-webmvc, I discovered a limitation in how operation methods are currently invoked in spring-cloud-gateway-server-webmvc
In spring-cloud-gateway-server-webflux, it is possible to rely on spring bean lifecycle for dependency injection and to compose logic in a typical spring-oriented manner. However, in spring-cloud-gateway-server-webmvc, RouterFunctionHolderFactory currently never attempts to resolve an invocation target when passing an OperationMethod to ReflectiveOperationInvoker. As a result, methods defined as instance methods within a spring bean cannot be used as operation methods — only static methods are currently supported.
In my opinion, given that this project is part of the spring cloud gateway family, it seems natural to support resolving a bean instance from the application context when the declaring type of an OperationMethod is registered as a bean. This behavior would align with spring’s general design philosophy, allowing bean-managed components to participate in operation method invocation without requiring them to be static.
The proposed change introduces a small enhancement to RouterFunctionHolderFactory, enabling resolution of a bean instance from the BeanFactory when invoking non-static OperationMethods. If the declaring type is not registered as a bean or multiple candidates are found, the invocation gracefully falls back to the existing reflective behavior — ensuring full backward compatibility.
Use Case Example
@Component
class SampleGatewayNameFilter(
private val sampleGatewayNameService: SampleGatewayNameService
) : SimpleFilterSupplier(SampleGatewayNameFilter::class.java) {
companion object {
private const val X_SAMPLE_GW_APP_NAME = "X-SAMPLE-GW-NAME"
}
data class Config(
val prefixes: List<String>
)
@Configurable
fun addGatewayName(config: Config): HandlerFilterFunction<ServerResponse, ServerResponse> {
return FilterFunctions.addRequestHeader(
X_SAMPLE_GW_APP_NAME,
"${config.prefixes.joinToString("-")}-${sampleGatewayNameService.getGatewayName()}"
)
}
}
@Service
class SampleGatewayNameService(
@Value("\${spring.application.name}")
private val appName: String
) {
fun getGatewayName(): String = appName
}
Please add a test that verifies the functionality
Sure! I will soon
@spencergibb I’ve added a test that verifies non-static methods are properly resolved to Spring beans when available. Let me know if you'd like any adjustments or additional cases.
@spencergibb I'd appreciate it if you could take a look when you get a chance!