強(qiáng)!Spring Boot 通過(guò)服務(wù)定位干掉if-else
環(huán)境:SpringBoot3.4.2
1. 簡(jiǎn)介
相信在項(xiàng)目中都遇到過(guò)這樣的需求,根據(jù)不同的傳入類型調(diào)用同一個(gè)接口的不同實(shí)現(xiàn)類或服務(wù)處理邏輯。
例如,需要不同的解析器來(lái)處理不同的文件類型。例如,XML 文件由 XML 解析器處理,而 JSON 文件則由 JSON 解析器處理。
圖片
對(duì)于這樣的場(chǎng)景,我們通常會(huì)在調(diào)用客戶端中使用 if-else 語(yǔ)句。例如,如下代碼示例:
public void processFile(String contentType, String filePath) {
if ("json".equalsIgnoreCase(contentType)) {
// ..
} else if ("xml".equalsIgnoreCase(contentType)) {
// ...
} else if ("csv".equalsIgnoreCase(contentType)) {
// ...
} else {
// ...
}
}
本篇文章將介紹另外一種使用 服務(wù)定位器模式(Service Locator Pattern)的方法。其核心思想是面向接口編程,幫助我們消除緊密耦合的實(shí)現(xiàn),并減輕客戶端對(duì)具體實(shí)現(xiàn)類的依賴。
2.實(shí)戰(zhàn)案例
2.1 定義枚舉
在該枚舉類中,我們定義了將要處理的文件類型。
public enum ContentType {
JSON(TypeConstants.JSON_PARSER),
XML(TypeConstants.XML_PARSER),
CSV(TypeConstants.CSV_PARSER);
private final String parserName;
ContentType(String parserName) {
this.parserName = parserName;
}
@Override
public String toString() {
return this.parserName;
}
public interface TypeConstants {
String CSV_PARSER = "csvParser";
String JSON_PARSER = "jsonParser";
String XML_PARSER = "xmlParser";
}
}
2.2 定義解析器接口
針對(duì)不同的文件類型,我們只需要定義對(duì)應(yīng)的接口實(shí)現(xiàn)即可。
public interface Parser {
Map<String, Object> parse(Reader r);
}
針對(duì)上面定義的3種文件類型,分別實(shí)現(xiàn)對(duì)應(yīng)的Parser。
@Component(TypeConstants.CSV_PARSER)
public class CSVParser implements Parser {
@Override
public Map<String, Object> parse(Reader r) {
return Map.of("csv", "csv文件解析成功") ;
}
}
@Component(TypeConstants.JSON_PARSER)
public class JSONParser implements Parser {
@Override
public Map<String, Object> parse(Reader r) {
return Map.of("json", "json文件解析成功") ;
}
}
@Component(TypeConstants.XML_PARSER)
public class XMLParser implements Parser {
@Override
public Map<String, Object> parse(Reader r) {
return Map.of("xml", "xml文件解析成功") ;
}
}
注意,我們這里的beanName。我們接下來(lái)將直接通過(guò)beanName自動(dòng)的查找對(duì)應(yīng)解析器實(shí)現(xiàn)。
2.3 定義服務(wù)定位器接口
該接口中只有一個(gè)方法 getParser,該方法接受一個(gè)內(nèi)容類型(contentType)作為參數(shù),并返回 Parser 接口。
public interface ParserFactory {
Parser getParser(ContentType contentType);
}
我們將直接通過(guò)參數(shù)ContentType來(lái)獲取對(duì)應(yīng)的Parser具體實(shí)現(xiàn)。
2.4 配置ServiceLocatorFactoryBean
該類是我們的重點(diǎn),我們就是通過(guò)它來(lái)定義具體的Parser實(shí)現(xiàn)。我們配置 ServiceLocatorFactoryBean 來(lái)使用 ParserFactory 作為服務(wù)定位器接口。ParserFactory 接口不需要具體的實(shí)現(xiàn)類。
@Configuration
public class ParserConfig {
@Bean("parserFactory")
ServiceLocatorFactoryBean serviceLocatorFactoryBean() {
ServiceLocatorFactoryBean factoryBean = new ServiceLocatorFactoryBean();
factoryBean.setServiceLocatorInterface(ParserFactory.class);
return factoryBean;
}
}
如上配置后,ServiceLocatorFactoryBean底層會(huì)生成ParserFactory的代理類,對(duì)應(yīng)的 InvocationHandler 實(shí)現(xiàn)會(huì)根據(jù)當(dāng)前調(diào)用的方法參數(shù)(第一個(gè)參數(shù))來(lái)獲取對(duì)應(yīng)的beanName。
2.5 測(cè)試使用
接下來(lái),在使用 Parser 時(shí)就無(wú)需關(guān)心去引入具體的實(shí)現(xiàn)了。通過(guò)上面的ServiceLocatorFactoryBean 可以直接根據(jù)類型獲取具有相應(yīng)功能的 Parser 接口。
@Service
public class ParserService {
private final ParserFactory parserFactory;
public ParserService(ParserFactory parserFactory) {
this.parserFactory = parserFactory;
}
public Map<String, Object> getData(ContentType contentType) {
Parser parser = parserFactory.getParser(contentType) ;
InputStreamReader reader = null ;
return parser.parse(reader);
}
}
接下來(lái),我們定義一個(gè)Runner進(jìn)行測(cè)試
@Component
public class ParserRunner implements CommandLineRunner {
private final ParserService parserService ;
public ParserRunner(ParserService parserService) {
this.parserService = parserService;
}
@Override
public void run(String... args) throws Exception {
Map<String, Object> data = this.parserService.getData(ContentType.CSV) ;
System.err.println(data) ;
data = this.parserService.getData(ContentType.JSON) ;
System.err.println(data) ;
data = this.parserService.getData(ContentType.XML) ;
System.err.println(data) ;
}
}
啟動(dòng)服務(wù)后,控制臺(tái)輸出結(jié)果如下:
2.6 工作原理
如下圖是ServiceLocator服務(wù)定位的工作原理:
總結(jié):服務(wù)定位器模式消除了客戶端對(duì)具體實(shí)現(xiàn)的依賴。以下是 Martin Fowler 文章中的一段話,它總結(jié)了該模式的核心思想:
“服務(wù)定位器的基本思想是擁有一個(gè)對(duì)象,該對(duì)象知道如何獲取應(yīng)用程序可能需要的所有服務(wù)。”