🎉🎉《Spring Boot实战案例合集》目前已更新136个案例,我们将持续不断的更新。文末有电子书目录。
💪💪永久更新承诺
我们郑重承诺,所有订阅合集的粉丝都将享受永久免费的后续更新服务。
💌💌如何获取
订阅我们的合集《点我订阅》,并通过私信联系我们,我们将第一时间将电子书发送给您。
环境:SpringBoot3.4.2
1. 简介
相信在项目中都遇到过这样的需求,根据不同的传入类型调用同一个接口的不同实现类或服务处理逻辑。
例如,需要不同的解析器来处理不同的文件类型。例如,XML 文件由 XML 解析器处理,而 JSON 文件则由 JSON 解析器处理。
对于这样的场景,我们通常会在调用客户端中使用 if-else 语句。例如,如下代码示例:
public void processFile(String contentType, String filePath) {if ("json".equalsIgnoreCase(contentType)) {// ..} else if ("xml".equalsIgnoreCase(contentType)) {// ...} else if ("csv".equalsIgnoreCase(contentType)) {// ...} else {// ...}}
当然我详细你大概率会使用下面这篇文章的方法来处理这种if-else:
优雅重构Spring Boot代码,我用这6种策略消灭if else
本篇文章将介绍另外一种使用 服务定位器模式(Service Locator Pattern)的方法。其核心思想是面向接口编程,帮助我们消除紧密耦合的实现,并减轻客户端对具体实现类的依赖。
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;}public String toString() {return this.parserName;}public interface TypeConstants {String CSV_PARSER = "csvParser";String JSON_PARSER = "jsonParser";String XML_PARSER = "xmlParser";}}
2.2 定义解析器接口
针对不同的文件类型,我们只需要定义对应的接口实现即可。
public interface Parser {Map<String, Object> parse(Reader r);}
针对上面定义的3种文件类型,分别实现对应的Parser。
(TypeConstants.CSV_PARSER)public class CSVParser implements Parser {public Map<String, Object> parse(Reader r) {return Map.of("csv", "csv文件解析成功") ;}}(TypeConstants.JSON_PARSER)public class JSONParser implements Parser {public Map<String, Object> parse(Reader r) {return Map.of("json", "json文件解析成功") ;}}(TypeConstants.XML_PARSER)public class XMLParser implements Parser {public Map<String, Object> parse(Reader r) {return Map.of("xml", "xml文件解析成功") ;}}
注意,我们这里的beanName。我们接下来将直接通过beanName自动的查找对应解析器实现。
2.3 定义服务定位器接口
该接口中只有一个方法 getParser,该方法接受一个内容类型(contentType)作为参数,并返回 Parser 接口。
public interface ParserFactory {Parser getParser(ContentType contentType);}
我们将直接通过参数ContentType来获取对应的Parser具体实现。
2.4 配置ServiceLocatorFactoryBean
该类是我们的重点,我们就是通过它来定义具体的Parser实现。我们配置 ServiceLocatorFactoryBean 来使用 ParserFactory 作为服务定位器接口。ParserFactory 接口不需要具体的实现类。
public class ParserConfig {ServiceLocatorFactoryBean serviceLocatorFactoryBean() {ServiceLocatorFactoryBean factoryBean = new ServiceLocatorFactoryBean();factoryBean.setServiceLocatorInterface(ParserFactory.class);return factoryBean;}}
如上配置后,ServiceLocatorFactoryBean底层会生成ParserFactory的代理类,对应的 InvocationHandler 实现会根据当前调用的方法参数(第一个参数)来获取对应的beanName。
2.5 测试使用
接下来,在使用 Parser 时就无需关心去引入具体的实现了。通过上面的ServiceLocatorFactoryBean 可以直接根据类型获取具有相应功能的 Parser 接口。
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);}}
接下来,我们定义一个Runner进行测试
public class ParserRunner implements CommandLineRunner {private final ParserService parserService ;public ParserRunner(ParserService parserService) {this.parserService = parserService;}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) ;}}
启动服务后,控制台输出结果如下:
2.6 工作原理
如下图是ServiceLocator服务定位的工作原理:

总结:服务定位器模式消除了客户端对具体实现的依赖。以下是 Martin Fowler 文章中的一段话,它总结了该模式的核心思想:
“服务定位器的基本思想是拥有一个对象,该对象知道如何获取应用程序可能需要的所有服务。”
推荐文章
如何在Spring Boot中优雅地加载配置?这些方法你必须掌握!
Spring Boot中记录JDBC、JPA及MyBatis执行SQL及参数的正确姿势
非常实用!玩转 Spring Boot 接口参数类型转换,支持任意场景
优雅!Spring Boot 一个注解轻松实现敏感词检查,支持多场景
基于 Spring Boot 玩转JPA各种锁的应用(乐观锁,悲观锁)


