Java 中的服务定位器模式:简化复杂系统中的服务访问
也称为
- 服务注册表
服务定位器设计模式的意图
Java 服务定位器模式提供了一种通过使用中央服务注册表来解耦 Java 客户端和服务的方法。
服务定位器模式的详细说明以及现实世界中的示例
现实世界的例子
在大型酒店中,礼宾台充当服务定位器。当客人需要服务,例如预订餐厅、寻找交通工具或安排城市观光时,他们不会直接自己寻找每项服务。相反,他们会去礼宾台,礼宾台会找到并安排所需的服務。这样,客人便与服务提供商解耦,可以依赖一个中心点来处理他们的请求,从而确保便利性和效率。
简单来说
服务定位器模式将定位服务的逻辑集中起来,从而将客户端与这些服务的具体实现解耦。
维基百科的说法
服务定位器模式是一种在软件开发中使用的一种设计模式,用于封装获取服务的流程,并提供强抽象层。这种模式使用一个称为“服务定位器”的中央注册表,它根据请求返回执行特定任务所需的信息。该模式的支持者认为,这种方法简化了基于组件的应用程序,其中所有依赖关系在整个应用程序设计之初都已清晰列出,从而使传统的依赖注入成为连接对象的一种更复杂的方式。该模式的批评者认为它是一种反模式,它会掩盖依赖关系,并使软件难以测试。
Java 中服务定位器模式的编程示例
服务定位器设计模式用于抽象获取服务的流程。它使用一个中央注册表,即“服务定位器”,它根据请求返回执行任务所需的信息。这种 Java 设计模式在需要集中管理服务的企业级 Java 应用程序中特别有用。
在此示例中,我们有一个 Service
接口和一个 ServiceLocator
类。Service
接口定义所有服务必须实现的方法。ServiceLocator
类负责检索和缓存这些服务。
public interface Service {
String getName();
int getId();
void execute();
}
Service
接口定义了三个方法:getName()
、getId()
和 execute()
。任何实现此接口的类都必须为这些方法提供实现。
public class App {
public static final String JNDI_SERVICE_A = "jndi/serviceA";
public static final String JNDI_SERVICE_B = "jndi/serviceB";
public static void main(String[] args) {
// Get the service from the ServiceLocator
var service = ServiceLocator.getService(JNDI_SERVICE_A);
// Execute the service
service.execute();
// Get another service from the ServiceLocator
service = ServiceLocator.getService(JNDI_SERVICE_B);
// Execute the service
service.execute();
// Get the service from the ServiceLocator again
service = ServiceLocator.getService(JNDI_SERVICE_A);
// Execute the service
service.execute();
// Get the service from the ServiceLocator again
service = ServiceLocator.getService(JNDI_SERVICE_A);
// Execute the service
service.execute();
}
}
在 App
类中,我们使用 ServiceLocator
通过名称获取服务,然后执行它们。ServiceLocator
处理查找和缓存服务的细节。这样,App
类便与服务的具体实现解耦。
以下是运行示例的输出
15:39:51.417 [main] INFO com.iluwatar.servicelocator.InitContext -- Looking up service A and creating new service for A
15:39:51.419 [main] INFO com.iluwatar.servicelocator.ServiceImpl -- Service jndi/serviceA is now executing with id 56
15:39:51.420 [main] INFO com.iluwatar.servicelocator.InitContext -- Looking up service B and creating new service for B
15:39:51.420 [main] INFO com.iluwatar.servicelocator.ServiceImpl -- Service jndi/serviceB is now executing with id 196
15:39:51.420 [main] INFO com.iluwatar.servicelocator.ServiceCache -- (cache call) Fetched service jndi/serviceA(56) from cache... !
15:39:51.420 [main] INFO com.iluwatar.servicelocator.ServiceImpl -- Service jndi/serviceA is now executing with id 56
15:39:51.420 [main] INFO com.iluwatar.servicelocator.ServiceCache -- (cache call) Fetched service jndi/serviceA(56) from cache... !
15:39:51.420 [main] INFO com.iluwatar.servicelocator.ServiceImpl -- Service jndi/serviceA is now executing with id 56
何时在 Java 中使用服务定位器模式
- 当您希望将服务创建与客户端类解耦以减少依赖关系并提高代码可维护性时使用。
- 适用于使用多个服务且需要集中管理依赖关系的大型企业应用程序。
- 适用于需要在多个客户端之间重用或共享服务实例的情况。
Java 中服务定位器模式的现实世界应用
- 企业级 Java 应用程序通常使用服务定位器来管理业务服务。
- Spring 框架使用类似的概念,其 BeanFactory 和 ApplicationContext 用于依赖注入。
- EJB(企业 JavaBean)使用服务定位器模式来查找和访问 EJB 组件。
服务定位器模式的优缺点
优点
- 解耦客户端类和服务类,减少代码依赖关系。
- 集中管理服务,使配置和管理服务更加容易。
- 促进服务实例的重用,可以提高性能和资源利用率。
权衡
- 如果服务定位器本身发生故障,可能会引入单点故障。
- 可能会增加代码库的复杂性,尤其是在配置和维护方面。
- 由于查找机制,可能会有潜在的性能开销。
相关的 Java 设计模式
- 工厂:两种模式都处理对象创建,但服务定位器侧重于定位服务,而工厂侧重于创建服务。
- 依赖注入:服务定位器的替代方案,它将依赖关系直接注入客户端,而不是让客户端从定位器请求依赖关系。
- 单例:服务定位器通常使用单例模式来确保定位器只有一个实例。