Spring Cloud Gateway Native镜像运行时Kotlin反射内部错误求助
问题
已成功生成基于Kotlin开发的Spring Cloud Gateway(2022.0.0 - Spring Boot 3.0.0)应用的Native镜像,安全配置代码如下:
@EnableReactiveMethodSecurity @Configuration(proxyBeanMethods = false) class SecurityConfiguration(private val clientService: ClientService, private val env: Environment) { @Bean fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain { return http.requestCache { it.requestCache(NoOpServerRequestCache.getInstance()) }.headers { headers -> headers.frameOptions { frameOptions -> frameOptions.disable() } }.csrf { it.disable() } .securityContextRepository(NoOpServerSecurityContextRepository.getInstance()) .addFilterAt(basicAuthenticationFilter(), SecurityWebFiltersOrder.HTTP_BASIC) .authorizeExchange() .pathMatchers("/actuator/health").permitAll() ... .and() .build() } @Bean fun passwordEncoder(): PasswordEncoder { return createDelegatingPasswordEncoder() } private fun basicAuthenticationFilter(): AuthenticationWebFilter { val authManager = ApiAuthenticationManager(clientService) val apiAuthenticationFilter = AuthenticationWebFilter(authManager) return apiAuthenticationFilter } }
运行Native镜像时抛出以下异常:
kotlin.reflect.jvm.internal.KotlinReflectionInternalError: Could not compute caller for function: private open fun basicAuthenticationFilter(): org.springframework.security.web.server.authentication.AuthenticationWebFilter defined in xxx.configuration.SecurityConfiguration[DeserializedSimpleFunctionDescriptor@3ff718f] (member = null) at kotlin.reflect.jvm.internal.KFunctionImpl$caller$2.invoke(KFunctionImpl.kt:88) at kotlin.reflect.jvm.internal.KFunctionImpl$caller$2.invoke(KFunctionImpl.kt:61) at kotlin.reflect.jvm.internal.ReflectProperties$LazyVal.invoke(ReflectProperties.java:63) at kotlin.reflect.jvm.internal.ReflectProperties$Val.getValue(ReflectProperties.java:32) at kotlin.reflect.jvm.internal.KFunctionImpl.getCaller(KFunctionImpl.kt:61) at kotlin.reflect.jvm.ReflectJvmMapping.getJavaMethod(ReflectJvmMapping.kt:63) at kotlin.reflect.jvm.ReflectJvmMapping.getKotlinFunction(ReflectJvmMapping.kt:136) at org.springframework.core.MethodParameter$KotlinDelegate.getGenericReturnType(MethodParameter.java:914) at org.springframework.core.MethodParameter.getGenericParameterType(MethodParameter.java:510) at org.springframework.core.SerializableTypeWrapper$MethodParameterTypeProvider.getType(SerializableTypeWrapper.java:291) at org.springframework.core.SerializableTypeWrapper.forTypeProvider(SerializableTypeWrapper.java:107) at org.springframework.core.ResolvableType.forType(ResolvableType.java:1413) at org.springframework.core.ResolvableType.forMethodParameter(ResolvableType.java:1334) at org.springframework.core.ResolvableType.forMethodParameter(ResolvableType.java:1316) at org.springframework.core.ResolvableType.forMethodParameter(ResolvableType.java:1283) at org.springframework.core.ResolvableType.forMethodReturnType(ResolvableType.java:1228) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.getTypeForFactoryMethod(AbstractAutowireCapableBeanFactory.java:814) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.determineTargetType(AbstractAutowireCapableBeanFactory.java:681) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.predictBeanType(AbstractAutowireCapableBeanFactory.java:652) at org.springframework.beans.factory.support.AbstractBeanFactory.isFactoryBean(AbstractBeanFactory.java:1632) at org.springframework.beans.factory.support.DefaultListableBeanFactory.doGetBeanNamesForType(DefaultListableBeanFactory.java:559) at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanNamesForType(DefaultListableBeanFactory.java:531) at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:106) at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:745) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:565) at org.springframework.boot.web.reactive.context.ReactiveWebServerApplicationContext.refresh(ReactiveWebServerApplicationContext.java:66) at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:730) at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:432) at org.springframework.boot.SpringApplication.run(SpringApplication.java:308) at org.springframework.boot.SpringApplication.run(SpringApplication.java:1302) at org.springframework.boot.SpringApplication.run(SpringApplication.java:1291) at xxx.GatewayApplicationKt.main(GatewayApplication.kt:27) Exception in thread "main" kotlin.reflect.jvm.internal.KotlinReflectionInternalError: Could not compute caller for function: private open fun basicAuthenticationFilter(): org.springframework.security.web.server.authentication.AuthenticationWebFilter defined in xxx.configuration.SecurityConfiguration[DeserializedSimpleFunctionDescriptor@3ff718f] (member = null)
问题出在调用.addFilterAt(basicAuthenticationFilter(), SecurityWebFiltersOrder.HTTP_BASIC)时,私有函数触发了Kotlin反射机制导致崩溃。
解决思路
方案1:将私有函数改为公开并注册为Bean
把basicAuthenticationFilter标记为@Bean,去掉private修饰符(Kotlin默认公开),让Spring直接管理这个Bean,避免反射调用私有方法:
@Bean fun basicAuthenticationFilter(): AuthenticationWebFilter { val authManager = ApiAuthenticationManager(clientService) val apiAuthenticationFilter = AuthenticationWebFilter(authManager) return apiAuthenticationFilter }
在springSecurityFilterChain中直接注入该Bean:
@Bean fun springSecurityFilterChain(http: ServerHttpSecurity, basicAuthFilter: AuthenticationWebFilter): SecurityWebFilterChain { return http.requestCache { it.requestCache(NoOpServerRequestCache.getInstance()) }.headers { headers -> headers.frameOptions { frameOptions -> frameOptions.disable() } }.csrf { it.disable() } .securityContextRepository(NoOpServerSecurityContextRepository.getInstance()) .addFilterAt(basicAuthFilter, SecurityWebFiltersOrder.HTTP_BASIC) .authorizeExchange() .pathMatchers("/actuator/health").permitAll() ... .and() .build() }
方案2:内联私有函数逻辑
直接把basicAuthenticationFilter的逻辑写到springSecurityFilterChain方法内部,彻底避免函数调用带来的反射问题:
@Bean fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain { // 直接创建过滤器实例 val authManager = ApiAuthenticationManager(clientService) val basicAuthFilter = AuthenticationWebFilter(authManager) return http.requestCache { it.requestCache(NoOpServerRequestCache.getInstance()) }.headers { headers -> headers.frameOptions { frameOptions -> frameOptions.disable() } }.csrf { it.disable() } .securityContextRepository(NoOpServerSecurityContextRepository.getInstance()) .addFilterAt(basicAuthFilter, SecurityWebFiltersOrder.HTTP_BASIC) .authorizeExchange() .pathMatchers("/actuator/health").permitAll() ... .and() .build() }
同时删除原来的private fun basicAuthenticationFilter()方法。
方案3:添加Native镜像反射配置
如果必须保留私有函数,可通过reflect-config.json告诉GraalVM需要反射访问该方法。在src/main/resources/META-INF/native-image目录下创建该文件,内容如下:
[ { "name": "xxx.configuration.SecurityConfiguration", "methods": [ { "name": "basicAuthenticationFilter", "parameterTypes": [] } ] } ]
注意替换xxx.configuration为实际包路径。此方案需要维护反射配置,不如前两种简洁。
内容的提问来源于stack exchange,提问作者codependent
相关产品推荐
相关产品推荐

