如何确保Guice注入的MyRepository在所有订阅者添加后才被使用?
可行解决方案
方案1:通过Guice Provider手动控制初始化顺序
先实现线程安全的MyRepository:
public class MyRepositoryImpl implements MyRepository { private final List<MyInterface> instances = new CopyOnWriteArrayList<>(); @Override public void addInstance(MyInterface instance) { instances.add(instance); } @Override public List<MyInterface> getInstances() { return Collections.unmodifiableList(instances); } }
在Guice模块中,先把MyRepository绑定为单例,然后在服务的Provider方法里先手动初始化所有MyInterface实现类(触发它们构造方法里的注册逻辑),再创建服务:
public class AppModule extends AbstractModule { @Override protected void configure() { bind(MyRepository.class).to(MyRepositoryImpl.class).in(Singleton.class); } @Provides @Singleton public MyService provideMyService(MyRepository repository) { // 先初始化所有订阅者,确保它们都注册到仓库 new ObjectA(repository); new ObjectB(repository); // 这里添加所有需要的MyInterface实现类 // 所有实例注册完成后,返回服务实例 return new MyService(repository); } }
这种方式能保证服务被注入时,仓库已经收集了所有订阅者,因为服务的创建依赖于所有实现类的初始化完成。
方案2:启动阶段提前初始化所有订阅者
将MyRepository绑定为单例后,在应用启动入口先获取所有MyInterface实现类的实例(触发构造方法的注册逻辑),再获取服务:
public class AppBootstrap { public static void main(String[] args) { Injector injector = Guice.createInjector(Stage.PRODUCTION, new AppModule()); // 提前初始化所有订阅者,触发它们向仓库注册 injector.getInstance(ObjectA.class); injector.getInstance(ObjectB.class); // 其他MyInterface实现类依次处理 // 仓库完成所有注册后,安全获取服务使用 MyService service = injector.getInstance(MyService.class); service.runBusinessLogic(); } }
注意使用Stage.PRODUCTION模式,Guice会保证单例的线程安全初始化,不会重复创建实例。
方案3:给仓库添加初始化完成钩子
扩展MyRepository,添加初始化完成的标记和等待逻辑,确保服务只能在初始化完成后访问仓库:
public class MyRepositoryImpl implements MyRepository { private final List<MyInterface> instances = new CopyOnWriteArrayList<>(); private final CountDownLatch initLatch = new CountDownLatch(1); private boolean isInitialized = false; @Override public void addInstance(MyInterface instance) { if (isInitialized) { throw new IllegalStateException("仓库已完成初始化,无法再添加实例"); } instances.add(instance); } @Override public List<MyInterface> getInstances() { try { initLatch.await(); // 阻塞直到初始化完成 } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new RuntimeException("等待仓库初始化被中断", e); } return Collections.unmodifiableList(instances); } // 对外暴露的初始化完成标记方法 public void finishInitialization() { isInitialized = true; initLatch.countDown(); } }
在启动流程中,先初始化所有订阅者,再标记仓库完成初始化,最后启动服务:
public class AppBootstrap { public static void main(String[] args) { Injector injector = Guice.createInjector(new AppModule()); MyRepository repository = injector.getInstance(MyRepository.class); // 初始化所有订阅者 injector.getInstance(ObjectA.class); injector.getInstance(ObjectB.class); // 标记仓库初始化完成 ((MyRepositoryImpl) repository).finishInitialization(); // 服务现在可以安全使用仓库 MyService service = injector.getInstance(MyService.class); service.processAllInstances(); } }
这个方案适合无法提前枚举所有MyInterface实现类的场景,只要在服务启动前调用finishInitialization即可。
内容的提问来源于stack exchange,提问作者user6342701
相关产品推荐
相关产品推荐

