Java Spring:如何基于配置动态注入指定的多接口实现类?
问题描述
我有如下接口(注:仅为简化示例使用Object,实际不会这么做):
public interface IObjectGenerator { public List<Object> getObjectsFromThing(String thing); }
以及该接口的多个实现类:
@Service public class FromFileObjectGenerator implements IObjectGenerator { // 文件交互相关变量与方法 public List<Object> getObjectsFromThing(String thing){ // 使用文件相关逻辑生成并返回对象列表 } } @Service public class FromDatabaseObjectGenerator implements IObjectGenerator { // 数据库交互相关变量与方法 public List<Object> getObjectsFromThing(String thing) { // 使用数据库相关逻辑生成并返回对象列表 } } @Service public class FromWebObjectGenerator implements IObjectGenerator { // 网络交互相关变量与方法 public List<Object> getObjectsFromThing(String thing) { // 使用网络相关逻辑生成并返回对象列表 } }
我有时仅需使用基于文件的生成器,有时需要文件和数据库生成器,或数据库和网络生成器,甚至全部使用等组合场景。因此希望能够通过配置文件(如application.properties)设置要注入的实现类,支持从至少一个到全部实现类(包括未来新增的)的任意组合。
当前我的依赖注入代码如下:
public class GeneratorUser { private List<IObjectGenerator> generators; @Autowired public GeneratorUser(List<IObjectGenerator> generators){ this.generators = generators; } }
我知道可以通过一个管理类实例化所有生成器,注入该管理类后按需返回所需实例,但希望尽可能避免实例化未使用的生成器。请问是否有简便的实现方式?
解决方案
方法一:结合@ConditionalOnProperty实现按需实例化
给每个实现类添加条件注解,通过配置文件直接控制是否实例化该类,未配置的实现类不会被Spring初始化。
- 修改实现类:
@Service @ConditionalOnExpression("'${generator.enabled}'.contains('file')") public class FromFileObjectGenerator implements IObjectGenerator { // 原有代码 } @Service @ConditionalOnExpression("'${generator.enabled}'.contains('database')") public class FromDatabaseObjectGenerator implements IObjectGenerator { // 原有代码 } @Service @ConditionalOnExpression("'${generator.enabled}'.contains('web')") public class FromWebObjectGenerator implements IObjectGenerator { // 原有代码 }
- 在
application.properties中配置启用的生成器:
# 示例:启用文件和数据库生成器 generator.enabled=file,database
- 保持
GeneratorUser的注入逻辑不变,Spring只会注入符合条件的实例,未配置的实现类不会被实例化。
方法二:自定义注解+Bean过滤器(适合扩展新增实现类)
如果未来要频繁新增实现类,不想每个类都写重复的条件注解,可以自定义标记注解配合Bean处理器过滤实例:
- 自定义标记注解:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface GeneratorType { String value(); // 标记生成器类型,如"file"、"database" }
- 给实现类添加注解:
@Service @GeneratorType("file") public class FromFileObjectGenerator implements IObjectGenerator { // 原有代码 } @Service @GeneratorType("database") public class FromDatabaseObjectGenerator implements IObjectGenerator { // 原有代码 }
- 自定义Bean处理器过滤实例:
@Component public class GeneratorBeanFilter implements BeanPostProcessor { @Value("${generator.enabled}") private String enabledGenerators; @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof IObjectGenerator) { GeneratorType annotation = bean.getClass().getAnnotation(GeneratorType.class); if (annotation != null && !enabledGenerators.contains(annotation.value())) { return null; // 返回null会阻止该Bean初始化 } } return bean; } }
- 配置文件同样使用
generator.enabled=file,database,新增实现类只需添加@GeneratorType注解即可,无需修改过滤逻辑。
方法三:ObjectProvider延迟初始化(动态按需加载)
如果需要在运行时动态决定是否初始化实例,而非启动时确定,可以用ObjectProvider实现懒加载:
- 修改
GeneratorUser:
public class GeneratorUser { private final ObjectProvider<IObjectGenerator> generatorProvider; private final List<String> enabledTypes; @Autowired public GeneratorUser(ObjectProvider<IObjectGenerator> generatorProvider, @Value("${generator.enabled}") String enabledTypes) { this.generatorProvider = generatorProvider; this.enabledTypes = Arrays.asList(enabledTypes.split(",")); } public List<IObjectGenerator> getEnabledGenerators() { return generatorProvider.stream() .filter(this::isEnabledGenerator) .collect(Collectors.toList()); } private boolean isEnabledGenerator(IObjectGenerator generator) { GeneratorType annotation = generator.getClass().getAnnotation(GeneratorType.class); if (annotation != null) { return enabledTypes.contains(annotation.value()); } // fallback:通过类名提取类型 String type = generator.getClass().getSimpleName() .replace("ObjectGenerator", "") .toLowerCase(); return enabledTypes.contains(type); } }
这种方式下,未被调用到的实例不会被初始化,直到调用getEnabledGenerators时才会初始化符合条件的实例。
内容的提问来源于stack exchange,提问作者cma0014
相关产品推荐
相关产品推荐

