提问人:Kodigas 提问时间:11/17/2023 最后编辑:Kodigas 更新时间:11/19/2023 访问量:38
@RefreshScope导致“对象不是声明类的实例”。为什么?
@RefreshScope causes "object is not an instance of declaring class". Why?
问:
这工作原理:
@Configuration
public class RouteLocatorConfig {
@Bean
public RouteLocator routeLocator(RouteLocatorProvider routeLocatorProvider) {
return routeLocatorProvider.getRouteLocator();
}
}
但事实并非如此:
@Configuration
public class RouteLocatorConfig {
@Bean
@RefreshScope
public RouteLocator routeLocator(RouteLocatorProvider routeLocatorProvider) {
return routeLocatorProvider.getRouteLocator();
}
}
RouteLocatorProvider
是我自己写的一门课。它被注释为 a,并由另一个称为 .让我们暂时忽略它在语义上有问题@Component
@Configuration
WebClientConfig
@Configuration
@ComponentScan(basePackages = "by.afinny.apigateway")
public class WebClientConfig {
@Bean
@LoadBalanced
public WebClient.Builder webClientBuilder() {
return WebClient.builder();
}
}
@Component
public class RouteLocatorProvider {
private final EurekaClient eurekaClient;
private final WebClient.Builder webClientBuilder;
private final RouteLocatorBuilder routeLocatorBuilder;
private final OpenAPIV3Parser apiParser;
@Getter
private RouteLocator routeLocator;
public RouteLocatorProvider(EurekaClient eurekaClient, WebClient.Builder webClientBuilder,
RouteLocatorBuilder routeLocatorBuilder) {
this.eurekaClient = eurekaClient;
this.webClientBuilder = webClientBuilder;
this.routeLocatorBuilder = routeLocatorBuilder;
this.apiParser = new OpenAPIV3Parser();
}
@PostConstruct
public void registerListener() {
eurekaClient.registerEventListener(event -> {
if (event instanceof CacheRefreshedEvent) { // wtf, spring
refreshRoutings();
}
});
}
private void refreshRoutings() {
List<Application> applications = findOtherRegisteredApplications();
Map<Application, SwaggerParseResult> docs = mapToDocs(applications);
routeLocator = buildRouteLocatorFrom(docs);
// initially, I called refreshScope.refresh(RouteLocator.class) here but then removed it to isolate the cause – removing it didn't help
}
// more logic
您可能想知道所有这些方法都有什么作用。但这真的无关紧要。如果你像这样实现它们(只是为了让编译器满意),问题就完全一样了:
private List<Application> findOtherRegisteredApplications() {
return Collections.emptyList();
}
private Map<Application, SwaggerParseResult> mapToDocs(List<Application> applications) {
return Collections.emptyMap();
}
private RouteLocator buildRouteLocatorFrom(Map<Application, SwaggerParseResult> appsToDocs) {
return routeLocatorBuilder.routes().build();
}
对方法进行注释会导致以下异常:RouteLocator
@Bean
@RefreshScope
org.springframework.context.ApplicationContextException: Failed to start bean 'eurekaAutoServiceRegistration'
at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:182) ~[spring-context-6.0.12.jar:6.0.12]
at org.springframework.context.support.DefaultLifecycleProcessor$LifecycleGroup.start(DefaultLifecycleProcessor.java:357) ~[spring-context-6.0.12.jar:6.0.12]
at java.base/java.lang.Iterable.forEach(Iterable.java:75) ~[na:na]
at org.springframework.context.support.DefaultLifecycleProcessor.startBeans(DefaultLifecycleProcessor.java:156) ~[spring-context-6.0.12.jar:6.0.12]
at org.springframework.context.support.DefaultLifecycleProcessor.onRefresh(DefaultLifecycleProcessor.java:124) ~[spring-context-6.0.12.jar:6.0.12]
at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:958) ~[spring-context-6.0.12.jar:6.0.12]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:611) ~[spring-context-6.0.12.jar:6.0.12]
at org.springframework.boot.web.reactive.context.ReactiveWebServerApplicationContext.refresh(ReactiveWebServerApplicationContext.java:66) ~[spring-boot-3.1.4.jar:3.1.4]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:737) ~[spring-boot-3.1.4.jar:3.1.4]
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:439) ~[spring-boot-3.1.4.jar:3.1.4]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:315) ~[spring-boot-3.1.4.jar:3.1.4]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1309) ~[spring-boot-3.1.4.jar:3.1.4]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1298) ~[spring-boot-3.1.4.jar:3.1.4]
at by.afinny.apigateway.ApiGatewayV2Application.main(ApiGatewayV2Application.java:9) ~[classes/:na]
Caused by: java.lang.IllegalArgumentException: object is not an instance of declaring class
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
at org.springframework.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:281) ~[spring-core-6.0.12.jar:6.0.12]
at org.springframework.cloud.context.scope.GenericScope$LockedScopedProxyFactoryBean.invoke(GenericScope.java:482) ~[spring-cloud-context-4.0.4.jar:4.0.4]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[spring-aop-6.0.12.jar:6.0.12]
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:244) ~[spring-aop-6.0.12.jar:6.0.12]
at jdk.proxy2/jdk.proxy2.$Proxy107.getRoutes(Unknown Source) ~[na:na]
at reactor.core.publisher.FluxMergeSequential$MergeSequentialMain.onNext(FluxMergeSequential.java:208) ~[reactor-core-3.5.10.jar:3.5.10]
at reactor.core.publisher.FluxIterable$IterableSubscription.slowPath(FluxIterable.java:335) ~[reactor-core-3.5.10.jar:3.5.10]
at reactor.core.publisher.FluxIterable$IterableSubscription.request(FluxIterable.java:294) ~[reactor-core-3.5.10.jar:3.5.10]
at reactor.core.publisher.FluxMergeSequential$MergeSequentialMain.onSubscribe(FluxMergeSequential.java:198) ~[reactor-core-3.5.10.jar:3.5.10]
at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:201) ~[reactor-core-3.5.10.jar:3.5.10]
at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:83) ~[reactor-core-3.5.10.jar:3.5.10]
at reactor.core.publisher.InternalFluxOperator.subscribe(InternalFluxOperator.java:62) ~[reactor-core-3.5.10.jar:3.5.10]
at reactor.core.publisher.FluxDefer.subscribe(FluxDefer.java:54) ~[reactor-core-3.5.10.jar:3.5.10]
at reactor.core.publisher.Flux.subscribe(Flux.java:8773) ~[reactor-core-3.5.10.jar:3.5.10]
at reactor.core.publisher.Flux.blockLast(Flux.java:2752) ~[reactor-core-3.5.10.jar:3.5.10]
at org.springframework.cloud.gateway.filter.WeightCalculatorWebFilter.lambda$onApplicationEvent$0(WeightCalculatorWebFilter.java:140) ~[spring-cloud-gateway-server-4.0.7.jar:4.0.7]
at org.springframework.beans.factory.support.DefaultListableBeanFactory$DependencyObjectProvider.ifAvailable(DefaultListableBeanFactory.java:2070) ~[spring-beans-6.0.12.jar:6.0.12]
at org.springframework.cloud.gateway.filter.WeightCalculatorWebFilter.onApplicationEvent(WeightCalculatorWebFilter.java:140) ~[spring-cloud-gateway-server-4.0.7.jar:4.0.7]
at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:174) ~[spring-context-6.0.12.jar:6.0.12]
at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:167) ~[spring-context-6.0.12.jar:6.0.12]
at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:145) ~[spring-context-6.0.12.jar:6.0.12]
at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:437) ~[spring-context-6.0.12.jar:6.0.12]
at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:370) ~[spring-context-6.0.12.jar:6.0.12]
at org.springframework.cloud.gateway.route.RouteRefreshListener.reset(RouteRefreshListener.java:73) ~[spring-cloud-gateway-server-4.0.7.jar:4.0.7]
at org.springframework.cloud.gateway.route.RouteRefreshListener.onApplicationEvent(RouteRefreshListener.java:54) ~[spring-cloud-gateway-server-4.0.7.jar:4.0.7]
at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:174) ~[spring-context-6.0.12.jar:6.0.12]
at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:167) ~[spring-context-6.0.12.jar:6.0.12]
at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:145) ~[spring-context-6.0.12.jar:6.0.12]
at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:437) ~[spring-context-6.0.12.jar:6.0.12]
at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:370) ~[spring-context-6.0.12.jar:6.0.12]
at org.springframework.cloud.netflix.eureka.serviceregistry.EurekaAutoServiceRegistration.start(EurekaAutoServiceRegistration.java:85) ~[spring-cloud-netflix-eureka-client-4.0.3.jar:4.0.3]
at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:179) ~[spring-context-6.0.12.jar:6.0.12]
... 13 common frames omitted
Suppressed: java.lang.Exception: #block terminated with an error
at reactor.core.publisher.BlockingSingleSubscriber.blockingGet(BlockingSingleSubscriber.java:103) ~[reactor-core-3.5.10.jar:3.5.10]
at reactor.core.publisher.Flux.blockLast(Flux.java:2753) ~[reactor-core-3.5.10.jar:3.5.10]
... 30 common frames omitted
它一定与依赖有关,因为如果我像这样重写方法:RouteLocatorProvider
routeLocator()
@Bean
@RefreshScope
public RouteLocator routeLocator(RouteLocatorBuilder routeLocatorBuilder) {
return routeLocatorBuilder.routes().build();
}
应用程序成功启动(即,它确实启动)
为什么会发生这种情况,我该如何解决?
答:
根据异常日志
如果未使用 @RefreshScope 注释,那么不会代理定制 routeLocator bean。此时 routeLocatorProvider.getRouteLocator() 返回的 RouteLocator 实例其实是 null,即 NullBean,因为你通过监听 CacheRefreshedEvent 事件初始化了 routeLocator 变量,而这个事件是由 DiscoveryClient (initScheduledTasks()) 中的定时任务发送的,默认延迟为 30 秒。
@Bean
@Primary
@ConditionalOnMissingBean(name = "cachedCompositeRouteLocator")
public RouteLocator cachedCompositeRouteLocator(List<RouteLocator> routeLocators) {
return new CachingRouteLocator(new CompositeRouteLocator(Flux.fromIterable(routeLocators)));
}
因此,从主 RouteLocator 获取的 routeLocator 实际上只有一个内置的 RouteDefinitionRouteLocator(因为 NullBean 在注入时会被过滤),并且没有你自定义的 routeLocator。稍后调用 getRoutes() 时不会有问题。当然,在这种情况下,您可以自定义它。RouteLocator 等同于未定义。
如果使用 @RefreshScope 注解,则自定义 routeLocator 将被代理(但代理的真正 Bean 仍然是 NullBean)。routeLocators 实际上有两个 bean。最后,当 getRoutes() 被执行时,它将在 NullBean 实例上执行,因此报告对象不是声明类错误的实例
评论
refreshRoutings()
@PostConstruct
评论