簡體   English   中英

SpringBoot:可配置@Qualifier為select bean

[英]SpringBoot: Configurable @Qualifier to select bean

SpringBoot 的小問題,請問如何使用@Qualifier配置bean。

我有一段非常簡單的代碼:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.0.1</version>
        <relativePath/>
    </parent>

    <groupId>com.question</groupId>
    <artifactId>language</artifactId>
    <version>1.1</version>

    <name>language</name>
    <description>Spring Boot</description>

    <properties>
        <java.version>17</java.version>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

package com.question;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class LanguageApplication {

    public static void main(String[] args) {
        SpringApplication.run(LanguageApplication.class, args);
    }

}
package com.question.service;

public interface LanguageService {

    String process(String name);

}

package com.question.service;

import org.springframework.stereotype.Service;

@Service("french")
public class FrenchLanguageServiceImpl implements LanguageService {

    @Override
    public String process(String name) {
        return "Bonjour " + name;
    }

}

package com.question.service;

import org.springframework.stereotype.Service;

@Service("english")
public class EnglishLanguageServiceImpl implements LanguageService {

    @Override
    public String process(String name) {
        return "Welcome " + name;
    }

}

package com.question.controller;

import com.question.service.LanguageService;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;

@RestController
public class LanguageController {

    private final LanguageService languageService;

    @Value("${configuration}")
    public String configuration;

    public LanguageController(@Qualifier(configuration) LanguageService languageService) {
        this.languageService = languageService;
    }

    @GetMapping("/test")
    public String test(@RequestParam String name) {
        return languageService.process(name);
    }

}

預期的:

我希望實現的目標同樣簡單明了。 我想將某種配置傳遞給application.properties ,比如configuration=frenchconfiguration=english

在 controller 層,使用(@Qualifier(configuration) LanguageService languageService)並且將使用正確的具體服務。

實際的:

很遺憾,

@Qualifier(configuration) + @Value("${configuration}") public String configuration;

將 yield Attribute Value must be constant

請問有沒有辦法通過可配置的@Qualifier配置具體的 Bean?

我知道有一種方法可以通過使用 ApplicationContext getBean 來解決這個問題。

但是有這樣的構造:@Qualifier(configuration) 使代碼干凈且易於理解。 請問如何做到這一點?

謝謝

您不能使其動態化,您需要使用變通方法。 在這里,工廠模式會做得很好。 您將擁有兩個 bean,而不是一個 bean,並且根據該屬性的值,您可以讓您的工廠選擇使用哪一個。

@Component
public class LanguageFactory {

  @Autowired
  @Qualifier("english")
  private LanguageService englishLanguage;

  @Autowired
  @Qualifier("french")
  private LanguageService frenchLanguage;

  @Value("${configuration}")
  private String chosenLanguage;

  public LanguageService getLanguageService() {
    switch(chosenLanguage) {
      case "english": return englishLanguage;
      case "french": return frenchLanguage;
      // add any other language you need
      default: throw new Exception("Invalid language: " + chosenLanguage);
    }
   }
}

所以,在你的 LanguageController 中,你會得到這樣的東西:

  @Autowired
  private LanguageFactory languageFactory;

  @GetMapping("/test")
  public String test(@RequestParam String name) {
      return languageFactory.getLangaugeService().process(name);
  }

編輯:

另一種選擇是使用 Map。您可以避免所有 if-elses,但您仍然必須將它們添加到 Map。

@Component
public class LanguageFactory {

  private Map<String, LanguageService> languages = new HashMap<>();

  @Value("${configuration}")
  private String chosenLanguage;

  @Autowired
  public LanguageFactory(@Qualifier("english") LanguageService englishLanguage, @Qualifer("french") LanguageService frenchService) {
    this.languages.put("english", englishLanguage);
    this.languages.put("french", frenchLanguage);
  }

  public LanguageService getLanguageService() {
    LanguageService ls = this.languages.get(chosenLanguage);
    if ( ls == null ) {
      // handle the issue as you see fit
    }
    return ls;
  }
}

如果您一次只需要 1 個LanguageService bean 處於活動狀態,那么您可以對它們中的每一個使用@ConditionalOnProperty ,每個使用一個唯一的havingValue 像這樣(警告,從內存中編寫的未經測試的代碼):

interface ConfigKeys {
    public static final String LANGUAGE = "my.app.prefix.language";
}


@Service
@ConditionalOnProperty(ConfigKeys.LANGUAGE, havingValue = "english")
public class EnglishLanguageServiceImpl implements LanguageService {

    @Override
    public String process(String name) {
        return "Welcome " + name;
    }

}

@Service
@ConditionalOnProperty(ConfigKeys.LANGUAGE, havingValue = "french")
public class FrenchLanguageServiceImpl implements LanguageService {

    @Override
    public String process(String name) {
        return "Bonjour " + name;
    }

}

這樣,您不需要任何限定符,只需將配置中的my.app.prefix.language屬性(application.properties 或 application.yaml)設置為您想要的值,上下文中將只有一個LanguageService bean . 您可以在任何需要的地方注入該 bean,而無需限定符。

一個更簡單的選擇是將相關的 beans 分組在@Configuration類中,然后使用@Conditional來啟用(或不啟用)配置,然后是一組 beans。 這樣,您就不會像您所說的那樣處理單個 bean。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM