You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

添加@RefreshScope后报‘object is not an instance of declaring class’的原因与解决

问题描述

以下代码可正常运行:

@Configuration
public class RouteLocatorConfig {
    @Bean
    public RouteLocator routeLocator(RouteLocatorProvider routeLocatorProvider) {
        return routeLocatorProvider.getRouteLocator();
    }
}

但给RouteLocator的@Bean方法添加@RefreshScope后,应用启动失败:

@Configuration
public class RouteLocatorConfig {
    @Bean
    @RefreshScope
    public RouteLocator routeLocator(RouteLocatorProvider routeLocatorProvider) {
        return routeLocatorProvider.getRouteLocator();
    }
}

相关依赖类说明

  • RouteLocatorProvider是自定义@Component类,由WebClientConfig扫描:
@Configuration
@ComponentScan(basePackages = "by.afinny.apigateway")
public class WebClientConfig {
    @Bean
    @LoadBalanced
    public WebClient.Builder webClientBuilder() {
        return WebClient.builder();
    }
}
  • RouteLocatorProvider核心实现:
@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) {
                refreshRoutings();
            }
        });
    }

    private void refreshRoutings() {
        List<Application> applications = findOtherRegisteredApplications();
        Map<Application, SwaggerParseResult> docs = mapToDocs(applications);
        routeLocator = buildRouteLocatorFrom(docs);
    }

    // 简化后的辅助方法
    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();
    }
}

异常信息

添加@RefreshScope后触发的核心异常:

org.springframework.context.ApplicationContextException: Failed to start bean 'eurekaAutoServiceRegistration'
    ...
Caused by: java.lang.IllegalArgumentException: object is not an instance of declaring class
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:568)
    at org.springframework.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:281)
    at org.springframework.cloud.context.scope.GenericScope$LockedScopedProxyFactoryBean.invoke(GenericScope.java:482)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:244)
    at jdk.proxy2/jdk.proxy2.$Proxy107.getRoutes(Unknown Source)
    ...

对比验证

若改写RouteLocator的Bean方法为以下形式,应用可正常启动:

@Bean
@RefreshScope
public RouteLocator routeLocator(RouteLocatorBuilder routeLocatorBuilder) {
    return routeLocatorBuilder.routes().build();
}
异常原因
  1. RefreshScope代理机制冲突:给RouteLocator的Bean添加@RefreshScope后,Spring会为该Bean生成基于JDK动态代理的代理对象(因为RouteLocator是接口)。但RouteLocatorProvider中直接持有的RouteLocator是通过routeLocatorBuilder创建的具体实现类,并非Spring管理的代理对象。

  2. 反射调用类型不匹配:应用启动时,WeightCalculatorWebFilter会调用所有RouteLocator的getRoutes()方法。此时调用的是RefreshScope生成的代理对象,代理内部通过反射调用目标实例(即RouteLocatorProvider中持有的RouteLocator)的方法时,出现代理类型与实际实例类型不匹配的问题,触发object is not an instance of declaring class异常。

  3. 生命周期不一致:RouteLocatorProvider是单例Bean,内部的routeLocator实例在@PostConstruct或Eureka事件触发时被创建;而RefreshScope要求Bean在每次刷新时重新实例化,两者生命周期不匹配,导致代理无法正确管理RouteLocator实例。

解决方案

方案一:调整Bean创建逻辑,每次刷新重新生成RouteLocator

修改RouteLocatorConfig,让Bean方法每次刷新时都通过RouteLocatorProvider的公共方法重新生成RouteLocator,而非直接返回provider中已持有的实例:

@Configuration
public class RouteLocatorConfig {
    @Bean
    @RefreshScope
    public RouteLocator routeLocator(RouteLocatorProvider routeLocatorProvider) {
        List<Application> applications = routeLocatorProvider.findOtherRegisteredApplications();
        Map<Application, SwaggerParseResult> docs = routeLocatorProvider.mapToDocs(applications);
        return routeLocatorProvider.buildRouteLocatorFrom(docs);
    }
}

同时将RouteLocatorProvider中的findOtherRegisteredApplications、mapToDocs、buildRouteLocatorFrom方法改为公共方法,移除内部的routeLocator成员变量。

方案二:给RouteLocatorProvider添加@RefreshScope

将@RefreshScope添加到RouteLocatorProvider类上,让整个Provider成为可刷新Bean,内部的routeLocator实例会随Provider刷新重新创建:

@Component
@RefreshScope
public class RouteLocatorProvider {
    // 原有代码不变
}

对应的RouteLocatorConfig保持如下写法:

@Configuration
public class RouteLocatorConfig {
    @Bean
    public RouteLocator routeLocator(RouteLocatorProvider routeLocatorProvider) {
        return routeLocatorProvider.getRouteLocator();
    }
}

方案三:触发RefreshScope主动刷新

修改RouteLocatorProvider,移除内部持有的routeLocator实例,改为在Eureka事件触发时主动刷新RouteLocator Bean:

@Component
public class RouteLocatorProvider {
    private final EurekaClient eurekaClient;
    private final RefreshScope refreshScope;

    public RouteLocatorProvider(EurekaClient eurekaClient, RefreshScope refreshScope) {
        this.eurekaClient = eurekaClient;
        this.refreshScope = refreshScope;
    }

    @PostConstruct
    public void registerListener() {
        eurekaClient.registerEventListener(event -> {
            if (event instanceof CacheRefreshedEvent) {
                refreshScope.refresh("routeLocator");
            }
        });
    }
}

对应的RouteLocatorConfig保持带@RefreshScope的写法:

@Configuration
public class RouteLocatorConfig {
    @Bean
    @RefreshScope
    public RouteLocator routeLocator(RouteLocatorBuilder routeLocatorBuilder) {
        return routeLocatorBuilder.routes().build();
    }
}

内容的提问来源于stack exchange,提问作者Sergey Zolotarev

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 03:12:34