【问题标题】:Decide which of multiple implementations to use in Spring Boot app决定在 Spring Boot 应用程序中使用多个实现中的哪一个
【发布时间】:2019-01-23 20:09:52
【问题描述】:

鉴于特定服务 API 的两个(或更多)实现,根据应用程序属性选择在运行时在我的应用程序中使用哪一个的最佳方法是什么?

示例 API:

public interface Greeting {
    String sayHello(String username);
}

实现:

public class FriendlyGreeting implements Greeting {
    public String sayHello(String username) {
        return "Hello, " + username;
    }
} 

public class HostileGreeting implements Greeting {
    public String sayHello(String username) {
        return "Go away, " + username;
    }
}

我有一个带有@Autowired 构造函数的单独服务类,它采用Greeting 的实例。我想要的是基于配置属性来决定注入和使用哪个问候实现。我想出了使用配置类来做出决定:

@Configuration
public class GreetingConfiguration {
    private String selection;

    @Autowired
    public GreetingConfiguration(@Value("${greeting.type}") String type) {
        this.selection = type;
    }

    @Bean
    public Greeting provideGreeting() {
        if ("friendly".equals(selection)) {
            return new FriendlyGreeting();
        } else {
            return new HostileGreeting();
        }
    }
}

这是做我想做的正确的方法吗?我在实现上使用@Qualifier,结果一团糟,Spring 看到了我的Greeting API 的 3 个实例,无论如何我都需要一个配置来选择要使用的实现并以唯一的方式返回它限定词名称,感觉比我确定的要糟糕。

【问题讨论】:

  • 它必须是配置属性还是可以是 Spring 配置文件?因为使用 Profile 会是一种很好且平静的 Springy 方式来做到这一点。之后,根据配置文件,您将提供恶意或友好配置来处理 bean。
  • 特别是:在你的组件上有合适的@Profile注解,并在运行时传递spring.profiles.active和/或spring.profiles.include属性...
  • 我会查看配置文件,但我认为它们是由我们将应用程序部署到的环境定义的,并且无法真正更改。
  • 使用@Conditionalstackoverflow.com/a/34351004/2958086也可能有帮助

标签: java spring


【解决方案1】:

您可以将 Greeting 标记为 @Service 并使用 @Qualifier("yourServiceHere") 选择所选择的,如下所示:

@Autowired
@Qualifier("friendlyGreeting")
private Greeting greeting;

另一种方法是使用配置文件。您可以使用 @Service 和 @Profile("friendly") 标记您的 FriendlyGreeting 服务,使用 @Service 和 @Profile("hostileGreeting") 标记 HostileGreeting 服务,然后在 application.properties 中添加以下内容:

spring.profiles.active=friendly

【讨论】:

  • 我怀疑,SpEL 是否适用于预选赛... (stackoverflow.com/a/29773117/592355)
  • 好主意,但是"${app.greeting}" 的评估没有发生,spring 去寻找一个字面限定符名称为${app.greeting} 的bean
  • 所以你可以把你的限定符作为一个字符串@Qualifier("friendlyGreeting")
  • 因此,如果不使用 Profile,您将无法管理在运行时使用哪个实现:Qualifier("friendlyGreeting") 是硬编码的;并且 Qualifier("${app.greetig}") 不起作用。这是正确的,配置文件是唯一的方法(除了问题中的初始解决方案)?
【解决方案2】:

回答我自己的问题。

@Compass@user268396 是正确的 - 使用 Profiles 可以按预期工作。

我创建了两个实现,用@Service 和@Profile("friendly") 或@Profile("hostile") 进行注释,并且可以将属性spring.profiles.active 更改为dev,friendly,例如,得到我想要的。

【讨论】:

    【解决方案3】:

    您可以使用https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/context/annotation/Conditional.htmlhttps://reflectoring.io/spring-boot-conditionals/ 中描述的@Conditional 注释

    上面提到的@Profile注解是基于@Conditional(来自Spring Framework);另见 Spring Boot:org.springframework.boot.autoconfigure.condition

    【讨论】:

      【解决方案4】:

      这是一个完整的解决方案,它使用上面 David 和 Vitor 提到的想法以及 @Profile 和 @Qualifer 注释。

      两个同名的bean,但根据定义的配置文件只激活一个。

      @Profile("profile1")
      @Bean("greeting")
      public class FriendlyGreeting implements Greeting {
      
      ---
      
      @Profile("profile2")
      @Bean("greeting")
      public class HostileGreeting implements Greeting {
      
      ---
      
      @Configuration
      public class GreetingConfiguration {
      
          private Greeting greeting;
      
          @Autowired
          public GreetingConfiguration(@Qualifier("greeting") Greeting greeting) {
              this.greeting = greeting;
          }
      
      }
      

      注意事项:

      • 您可以删除中间类 GreetingConfiguration 并将“问候”bean 粘贴到您需要的任何位置
      • 我更喜欢构造函数上的@Autowired 而不是类成员,以便更轻松地进行单元测试。

      【讨论】:

        猜你喜欢
        • 2018-05-31
        • 1970-01-01
        • 2015-11-25
        • 2012-08-22
        • 2017-01-19
        • 2014-11-10
        • 2015-12-30
        • 1970-01-01
        • 2019-06-09
        相关资源
        最近更新 更多