如何使用Java ServiceLoader加载单例类实例
解决方案:ServiceLoader结合单例的实现方案
首先明确:Java ServiceLoader无法直接加载带有私有构造方法的类,因为它的底层实现依赖公共无参构造方法来实例化服务提供者(早期用Class.newInstance(),后来改用Constructor.newInstance(),都要求构造方法可访问)。所以要使用ServiceLoader,必须让实现类具备公共无参构造,下面提供两种安全的单例实现方式,同时兼容ServiceLoader:
方式一:带构造校验的线程安全单例
在公共无参构造中加入校验逻辑,防止重复实例化,确保单例的安全性:
public class MySingletonImpl implements MyInterface { // 懒汉式单例(线程安全) private static volatile MySingletonImpl instance; // 公共无参构造,供ServiceLoader调用 public MySingletonImpl() { synchronized (MySingletonImpl.class) { if (instance != null) { throw new IllegalStateException("该类为单例,禁止重复实例化"); } instance = this; } } // 保留原有的单例获取方法 public static MySingletonImpl getInstance() { if (instance == null) { synchronized (MySingletonImpl.class) { if (instance == null) { // 实际不会执行,因为ServiceLoader已完成初始化 new MySingletonImpl(); } } } return instance; } // 实现接口方法 @Override public void doBusiness() { // 业务逻辑实现 } }
核心逻辑:
- ServiceLoader首次加载时调用公共构造,此时
instance为null,完成单例初始化 - 后续任何尝试实例化(包括反射调用构造)都会触发异常,阻止多实例创建
- 双重校验锁保证并发场景下的线程安全
方式二:枚举单例+代理类
利用枚举天然的JVM级单例特性,通过代理类兼容ServiceLoader的要求:
1. 枚举实现核心单例逻辑
public enum MyCoreSingleton implements MyInterface { INSTANCE; @Override public void doBusiness() { // 核心业务逻辑 } }
2. 代理类供ServiceLoader加载
public class SingletonProxy implements MyInterface { // 内部持有枚举单例实例 private final MyInterface delegate = MyCoreSingleton.INSTANCE; // 公共无参构造 public SingletonProxy() {} // 代理所有接口方法 @Override public void doBusiness() { delegate.doBusiness(); } }
核心逻辑:
- 枚举单例由JVM保证绝对安全,无法被反射破坏
- ServiceLoader加载的代理类实例,所有业务操作都会委托给同一个枚举单例
- 终端用户只需将代理类配置到ServiceLoader服务文件中,隐藏核心单例细节
ServiceLoader的配置与使用
- 在项目的
META-INF/services目录下,创建以接口全类名为文件名的文件(比如com.example.MyInterface) - 文件中写入实现类的全类名(比如
com.example.MySingletonImpl或com.example.SingletonProxy) - 代码中加载服务:
ServiceLoader<MyInterface> serviceLoader = ServiceLoader.load(MyInterface.class); // 遍历获取服务实例(单例只会初始化一次) for (MyInterface service : serviceLoader) { service.doBusiness(); }
为什么比原反射方式性能好?
- ServiceLoader会缓存已加载的服务实例,后续获取无需重复实例化
- 避免了反射中
Class.forName()、getDeclaredMethod()、invoke()等耗时操作 - 懒加载机制只会在首次使用时加载服务,减少启动开销
内容的提问来源于stack exchange,提问作者booyaakaashaa
相关产品推荐
相关产品推荐

