[英]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=french
或configuration=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.