如何确保仅生成选中的IpAssginer接口Bean的单个实例?
解决Spring中条件选择Bean时避免双实例的问题
这个问题我之前也碰到过!核心原因是你直接@Autowired了cloudIpAssginer和dhcpClientService两个实现类,Spring启动时会自动实例化这两个Bean,不管你最后返回哪个,所以导致双实例。下面给你几个靠谱的解决方案,都能实现只创建选中的那个实例:
方案一:使用@Conditional注解(推荐)
通过自定义条件判断类,让Spring只实例化符合当前系统环境的Bean,完美避免不必要的实例创建。
首先创建两个条件判断类,分别判断是否处于云环境:
public class CloudEnvironmentCondition implements Condition { @Autowired private ShellUtilsService shellUtilsService; @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { return shellUtilsService.getHwType().isInCloud(); } } public class DhcpEnvironmentCondition implements Condition { @Autowired private ShellUtilsService shellUtilsService; @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { return !shellUtilsService.getHwType().isInCloud(); } }
然后修改你的配置类,给两个实现类的Bean加上对应的@Conditional注解,最后通过@Primary确保注入的是唯一符合条件的实例:
@Configuration public class BeanConfiguration { @Autowired private ShellUtilsService shellUtilsService; @Bean @Conditional(CloudEnvironmentCondition.class) public IpAssginer cloudIpAssginer() { return new CloudIpAssginer(); } @Bean @Conditional(DhcpEnvironmentCondition.class) public IpAssginer dhcpClientService() { return new DHCPClientService(); } @Bean(name = "ipAssginer") @Primary public IpAssginer ipAssginer(IpAssginer ipAssginer) { Log.Management.info("{} IpAssginer created with type: {}", this.getClass().getSimpleName(), ipAssginer.getIPAssginerType()); return ipAssginer; } }
这个方案的优势是完全遵循Spring的条件Bean管理机制,即使其他地方需要注入IpAssginer,也会自动拿到符合条件的实例,扩展性很强。
方案二:手动实例化选中的Bean(适合简单场景)
如果你的CloudIpAssginer和DHCPClientService没有依赖其他Spring管理的Bean,可以直接在方法里手动实例化需要的那个,这样只会创建一个实例:
@Configuration public class BeanConfiguration { @Autowired private ShellUtilsService shellUtilsService; @Bean(name = "ipAssginer") public IpAssginer ipAssginer() { HwType hwType = shellUtilsService.getHwType(); IpAssginer ipAssginer = hwType.isInCloud() ? new CloudIpAssginer() : new DHCPClientService(); Log.Management.info("{} IpAssginer created with type: {}", this.getClass().getSimpleName(), ipAssginer.getIPAssginerType()); return ipAssginer; } }
注意:如果这两个实现类内部有@Autowired的依赖,手动new的话这些依赖不会被Spring自动注入,所以这个方案只适合无依赖的简单类。
方案三:在Bean方法中条件调用创建逻辑
把两个实现类的Bean定义为方法,然后在主方法中根据条件只调用其中一个的创建方法,这样未被调用的方法不会实例化对应的Bean:
@Configuration public class BeanConfiguration { @Autowired private ShellUtilsService shellUtilsService; @Bean(name = "ipAssginer") public IpAssginer ipAssginer() { HwType hwType = shellUtilsService.getHwType(); IpAssginer ipAssginer = hwType.isInCloud() ? cloudIpAssginer() : dhcpClientService(); Log.Management.info("{} IpAssginer created with type: {}", this.getClass().getSimpleName(), ipAssginer.getIPAssginerType()); return ipAssginer; } // 这里可以不用@Scope,但如果其他地方不会注入这两个Bean,单例也没问题 @Bean @Scope("prototype") public IpAssginer cloudIpAssginer() { return new CloudIpAssginer(); } @Bean @Scope("prototype") public IpAssginer dhcpClientService() { return new DHCPClientService(); } }
这个方案的前提是其他地方不会直接注入cloudIpAssginer或dhcpClientService,否则Spring还是会实例化它们。
内容的提问来源于stack exchange,提问作者Gal Sosin
相关产品推荐
相关产品推荐

