本文目录导读:

Java中实现“服务随需”(Service on Demand),通常有两种主要场景:
- 动态加载/卸载服务:服务在运行时按需启动,不需要时关闭(类似插件系统或微服务中的动态扩缩容)。
- 懒加载服务:服务第一次被请求时才初始化(类似Spring的
@Lazy或单例的延迟初始化)。
下面分别给出两种场景的经典实现案例。
动态注册与按需调用(SPI + 类加载器隔离)
适用于插件系统、热部署、按需启停服务,核心思路是:使用Java SPI机制动态发现服务实现类,并通过自定义类加载器实现服务的热加载/卸载。
定义服务接口
public interface ReportService {
void generateReport(String data);
default void destroy() { /* 清理资源 */ }
}
服务提供者实现(放在独立jar包或目录中)
// PluginA.jar 中的实现
public class PdfReportService implements ReportService {
@Override
public void generateReport(String data) {
System.out.println("生成PDF报表: " + data);
}
@Override
public void destroy() {
System.out.println("PDF报表服务已销毁");
}
}
编写SPI配置文件
在插件jar包的 META-INF/services/ 目录下,创建文件:
com.example.ReportService
com.example.impl.PdfReportService
服务管理器(按需加载/卸载)
public class ServiceManager {
private final Map<String, ReportService> activeServices = new ConcurrentHashMap<>();
private final Map<String, URLClassLoader> loaders = new ConcurrentHashMap<>();
// 按需加载服务
public ReportService loadService(String serviceName, URL jarUrl) throws Exception {
if (activeServices.containsKey(serviceName)) {
return activeServices.get(serviceName);
}
URLClassLoader classLoader = new URLClassLoader(
new URL[]{jarUrl},
Thread.currentThread().getContextClassLoader()
);
ServiceLoader<ReportService> loader = ServiceLoader.load(ReportService.class, classLoader);
for (ReportService service : loader) {
activeServices.put(serviceName, service);
loaders.put(serviceName, classLoader);
System.out.println("服务 " + serviceName + " 已加载");
return service;
}
throw new RuntimeException("未找到服务实现");
}
// 按需卸载服务
public void unloadService(String serviceName) {
ReportService service = activeServices.remove(serviceName);
if (service != null) {
service.destroy(); // 清理资源
}
URLClassLoader loader = loaders.remove(serviceName);
if (loader != null) {
try {
loader.close(); // 释放类加载器及其加载的类
} catch (IOException e) {
e.printStackTrace();
}
}
System.out.println("服务 " + serviceName + " 已卸载");
}
// 获取已激活的服务列表
public Set<String> getActiveServiceNames() {
return activeServices.keySet();
}
}
使用示例
public class OnDemandDemo {
public static void main(String[] args) throws Exception {
ServiceManager manager = new ServiceManager();
// 当用户需要生成PDF报表时,才加载服务
ReportService pdfService = manager.loadService("pdf",
new URL("file:///path/to/pluginA.jar"));
pdfService.generateReport("销售数据");
// 不需要时卸载
manager.unloadService("pdf");
// 再次需要时重新加载(可热更新)
pdfService = manager.loadService("pdf",
new URL("file:///path/to/updatedPluginA.jar"));
pdfService.generateReport("更新后的报表");
}
}
基于接口代理的懒加载(Spring @Lazy风格)
适用于单体应用内部,服务资源消耗大或启动慢,希望在第一次调用时才初始化。
定义一个懒加载代理工厂
public class LazyServiceProxy<T> implements InvocationHandler {
private final Class<T> serviceInterface;
private final Supplier<T> supplier; // 真正的创建逻辑
private volatile T realService;
public LazyServiceProxy(Class<T> serviceInterface, Supplier<T> supplier) {
this.serviceInterface = serviceInterface;
this.supplier = supplier;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (realService == null) {
synchronized (this) {
if (realService == null) {
realService = supplier.get(); // 首次调用时初始化
System.out.println("服务实例已创建: " + serviceInterface.getSimpleName());
}
}
}
return method.invoke(realService, args);
}
@SuppressWarnings("unchecked")
public static <T> T create(Class<T> serviceInterface, Supplier<T> supplier) {
return (T) Proxy.newProxyInstance(
serviceInterface.getClassLoader(),
new Class[]{serviceInterface},
new LazyServiceProxy<>(serviceInterface, supplier)
);
}
}
服务接口与实现
public interface HeavyComputeService {
int compute(int a, int b);
}
public class HeavyComputeServiceImpl implements HeavyComputeService {
public HeavyComputeServiceImpl() {
System.out.println("初始化重型计算服务...");
try { Thread.sleep(2000); } catch (InterruptedException e) { }
System.out.println("初始化完成");
}
@Override
public int compute(int a, int b) {
return a + b;
}
}
使用示例
public class LazyOnDemandDemo {
public static void main(String[] args) {
System.out.println("程序启动...");
// 创建代理,此时不创建真实服务
HeavyComputeService service = LazyServiceProxy.create(
HeavyComputeService.class,
HeavyComputeServiceImpl::new
);
System.out.println("代理已创建,但服务并未初始化");
// 第一次调用时,才真正创建服务
int result = service.compute(3, 5);
System.out.println("计算结果: " + result);
// 后续调用复用已创建的服务
int result2 = service.compute(10, 20);
System.out.println("第二次计算结果: " + result2);
}
}
输出:
程序启动...
代理已创建,但服务并未初始化
初始化重型计算服务...
初始化完成
计算结果: 8
第二次计算结果: 30
| 维度 | SPI动态加载(场景一) | 接口代理懒加载(场景二) |
|---|---|---|
| 使用场景 | 插件系统、热部署、微服务动态扩缩容 | 单体内资源密集型服务的延迟初始化 |
| 加载时机 | 显式调用loadService()时 |
第一次调用代理方法时自动触发 |
| 卸载能力 | 支持,可释放类加载器和资源 | 通常不支持显式卸载(生命周期由JVM管理) |
| 实现复杂度 | 较高(需处理类加载器隔离、SPI配置) | 较低(基于JDK动态代理) |
| 典型框架 | OSGi、Java SPI、自定义插件架构 | Spring的@Lazy、Guice的懒绑定 |
根据你的具体需求(是热插拔插件,还是单纯延迟初始化)选择合适的方案即可。