@RefreshScope 导致“对象不是声明类的实例”。为什么?

问题描述 投票:0回答:1

这有效:

@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
是我自己写的一个类。它被注释为
@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();
    }

应用程序成功启动(即,它确实启动)

为什么会发生这种情况以及如何解决?

java spring-cloud spring-cloud-gateway
1个回答
0
投票

根据异常日志

如果不使用@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实例上执行,因此报object is not an instance of declaring class错误

© www.soinside.com 2019 - 2024. All rights reserved.