Spring:如何在运行时更改接口实现

2023-11-24

作为一名 Java 开发人员,我经常需要在接口的不同实现之间进行选择。有时这种选择是可以做到的once,而有时我需要不同的实现来响应我的程序收到的不同输入。换句话说,我需要能够change运行时执行。这可以通过帮助程序对象轻松实现,该对象将某些键(基于用户输入)转换为对合适接口实现的引用。

使用 Spring,我可以设计一个像 bean 这样的对象,并将其注入到任何我需要的地方:

public class MyClass {

    @Autowired
    private MyHelper helper;

    public void someMethod(String someKey) {
        AnInterface i = helper.giveMeTheRightImplementation(someKey);
        i.doYourjob();
    }

}

现在,我应该如何实现这个助手?让我们从这个开始:

@Service
public class MyHelper {

    public AnInterface giveMeTheRightImplementation(String key) {
        if (key.equals("foo")) return new Foo();
        else if (key.equals("bar")) return new Bar();
        else ...
    }

}

这种解决方案有几个缺陷。最糟糕的情况之一是容器不知道从帮助器返回的实例,因此无法从依赖项注入中受益。换句话说,即使我定义Foo像这样的类:

@Service
public class Foo {

    @Autowired
    private VeryCoolService coolService;

    ...

}

...实例Foo由返回MyHelper不会有coolService字段已正确初始化。

为了避免这种情况,经常建议的解决方法是注入每个可能的实现助手内部:

@Service
public class MyHelper {

    @Autowired
    private Foo foo;

    @Autowired
    private Bar bar;

    ...

    public AnInterface giveMeTheRightImplementation(String key) {
        if (key.equals("foo")) return foo;
        else if (key.equals("bar")) return bar;
        else ...
    }

}

但我不太喜欢这样的解决方案。我发现更优雅和可维护的东西是这样的:

@Service
public class MyHelper {

    @Autowired
    private ApplicationContext app;

    public AnInterface giveMeTheRightImplementation(String key) {
        return (AnInterface) app.getBean(key);
    }

}

这是基于 Spring 的应用上下文.

类似的解决方案是使用服务定位器工厂Bean class:

public interface MyHelper {

    AnInterface giveMeTheRightImplementation(String key);

}

// Somewhere else, in Java config

@Bean
ServiceLocatorFactoryBean myHelper() {
    ServiceLocatorFactoryBean bean = new ServiceLocatorFactoryBean();
    bean.setServiceLocatorInterface(MyHelper.class);
    return bean;
}

但由于我不是 Spring 专家,我想知道是否有更好的方法。


我在我的项目中遵循这种方法。这并不是万无一失的,但它在用很少的配置代码添加新的实现方面效果很好。

我用这样的东西创建一个枚举

enum Mapper{
    KEY1("key1", "foo"),
    KEY2("key2", "bar")
    ;

    private String key;
    private String beanName;

    public static getBeanNameForKey(String key){
       // traverse through enums using values() and return beanName for key
    }
}

我们假设 Foo 和 Bar 都通过一个公共接口实现。我们称之为接口

class ImplFactory{

    @Autowired
    Map<String, AnInterface> implMap; // This will autowire all the implementations of AnInterface with the bean name as the key

    public AnInterface getImpl(string beanName){
            implMap.get(beanName);
    }
  }

你的助手类看起来像这样

@Service
public class MyHelper {

@Autowired
ImplFactory factory;

    public AnInterface giveMeTheRightImplementation(String key) {

        String beanName = Mapper.getBeanNameForKey(key);  
        factory.getImpl(beanName);
    }  
}

这种方法的一些优点是,
1. 在选择正确的实现时避免了冗长的 if else 或 switch 情况。
2. 如果您想添加新的实现。您所要做的就是在枚举中添加一个 Mapper(除了添加新的 Impl 类之外)。
3. 您甚至可以为您想要的 impl 类配置 Bean 名称(如果您不想要 spring 给出的默认 bean 名称)。该名称将成为工厂类中地图的键。您必须在枚举中使用它。

编辑: 如果您希望为您的 bean 指定一个自定义名称,您可以使用构造型注释之一的 value 属性。例如。如果您已将 Impl 注释为 @Component 或 @Service,则执行以下操作@Component("myBeanName1") or @Service("myBeanName2")

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Spring:如何在运行时更改接口实现 的相关文章

随机推荐

  • ValueError:找到具有 0 个样本的数组(形状= (0, 1),而 MinMaxScaler 要求最小值为 1

    我是机器学习的初学者 我正在帮助我的数学专业朋友基于 TensorFlow 创建一个股票预测器 csv他提供的文件 我有一些问题 第一个是他的 csv文件 该文件只有日期和结束值 它们没有分开 因此我必须手动分隔日期和值 我已经成功做到了这
  • ArrayList 的不安全或未经检查的操作

    我被指派编写一个程序 获取 100 个 0 25 之间的随机整数并将它们存储在一个数组中 然后我必须调用两种方法来分割偶数和赔率 非常典型 所以我尝试了 ArrayList 的东西 我刚刚学会它 它看起来很好 我正在关注教程和在线的东西 直
  • hibernate oracle 标识符太长 ORA-00972

    我被这个问题困扰了 数据库架构是由其他人提供的 因此我不能简单地更改名称 我尝试在各处添加正确的注释 也许我遗漏了一些东西 明显的 这是我的完整映射 相当多的类 我将提交 getter setter 问题是当休眠试图获取所有List
  • Android Service.startForeground 不尊重通知 ID 的唯一性

    Service startForeground vs 通知管理器 notify 给出不同的行为 使用时notify使用两个不同的通知 ID 创建 2 个通知 Good 当做同样的事情时启动前台 一个通知会覆盖另一个通知 Bad 测试设备 N
  • 用于纯 Firebase JavaScript API 的 orderby 对象过滤器

    I found angularFire当与纯 Firebase JavaScript API 结合使用时 总是让我感到困惑 假设我不知道如何调用 Firebase datasapshot APIss name ss hasChild ss
  • 在python中获取函数调用者的信息

    我想获取有关 python 中特定函数的调用者的信息 例如 class SomeClass def init self x self x x def caller self return special func self x def sp
  • 内存泄漏单元测试C++

    我刚刚解决了应用程序中的内存泄漏问题 现在我想编写一个单元测试以确保这种情况不会再次发生 我正在寻找一种方法来检测当前应用程序 工作集 在某些功能之前和之后的内存使用情况 例如 long mem used GetMemUsed Do som
  • Java ObjectInputStream 挂起

    我现在感觉真的很愚蠢 伙计们 基本上我是通过本地计算机上的 TCP 连接的 当我尝试在客户端创建输入 输出流时 它不会通过创建对象输入流 是什么赋予了 这在打印 2 后停止 没有异常或任何东西 这不是我第一次使用这个类 这部分是我感到困惑的
  • XSLT 将同名兄弟节点的值合并/连接到单个节点

    输入XML
  • 实体管理器是否创建与数据库的连接?

    在我的项目中 我忘记关闭每个操作的实体管理器 一段时间后 由于与 mysql 服务器的连接过多 我遇到了异常 这是否意味着每个实体管理器都建立连接 当我们忘记关闭连接时会发生什么 我只使用了一个实体管理器工厂 假设您正在使用应用程序管理的实
  • 根据“grid_location”方法,按钮有自己的坐标系吗?

    我正在尝试使用grid location方法 从网格几何管理器 在 Tkinter 中 但似乎我做错了什么 这是我的代码 from tkinter import root Tk b Button root text 00 b grid ro
  • 如何确保我的 git 存储库代码是安全的?

    如果我们的组织要从像 subversion 这样的中央服务器 VCS 切换到像 git 这样的分布式 VCS 我如何确保我的所有代码都不会出现硬件故障 使用中央服务器 VCS 我只需要每天备份存储库 如果我们使用 DVCS 那么所有开发人员
  • iOS 7 自定义后退按钮

    我想使用自定义后退按钮 在 iOS 6 中 一切都很完美 但是iOS 7很奇怪 UIBarButtonItem appearance setBackButtonBackgroundImage UIImage imageNamed back
  • org.openqa.selenium.InvalidCookieDomainException:使用 Selenium 和 WebDriver 文档不支持 cookie

    我正在尝试将 cookie 推送到从上一个会话存储的 selenium firefox webdriver 但出现错误 org openqa selenium InvalidCookieDomainException Document is
  • UIPanGestureRecognizer 起点已关闭

    我有一个 UIView 它附加了一个 UIPanGestureRecognizer 手势工作正常 只是起点不是平移第一次开始的位置 它通常在 x 和 y 坐标中偏离 5 到 15 个像素 不幸的是 方差不是一致并且似乎与平移运动发生的速度有
  • 使用 php 向后读取文件行(fgets)

    我有一个txt文件 我想向后阅读 目前我正在使用这个 fh fopen myfile txt r while line fgets fh echo line br 这将输出我文件中的所有行 我想从下到上阅读这些行 有办法做到吗 第一种方式
  • C# 服务:如何获取用户配置文件文件夹路径

    我需要从 C windows 服务中获取用户目录 比如 C Users myusername 理想情况下 我希望有漫游路径 就像 C Users myusername AppData Roaming 当我在控制台程序中使用以下内容时 我得到
  • 匹配结束 HTML 标签的正则表达式

    我正在编写一个小型 Python 脚本来清理 HTML 文档 它的工作原理是接受要保留的标签列表 然后解析 HTML 代码 丢弃不在列表中的标签我一直在使用正则表达式来做到这一点 并且我已经能够匹配开始标签和自关闭标签但不关闭标签 我一直在
  • 如何自动生成日期属性为 Date 而不是 NSDate 的 NSManagedObject 子类?

    我目前正在将我的项目更新到 Swift 3 并将所有 NSDate 方法和扩展移至 Date 以便在应用程序中保持标准 问题是我使用 Xcode 自动生成 NSManagedObject 子类 并且它生成日期属性为 NSDate 而不是 D
  • Spring:如何在运行时更改接口实现

    作为一名 Java 开发人员 我经常需要在接口的不同实现之间进行选择 有时这种选择是可以做到的once 而有时我需要不同的实现来响应我的程序收到的不同输入 换句话说 我需要能够change运行时执行 这可以通过帮助程序对象轻松实现 该对象将