《服务定位器模式》|J2EE企业架构模式
关键词:Service Locator、服务定位器、缓存服务实例、查找服务、解耦服务查找
说明:服务定位器模式不属于GOF23设计模式,属于J2EE企业架构模式
一、什么是服务定位器模式
服务定位器模式(Service Locator Pattern):提供一个统一的服务查找中心,客户端通过服务名称/类型来获取所需服务实例,不需要直接new或者手动创建服务;服务定位器内部维护服务缓存,重复获取时直接返回缓存实例,提升性能。
用来封装服务查找、实例创建逻辑,隔离客户端与服务的具体实现。

核心意图
集中管理服务实例的查找与缓存;客户端只依赖服务抽象,不用关心服务的实例化、远程查找细节。
角色划分
- Client(客户端):需要使用服务的调用方,向ServiceLocator请求服务
- Service Locator(服务定位器):核心入口,负责查找服务、缓存服务实例
- Initial Context(初始上下文):模拟服务注册中心,获取原始服务实例
- Service(服务接口):定义业务服务的抽象方法
- Concrete Service(具体服务):服务接口实现类
数据流:
Client → ServiceLocator → 先查缓存,无缓存则访问InitialContext获取服务 → 返回Service
二、UML结构图
角色划分:
- Client:调用服务定位器获取业务服务
- ServiceLocator:持有缓存,调用InitialContext获取服务
- InitialContext:服务原始查找容器
- Service:服务抽象接口
- ConcreteService:服务实现

三、Java完整示例
模拟服务定位器,包含缓存逻辑,代码全部使用英文。
// Service Interface
public interface Service {
String getName();
void execute();
}
// Concrete Service
public class EmailService implements Service {
@Override
public String getName() {
return "EmailService";
}
@Override
public void execute() {
System.out.println("EmailService: Send email message");
}
}
public class SmsService implements Service {
@Override
public String getName() {
return "SmsService";
}
@Override
public void execute() {
System.out.println("SmsService: Send sms message");
}
}
// Initial Context
public class InitialContext {
public Service lookup(String serviceName) {
if ("EmailService".equalsIgnoreCase(serviceName)) {
return new EmailService();
} else if ("SmsService".equalsIgnoreCase(serviceName)) {
return new SmsService();
}
return null;
}
}
// Service Locator
import java.util.HashMap;
import java.util.Map;
public class ServiceLocator {
private static Map<String, Service> cache = new HashMap<>();
private InitialContext context;
public ServiceLocator() {
context = new InitialContext();
}
public Service getService(String serviceName) {
if(cache.containsKey(serviceName)){
System.out.println("Get service from cache: " + serviceName);
return cache.get(serviceName);
}
Service service = context.lookup(serviceName);
if(service != null){
cache.put(serviceName, service);
System.out.println("Create new service and put into cache: " + serviceName);
}
return service;
}
}
// Client
public class Client {
public static void main(String[] args) {
ServiceLocator locator = new ServiceLocator();
Service service1 = locator.getService("EmailService");
service1.execute();
Service service2 = locator.getService("EmailService");
service2.execute();
Service service3 = locator.getService("SmsService");
service3.execute();
Service service4 = locator.getService("SmsService");
service4.execute();
}
}
运行输出:
Create new service and put into cache: EmailService
EmailService: Send email message
Get service from cache: EmailService
EmailService: Send email message
Create new service and put into cache: SmsService
SmsService: Send sms message
Get service from cache: SmsService
SmsService: Send sms message
四、服务定位器模式 vs 依赖注入(DI) vs 业务代表模式
| 模式 | 定位 | 核心区别 |
|---|---|---|
| 服务定位器 | J2EE架构模式,主动查找 | 代码主动向定位器拉取服务;自带缓存,客户端控制获取时机 |
| 依赖注入DI | 容器自动注入 | 容器主动把服务注入对象,客户端不主动查找 |
| 业务代表模式 | J2EE架构模式 | 业务代表内部封装服务定位器,重点是隔离表现层和业务服务 |
业务代表模式底层经常组合使用服务定位器。
五、优缺点总结
✅ 优点
- 服务查找逻辑集中封装,客户端不用关心实例创建
- 内置缓存,多次获取同一个服务,减少实例创建开销
- 服务实现变更,客户端代码无需改动
- 支持动态切换服务实现
❌ 缺点
- 全局缓存,服务状态共享容易引发并发问题
- 隐藏依赖:代码很难直观看到类依赖了哪些服务,不利于静态分析
- 单元测试不方便,很难Mock被缓存的服务
- 缓存管理复杂,服务销毁、资源释放需要额外处理
六、框架应用场景
- 早期J2EE EJB查找(JNDI lookup)
- Spring早期BeanFactory有类似思想
现代开发更推荐依赖注入DI,服务定位器模式使用减少;但在插件系统、动态服务加载场景仍有使用。
七、小结
服务定位器模式核心:提供统一服务查找入口,内置缓存;客户端根据名称主动获取服务实例,屏蔽服务实例化与查找细节。
归属:J2EE架构模式,不属于GOF23;和DI是两种获取依赖的思路。
0 条笔记