FeignClient子上下文机制详解

小豆丁 10小时前 ⋅ 4 阅读
聊天记录
头像

在FeignClient的场景中,每个FeignClient都有独立的子上下文,所以每个子上下文都会有自己的Bean实例,即使配置类有@Configuration注解。 子上下文是什么?

头像

这是一个非常好的问题,触及了 Spring Cloud Feign 的核心机制。让我详细解释 子上下文(FeignContext) 的概念和工作原理。

什么是子上下文?

在 Spring Cloud Feign 中,子上下文是一个独立的、隔离的 ApplicationContext,专门为某个 FeignClient 创建。它有如下特点:

JAVA
ApplicationContext 层次结构:

主 ApplicationContext (Spring Boot 应用)
    ├── 子上下文 FeignContext-A (为 FeignClient-A 创建)
    ├── 子上下文 FeignContext-B (为 FeignClient-B 创建)  
    ├── 子上下文 FeignContext-C (为 FeignClient-C 创建)
    └── ... 总共 313 个

子上下文的工作原理

1. 创建时机

JAVA
@FeignClient(name = "user-service", 
             configuration = UserServiceConfig.class)
public interface UserServiceClient {
    // FeignClient 接口
}

@FeignClient(name = "order-service",
             configuration = OrderServiceConfig.class)  
public interface OrderServiceClient {
    // 另一个 FeignClient
}

启动时

  • Spring Cloud 为每个 @FeignClient 创建一个独立的 FeignContext
  • 每个 FeignContext 都是主 ApplicationContext 的子上下文
  • 每个子上下文都有自己的 Bean 定义

2. 代码示例验证

JAVA
@SpringBootTest
class FeignContextTest {
    
    @Autowired
    private ApplicationContext parentContext;  // 主上下文
    
    @Autowired
    private FeignContext feignContext;  // FeignContext 工厂
    
    @Test
    void testChildContextHierarchy() {
        // 获取 user-service 的子上下文
        ApplicationContext userContext = feignContext.getInstance(
            "user-service", ApplicationContext.class);
        
        // 获取 order-service 的子上下文  
        ApplicationContext orderContext = feignContext.getInstance(
            "order-service", ApplicationContext.class);
        
        // 验证父子关系
        System.out.println("userContext 的父上下文: " + 
            userContext.getParent());
        System.out.println("与 parentContext 相同: " + 
            (userContext.getParent() == parentContext));  // true
        
        // 验证不同的子上下文
        System.out.println("userContext == orderContext: " + 
            (userContext == orderContext));  // false
        
        // 验证不同的 Bean
        Object userConfig = userContext.getBean("userServiceConfig");
        Object orderConfig = orderContext.getBean("userServiceConfig");
        System.out.println("配置类相同: " + (userConfig == orderConfig));  // false
    }
}

子上下文的隔离性

示例 1:Bean 隔离

JAVA
@Configuration
public class ClientConfig {
    
    @Bean
    public OkHttpClient okHttpClient() {
        return new OkHttpClient.Builder()
            .connectTimeout(5, TimeUnit.SECONDS)
            .build();
    }
    
    @Bean
    public RequestInterceptor interceptor() {
        return template -> {
            template.header("X-Client-Name", "custom-client");
        };
    }
}

结果

主 ApplicationContext
    ├── FeignContext-user-service
    │   ├── OkHttpClient@1234  (user-service 专用)
    │   └── RequestInterceptor@5678
    │
    └── FeignContext-order-service
        ├── OkHttpClient@9012  (order-service 专用)
        └── RequestInterceptor@3456

每个子上下文有独立的、完整的 Bean 集合

示例 2:验证隔离性

JAVA
@Component
public class BeanCounter {
    
    private static final Map<String, List<OkHttpClient>> clientsByContext = 
        new ConcurrentHashMap<>();
    
    public void recordClient(String contextName, OkHttpClient client) {
        clientsByContext.computeIfAbsent(contextName, k -> new ArrayList<>())
            .add(client);
    }
}

@Configuration
public class TrackingConfig {
    
    @Autowired
    private BeanCounter counter;
    
    @Bean
    public OkHttpClient okHttpClient() {
        OkHttpClient client = new OkHttpClient();
        
        // 记录是哪个上下文创建的
        String contextName = determineContextName();
        counter.recordClient(contextName, client);
        
        return client;
    }
}

运行后会看到 313 个不同的 OkHttpClient 实例。

子上下文的继承关系

虽然子上下文是独立的,但它们继承父上下文的 Bean:

JAVA
@Configuration
public class ParentConfig {
    
    @Bean
    public ConnectionPool sharedConnectionPool() {
        // 这个 Bean 在主上下文中
        return new ConnectionPool(200, 5, TimeUnit.MINUTES);
    }
}

public class ClientConfig {
    
    @Bean
    public OkHttpClient okHttpClient(ConnectionPool pool) {
        // 这个 pool 是从父上下文继承的
        return new OkHttpClient.Builder()
            .connectionPool(pool)
            .build();
    }
}

继承规则

  1. 子上下文可以访问父上下文的 Bean
  2. 子上下文可以定义同名的 Bean,覆盖父上下文的定义
  3. 父上下文不能访问子上下文的 Bean

查看实际的上下文结构

JAVA
@SpringBootTest
class ContextDebugTest {
    
    @Autowired
    private ApplicationContext context;
    
    @Test
    void debugContextHierarchy() {
        printContextHierarchy(context, "");
    }
    
    private void printContextHierarchy(ApplicationContext ctx, String indent) {
        System.out.println(indent + "Context: " + ctx.getDisplayName());
        System.out.println(indent + "  ID: " + ctx.getId());
        System.out.println(indent + "  Parent: " + 
            (ctx.getParent() != null ? ctx.getParent().getId() : "null"));
        
        // 统计 Bean
        String[] beanNames = ctx.getBeanDefinitionNames();
        System.out.println(indent + "  Bean 数量: " + beanNames.length);
        
        // 显示 Feign 相关的 Bean
        Arrays.stream(beanNames)
            .filter(name -> name.contains("Feign") || 
                           name.contains("Client") || 
                           name.contains("Config"))
            .forEach(name -> System.out.println(indent + "    - " + name));
        
        // 递归查看子上下文
        if (ctx instanceof HierarchicalBeanFactory) {
            HierarchicalBeanFactory hbf = (HierarchicalBeanFactory) ctx;
            if (hbf.getParentBeanFactory() instanceof ApplicationContext) {
                ApplicationContext parent = 
                    (ApplicationContext) hbf.getParentBeanFactory();
                printContextHierarchy(parent, indent + "  ");
            }
        }
    }
}

为什么会创建子上下文?

Spring Cloud Feign 创建子上下文的主要原因是:

1. 配置隔离

JAVA
// 不同的微服务可能需要不同的配置
@FeignClient(name = "payment-service",
             configuration = PaymentConfig.class)  // 支付专用配置
interface PaymentClient {}

@FeignClient(name = "inventory-service", 
             configuration = InventoryConfig.class)  // 库存专用配置
interface InventoryClient {}

2. Bean 覆盖

JAVA
// 主上下文
@Configuration
public class GlobalConfig {
    
    @Bean
    public Decoder defaultDecoder() {
        return new JacksonDecoder();
    }
}

// Client A 需要不同的 Decoder
public class ClientAConfig {
    
    @Bean
    public Decoder customDecoder() {  // 覆盖父上下文的 Decoder
        return new CustomDecoder();
    }
}

3. 生命周期管理

JAVA
// 每个 FeignClient 可以有自己的销毁逻辑
public class ClientConfig {
    
    @Bean
    @Scope("prototype")
    public Client client() {
        return new Client();
    }
    
    @PreDestroy
    public void cleanup() {
        // 只清理这个客户端的资源
    }
}

验证子上下文的存在

方法 1:通过日志

YAML
# application.yml
logging:
  level:
    org.springframework.cloud.openfeign: DEBUG
    org.springframework.context: DEBUG

启动时日志会显示:

Creating new FeignContext for client: user-service
Creating new FeignContext for client: order-service

方法 2:编程方式

JAVA
@RestController
public class DebugController {
    
    @Autowired
    private FeignContext feignContext;
    
    @GetMapping("/debug/feign-contexts")
    public Map<String, Object> listFeignContexts() {
        Map<String, Object> result = new HashMap<>();
        
        // 获取所有 FeignClient 名称
        String[] clientNames = getFeignClientNames();
        
        for (String clientName : clientNames) {
            ApplicationContext childContext = 
                feignContext.getInstance(clientName, ApplicationContext.class);
            
            Map<String, Object> contextInfo = new HashMap<>();
            contextInfo.put("parent", childContext.getParent() != null ? 
                childContext.getParent().getId() : "null");
            contextInfo.put("beanCount", childContext.getBeanDefinitionCount());
            
            // 获取特定类型的 Bean
            Map<String, OkHttpClient> clients = 
                childContext.getBeansOfType(OkHttpClient.class);
            contextInfo.put("okHttpClientCount", clients.size());
            
            result.put(clientName, contextInfo);
        }
        
        return result;
    }
}

如何避免 313 个重复的 Bean?

理解了子上下文后,解决方案就清晰了:

方案 1:共享配置到父上下文

JAVA
// 1. 在主上下文中定义共享 Bean
@Configuration
public class SharedConfig {
    
    @Bean
    public ConnectionPool sharedConnectionPool() {
        return new ConnectionPool(200, 5, TimeUnit.MINUTES);
    }
}

// 2. 在 FeignClient 配置中注入
public class ClientConfig {
    
    @Bean
    public OkHttpClient okHttpClient(
        // 从父上下文注入共享的连接池
        @Autowired ConnectionPool sharedConnectionPool) {
        
        return new OkHttpClient.Builder()
            .connectionPool(sharedConnectionPool)  // 共享
            .build();
    }
}

方案 2:使用原型 Bean

JAVA
@Configuration
public class ClientConfig {
    
    // 原型 Bean,每次注入时创建新实例
    @Bean
    @Scope("prototype")
    public OkHttpClient okHttpClientPrototype(
        @Autowired ConnectionPool sharedPool) {
        
        return new OkHttpClient.Builder()
            .connectionPool(sharedPool)
            .build();
    }
    
    // 单例 Bean,但复用连接池
    @Bean
    public OkHttpClient okHttpClient(ConnectionPool sharedPool) {
        return okHttpClientPrototype(sharedPool);
    }
}

子上下文的内存布局

内存布局(简化):
┌─────────────────────────────────────────────────┐
│          主 ApplicationContext                   │
│  ┌──────────────────────────────────────────┐  │
│  │ 共享 ConnectionPool@1234                  │  │
│  │ 其他共享 Bean...                          │  │
│  └──────────────────────────────────────────┘  │
│                                                 │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐    │
│  │FeignContext││FeignContext││FeignContext│    │
│  │  A        ││  B        ││  C        │    │
│  │  ┌──────┐ ││  ┌──────┐ ││  ┌──────┐ │    │
│  │  │ConfigA│ ││  │ConfigB│ ││  │ConfigC│ │    │
│  │  │@4567 │ ││  │@8910 │ ││  │@1112 │ │    │
│  │  └──────┘ ││  └──────┘ ││  └──────┘ │    │
│  └──────────┘ └──────────┘ └──────────┘    │
└─────────────────────────────────────────────────┘

每个 FeignContext 有自己的:
1. Config 实例
2. 从 Config 创建的 Bean
3. 但可以访问父上下文的共享 Bean

总结

子上下文是 Spring Cloud Feign 为每个 FeignClient 创建的独立 ApplicationContext

  1. 隔离性:每个子上下文是独立的,有自己的 Bean 实例
  2. 继承性:子上下文可以访问父上下文的 Bean
  3. 覆盖性:子上下文可以定义同名 Bean 覆盖父上下文
  4. 独立性:即使配置类有 @Configuration,每个子上下文也会独立实例化
  5. 问题根源:313 个 FeignClient → 313 个子上下文 → 313 个重复的 Bean 实例
  6. 解决方案:将共享 Bean 提升到父上下文,子上下文通过注入共享

理解了子上下文,就能明白为什么会出现 313 个重复实例,也知道了如何正确设计配置结构来避免这个问题。

头像

子上下文是Spring 原生支持吗?

头像

是的,子上下文是 Spring 框架的原生特性,称为 ApplicationContext 层次结构(Hierarchical ApplicationContext)。

这是 Spring 的核心设计之一,让我详细解释:

Spring 原生支持的上下文层次结构

1. 基本概念

JAVA
// Spring 原生支持创建父子上下文
public interface ApplicationContext extends ... {
    ApplicationContext getParent();  // 获取父上下文
    
    // 层次结构相关方法
    boolean containsBeanDefinition(String beanName);
    String[] getBeanDefinitionNames();
}

2. 创建父子上下文的原生方式

JAVA
// 手动创建父子上下文层次结构
public class SpringHierarchyDemo {
    
    public static void main(String[] args) {
        // 1. 创建父上下文
        AnnotationConfigApplicationContext parentContext = 
            new AnnotationConfigApplicationContext();
        parentContext.register(ParentConfig.class);
        parentContext.refresh();
        
        // 2. 创建子上下文,并设置父上下文
        AnnotationConfigApplicationContext childContext = 
            new AnnotationConfigApplicationContext();
        childContext.setParent(parentContext);  // 设置父子关系
        childContext.register(ChildConfig.class);
        childContext.refresh();
        
        // 3. 验证层次结构
        System.out.println("Child parent: " + (childContext.getParent() == parentContext));
        // 输出: true
        
        // 4. 验证 Bean 访问
        // 子可以访问父的 Bean
        ParentBean parentBeanFromChild = childContext.getBean(ParentBean.class);
        System.out.println("子上下文能访问父的 Bean: " + (parentBeanFromChild != null));
        // 输出: true
        
        // 父不能访问子的 Bean
        try {
            ChildBean childBeanFromParent = parentContext.getBean(ChildBean.class);
        } catch (NoSuchBeanDefinitionException e) {
            System.out.println("父上下文不能访问子的 Bean: " + e.getMessage());
        }
        
        childContext.close();
        parentContext.close();
    }
}

@Configuration
class ParentConfig {
    @Bean
    public ParentBean parentBean() {
        return new ParentBean();
    }
}

@Configuration  
class ChildConfig {
    @Bean
    public ChildBean childBean() {
        return new ChildBean();
    }
}

Spring 中上下文层次结构的常见用途

1. Spring MVC 的经典分层

JAVA
// Spring MVC 的传统配置
public class WebAppInitializer implements WebApplicationInitializer {
    
    @Override
    public void onStartup(ServletContext servletContext) {
        // 1. 创建根上下文(父)
        AnnotationConfigWebApplicationContext rootContext = 
            new AnnotationConfigWebApplicationContext();
        rootContext.register(RootConfig.class);
        
        // 2. 创建 Servlet 上下文(子)
        AnnotationConfigWebApplicationContext dispatcherContext = 
            new AnnotationConfigWebApplicationContext();
        dispatcherContext.register(WebMvcConfig.class);
        dispatcherContext.setParent(rootContext);  // 设置父子关系
        
        // 3. 配置 DispatcherServlet
        ServletRegistration.Dynamic dispatcher = 
            servletContext.addServlet("dispatcher", 
                new DispatcherServlet(dispatcherContext));
    }
}

@Configuration
class RootConfig {
    // 业务逻辑、数据访问等共享 Bean
    @Bean
    public DataSource dataSource() { ... }
    
    @Bean
    public JpaTransactionManager transactionManager() { ... }
}

@Configuration
@EnableWebMvc
class WebMvcConfig implements WebMvcConfigurer {
    // MVC 相关配置
    // 可以覆盖父上下文的一些定义
}

2. Spring Boot 的内置层次结构

JAVA
// Spring Boot 也有层次结构
@SpringBootTest
class SpringBootHierarchyTest {
    
    @Test
    void testSpringBootHierarchy() {
        SpringApplication app = new SpringApplication(MyApp.class);
        ConfigurableApplicationContext context = app.run();
        
        // Spring Boot 应用默认的层次结构
        System.out.println("应用上下文类型: " + context.getClass());
        
        // 检查是否有父上下文
        ApplicationContext parent = context.getParent();
        if (parent != null) {
            System.out.println("父上下文: " + parent.getClass());
        }
        
        context.close();
    }
}

Spring Cloud Feign 如何使用这个特性

JAVA
// Spring Cloud Feign 源码简化
public class FeignContext extends NamedContextFactory<FeignClientSpecification> {
    
    // FeignContext 继承自 NamedContextFactory
    // NamedContextFactory 管理多个子上下文
}

public abstract class NamedContextFactory<C extends NamedContextFactory.Specification>
        implements DisposableBean, ApplicationContextAware {
    
    // 存储子上下文的映射
    private Map<String, AnnotationConfigApplicationContext> contexts = new ConcurrentHashMap<>();
    
    // 为每个名称创建子上下文
    protected AnnotationConfigApplicationContext createContext(String name) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        
        // 设置父上下文
        if (this.applicationContext != null) {
            context.setParent(this.applicationContext);
        }
        
        // 注册配置
        context.register(DefaultConfigurations.class);
        context.register(getConfigurationsForName(name));
        
        context.refresh();
        contexts.put(name, context);
        return context;
    }
}

实际验证 Spring 原生层次结构

JAVA
@SpringBootTest
class SpringHierarchyProofTest {
    
    @Test
    void testSpringNativeHierarchy() {
        // 1. 证明 Spring 原生支持层次结构
        AnnotationConfigApplicationContext parent = new AnnotationConfigApplicationContext();
        parent.registerBean("parentBean", String.class, () -> "parent-value");
        parent.refresh();
        
        AnnotationConfigApplicationContext child = new AnnotationConfigApplicationContext();
        child.setParent(parent);
        child.registerBean("childBean", String.class, () -> "child-value");
        child.refresh();
        
        // 2. 验证 Bean 可见性
        // 子可以访问父
        String parentBeanFromChild = child.getBean("parentBean", String.class);
        System.out.println("子访问父: " + parentBeanFromChild);  // parent-value
        
        // 父不能访问子
        try {
            String childBeanFromParent = parent.getBean("childBean", String.class);
        } catch (NoSuchBeanDefinitionException e) {
            System.out.println("父不能访问子: " + e.getMessage());
        }
        
        // 3. 验证 Bean 覆盖
        AnnotationConfigApplicationContext child2 = new AnnotationConfigApplicationContext();
        child2.setParent(parent);
        child2.registerBean("parentBean", String.class, () -> "overridden-value");
        child2.refresh();
        
        // 子上下文覆盖父的 Bean
        String overridden = child2.getBean("parentBean", String.class);
        System.out.println("子覆盖父: " + overridden);  // overridden-value
        
        // 父的 Bean 不变
        String original = parent.getBean("parentBean", String.class);
        System.out.println("父的原始: " + original);  // parent-value
        
        child2.close();
        child.close();
        parent.close();
    }
}

Spring 层次结构的源码支持

JAVA
// Spring 框架核心接口
public interface ApplicationContext extends ... {
    
    /**
     * 返回父上下文,如果没有则返回 null
     */
    @Nullable
    ApplicationContext getParent();
    
    /**
     * 获取父上下文的 BeanFactory
     */
    @Nullable
    ConfigurableListableBeanFactory getParentBeanFactory();
}

// 在 AbstractApplicationContext 中的实现
public abstract class AbstractApplicationContext extends ... {
    
    @Nullable
    private ApplicationContext parent;
    
    // 在获取 Bean 时,会检查父上下文
    @Override
    public Object getBean(String name) throws BeansException {
        // 先在本上下文中查找
        // 如果找不到,并且有父上下文,则在父上下文中查找
        return doGetBean(name, null, null, false);
    }
}

层次结构的 Bean 查找规则

JAVA
@Component
class BeanLookupDemo {
    
    @Autowired
    private ApplicationContext context;
    
    public void demonstrateLookupRules() {
        // 创建层次结构
        ApplicationContext grandParent = createContext("grandParent");
        ApplicationContext parent = createContext("parent");
        parent.setParent(grandParent);
        ApplicationContext child = createContext("child");
        child.setParent(parent);
        
        // 1. 查找顺序:自底向上
        // 当在 child 中查找 Bean 时:
        // 1) 先在 child 中找
        // 2) 然后在 parent 中找  
        // 3) 最后在 grandParent 中找
        
        // 2. Bean 定义覆盖
        // 子上下文可以定义同名的 Bean,覆盖父上下文的定义
        // 查找时,优先使用子上下文中的定义
        
        // 3. 原型 Bean 的实例化
        // 每个上下文都有自己的原型 Bean 实例
    }
    
    private ApplicationContext createContext(String name) {
        GenericApplicationContext ctx = new GenericApplicationContext();
        ctx.registerBean(name + "Bean", String.class, () -> "value-from-" + name);
        ctx.refresh();
        return ctx;
    }
}

为什么 Spring 要支持层次结构?

1. 模块化和隔离

JAVA
// 不同模块可以有独立的配置
ApplicationContext moduleAContext = new AnnotationConfigApplicationContext();
moduleAContext.register(ModuleAConfig.class);

ApplicationContext moduleBContext = new AnnotationConfigApplicationContext();
moduleBContext.register(ModuleBConfig.class);

// 根上下文共享公共配置
ApplicationContext rootContext = new AnnotationConfigApplicationContext();
rootContext.register(SharedConfig.class);

moduleAContext.setParent(rootContext);
moduleBContext.setParent(rootContext);

2. 配置覆盖

JAVA
// 根上下文:默认配置
@Configuration
class DefaultConfig {
    @Bean
    public DataSource dataSource() {
        return new HikariDataSource();  // 默认数据源
    }
}

// 子上下文:覆盖配置
@Configuration
class OverrideConfig {
    @Bean
    public DataSource dataSource() {
        return new CustomDataSource();  // 自定义数据源
    }
}

3. 资源管理

JAVA
// 每个子上下文可以独立关闭
ApplicationContext child1 = createChildContext();
ApplicationContext child2 = createChildContext();

// 独立管理生命周期
child1.close();  // 只关闭 child1,不影响 child2

4. 测试隔离

JAVA
@SpringBootTest
class TestIsolation {
    
    // 每个测试可以有自己的子上下文
    // 避免测试之间的干扰
    @Test
    void test1() {
        // 使用独立的上下文配置
    }
    
    @Test  
    void test2() {
        // 另一个独立的上下文
    }
}

Spring Cloud 对层次结构的扩展

JAVA
// Spring Cloud 扩展了 Spring 的层次结构
public class NamedContextFactory<C extends NamedContextFactory.Specification>
        implements ApplicationContextAware, DisposableBean {
    
    // 管理多个命名上下文
    private Map<String, AnnotationConfigApplicationContext> contexts = new HashMap<>();
    
    // 为不同的客户端创建隔离的上下文
    public <T> T getInstance(String name, Class<T> type) {
        AnnotationConfigApplicationContext context = getContext(name);
        return context.getBean(type);
    }
    
    protected AnnotationConfigApplicationContext getContext(String name) {
        if (!this.contexts.containsKey(name)) {
            synchronized (this.contexts) {
                if (!this.contexts.containsKey(name)) {
                    this.contexts.put(name, createContext(name));
                }
            }
        }
        return this.contexts.get(name);
    }
}

验证 Feign 使用的确实是 Spring 原生特性

JAVA
@SpringBootTest
class FeignUsesSpringHierarchyTest {
    
    @Autowired
    private FeignContext feignContext;
    
    @Test
    void verifyFeignUsesSpringHierarchy() {
        // 获取主应用上下文
        ApplicationContext mainContext = ...;
        
        // 获取 FeignClient 的子上下文
        ApplicationContext feignChildContext = 
            feignContext.getInstance("myFeignClient", ApplicationContext.class);
        
        // 验证确实是 Spring 的 ApplicationContext
        System.out.println("子上下文类型: " + feignChildContext.getClass());
        // 输出: org.springframework.context.annotation.AnnotationConfigApplicationContext
        
        // 验证父子关系
        System.out.println("父上下文: " + feignChildContext.getParent());
        System.out.println("父上下文就是主上下文: " + 
            (feignChildContext.getParent() == mainContext));
        
        // 验证层次结构的方法
        System.out.println("是否有父上下文: " + 
            (feignChildContext.getParent() != null));
        
        // 验证 BeanFactory 层次结构
        ConfigurableListableBeanFactory beanFactory = 
            (ConfigurableListableBeanFactory) feignChildContext.getAutowireCapableBeanFactory();
        System.out.println("父 BeanFactory: " + beanFactory.getParentBeanFactory());
    }
}

总结

是的,子上下文是 Spring 框架的原生特性

  1. 原生支持:Spring 框架的 ApplicationContext 从一开始就支持层次结构
  2. 核心接口ApplicationContext.getParent() 是框架标准接口
  3. 广泛使用:Spring MVC、Spring Boot、Spring Cloud 都利用了这个特性
  4. Feign 实现:Spring Cloud Feign 的 FeignContext 是基于 NamedContextFactory,而后者使用标准的 Spring 上下文层次结构
  5. 设计目的:实现配置隔离、Bean 覆盖、模块化和独立生命周期管理

你的 313 个 FeignClient 问题本质是

  • Spring 原生支持上下文层次结构
  • Spring Cloud Feign 为每个客户端创建独立的子上下文
  • 每个子上下文是标准的 Spring ApplicationContext
  • 每个子上下文都会重新实例化配置类中的 @Bean 方法
  • 要解决内存浪费,需要将共享 Bean 定义在父上下文中

全部评论: 0

    我有话说: