如何在Spring中用YML和Java Config实例化相互依赖的Bean列表?
解决方案:通过YML配置动态注册Bean并处理依赖注入
问题根源
你遇到的核心问题是Bean注册时机不匹配:用@PostConstruct注册Service Bean的时机晚于Capability Bean的初始化阶段,导致Capability无法找到对应的Service Bean。此外,直接用registerSingleton只是将POJO注册为Bean,但没有利用Spring的依赖注入生命周期来处理对象间的依赖。
修正实现步骤
下面是无需XML配置的简洁方案,通过@Configuration类统一管理Bean的创建与依赖注入:
1. 修正实体类拼写(可选但重要)
你代码中的Capabilities类名是复数,与配置里的capabilities列表项的语义不匹配,建议改为单数Capability,避免绑定歧义:
public class Capability { private String name; private String service; // 存储配置中的服务名称,而非直接存Service对象 private String inputString; private Service serviceBean; // 用于注入实际的Service Bean public String doIt(){ return serviceBean.callIt(inputString); } // 省略getter/setter }
2. 配置类:统一注册Service和Capability Bean
创建一个@Configuration类,利用Spring的上下文管理Bean的创建顺序,确保Service先被注册,再为Capability注入对应的Service:
import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.boot.context.properties.EnableConfigurationProperties; import java.util.List; import java.util.stream.Collectors; @Configuration @EnableConfigurationProperties(ConfigureComponents.class) public class ComponentBeanConfig { private final ConfigureComponents config; private final ApplicationContext context; // 构造注入配置类和上下文 public ComponentBeanConfig(ConfigureComponents config, ApplicationContext context) { this.config = config; this.context = context; } // 注册所有Service为Spring Bean @Bean public List<Service> serviceBeans() { List<Service> services = config.getServices(); services.forEach(service -> { // 初始化Bean(处理注解、生命周期回调) context.getAutowireCapableBeanFactory().initializeBean(service, service.getName()); // 注册为单例Bean,可通过名称获取 context.getAutowireCapableBeanFactory().registerSingleton(service.getName(), service); }); return services; } // 注册所有Capability Bean,并注入对应的Service @Bean public List<Capability> capabilityBeans() { return config.getCapabilities().stream() .map(capability -> { // 根据配置中的service名称获取已注册的Service Bean Service targetService = context.getBean(capability.getService(), Service.class); capability.setServiceBean(targetService); // 初始化Capability Bean context.getAutowireCapableBeanFactory().initializeBean(capability, capability.getName()); // 注册为单例Bean,可通过名称获取 context.getAutowireCapableBeanFactory().registerSingleton(capability.getName(), capability); return capability; }) .collect(Collectors.toList()); } }
3. 调整ConfigureComponents类
确保ConfigureComponents的字段类型与修正后的实体类匹配:
@ConfigurationProperties(prefix = "component") public class ConfigureComponents { private List<Service> services; private List<Capability> capabilities; // 省略getter/setter }
关键说明
- Bean创建顺序:Spring会自动处理
capabilityBeans()对serviceBeans()的依赖,确保所有Service Bean先被注册完成,再创建Capability并注入依赖。 - Bean初始化:使用
AutowireCapableBeanFactory.initializeBean()方法,确保Bean能被Spring完整初始化(比如处理@PostConstruct、@Autowired等注解)。 - 可通过名称获取Bean:调用
registerSingleton()后,你可以直接通过context.getBean("google", Service.class)或context.getBean("searchTehWebsForDragons", Capability.class)获取对应的Bean实例。
验证使用
你可以在任意Spring组件中注入List<Service>或List<Capability>,或者通过Bean名称直接获取:
@Service public class ComponentUsageService { @Autowired private List<Capability> capabilityBeans; public void runCapability(String capabilityName) { Capability capability = capabilityBeans.stream() .filter(c -> c.getName().equals(capabilityName)) .findFirst() .orElseThrow(() -> new IllegalArgumentException("Capability not found: " + capabilityName)); capability.doIt(); } }
内容的提问来源于stack exchange,提问作者darktrek
相关产品推荐
相关产品推荐

