spring-cloud-gateway icon indicating copy to clipboard operation
spring-cloud-gateway copied to clipboard

Support Bean Context for Operation Method Invocation

Open nerdroid-dev opened this issue 3 months ago • 3 comments

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
}

nerdroid-dev avatar Oct 10 '25 14:10 nerdroid-dev

Please add a test that verifies the functionality

Sure! I will soon

nerdroid-dev avatar Oct 10 '25 14:10 nerdroid-dev

@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.

nerdroid-dev avatar Oct 11 '25 04:10 nerdroid-dev

@spencergibb I'd appreciate it if you could take a look when you get a chance!

nerdroid-dev avatar Oct 20 '25 01:10 nerdroid-dev