动态注入spring bean

新手上路,请多包涵

在 java-spring 网络应用程序中,我希望能够动态注入 bean。例如,我有一个具有 2 种不同实现的接口:

在此处输入图像描述

在我的应用程序中,我使用一些属性文件来配置注入:

 #Determines the interface type the app uses. Possible values: implA, implB
myinterface.type=implA

我的注入实际上是根据属性文件中的属性值有条件地加载的。例如,在这种情况下,myinterface.type=implA 在我注入 MyInterface 的任何地方,将注入的实现将是 ImplA(我通过扩展 Conditional annotation 实现了这一点)。

我希望在运行时 - 一旦更改属性,将发生以下情况(无需重新启动服务器):

  1. 将注入正确的实现。例如,当设置 myinterface.type=implB ImplB 将被注入到任何使用 MyInterface 的地方
  2. 应使用新值刷新 Spring 环境 并重新注入 bean。

我想刷新我的上下文,但这会产生问题。我想也许可以使用 setters 进行注入,并在重新配置属性后重新使用这些 setters。是否有针对此类要求的工作实践?

有任何想法吗?

更新

正如一些人所建议的那样,我可以使用一个工厂/注册表来保存这两种实现(ImplA 和 ImplB)并通过查询相关属性返回正确的实现。如果我这样做,我还有第二个挑战——环境。例如,如果我的注册表如下所示:

 @Service
public class MyRegistry {

private String configurationValue;
private final MyInterface implA;
private final MyInterface implB;

@Inject
public MyRegistry(Environmant env, MyInterface implA, MyInterface ImplB) {
        this.implA = implA;
        this.implB = implB;
        this.configurationValue = env.getProperty("myinterface.type");
}

public MyInterface getMyInterface() {
        switch(configurationValue) {
        case "implA":
                return implA;
        case "implB":
                return implB;
        }
}
}

一旦属性发生变化,我应该重新注入我的环境。有什么建议吗?

我知道我可以在方法中查询 env 而不是构造函数,但这是一种性能降低,而且我想考虑一个用于重新注入环境的 ider(同样,也许使用 setter 注入?)。

原文由 forhas 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 542
1 个回答

我会让这个任务尽可能简单。与其在启动时有条件地加载 MyInterface 接口的一个实现,然后触发触发动态加载同一接口的另一个实现的事件,我将以不同的方式解决这个问题,这更简单实施和维护。

首先,我只加载所有可能的实现:

 @Component
public class MyInterfaceImplementationsHolder {

    @Autowired
    private Map<String, MyInterface> implementations;

    public MyInterface get(String impl) {
        return this.implementations.get(impl);
    }
}

这个 bean 只是 MyInterface 接口的所有实现的持有者。这里没有什么神奇的,只是常见的 Spring 自动装配行为。

现在,无论您需要注入 MyInterface 的特定实现,您都可以在接口的帮助下完成:

 public interface MyInterfaceReloader {

    void changeImplementation(MyInterface impl);
}

然后,对于每个需要 通知 实现更改的类,只需使其实现 MyInterfaceReloader 接口即可。例如:

 @Component
public class SomeBean implements MyInterfaceReloader {

    // Do not autowire
    private MyInterface myInterface;

    @Override
    public void changeImplementation(MyInterface impl) {
        this.myInterface = impl;
    }
}

最后,您需要一个 bean,它实际上更改了每个具有 MyInterface 作为属性的 bean 中的实现:

 @Component
public class MyInterfaceImplementationUpdater {

    @Autowired
    private Map<String, MyInterfaceReloader> reloaders;

    @Autowired
    private MyInterfaceImplementationsHolder holder;

    public void updateImplementations(String implBeanName) {
        this.reloaders.forEach((k, v) ->
            v.changeImplementation(this.holder.get(implBeanName)));
    }
}

这简单地自动装配所有实现 MyInterfaceReloader 接口的 beans 并使用新实现更新它们中的每一个,新实现从 holder 中检索并作为参数传递。同样,常见的 Spring 自动装配规则。

每当您想要更改实现时,您应该只调用 updateImplementations 方法,并使用新实现的 bean 的名称,这是类的小驼峰简单名称,即 myImplAmyImplBMyImplAMyImplB

您还应该在启动时调用此方法,以便在每个实现 MyInterfaceReloader 接口的 bean 上设置初始实现。

原文由 fps 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题