【问题标题】:Is there a more convinient to provide (bind to guice module) already created instances?提供(绑定到 guice 模块)已经创建的实例是否更方便?
【发布时间】:2021-01-05 19:24:41
【问题描述】:

我在桌面应用程序中使用 Guice,我想为服务添加通用绑定。这些服务是单例(绑定)实例,并在应用程序启动期间手动创建。我只使用 IoC 容器来创建 GUI。我手动创建这些服务,因为在启动期间我想发布进度。

由于 GUI 使用这些服务,它们必须绑定到 guice GUI 模块。

如果不对每个类使用带有 setter 和 getter 的类,我想不出一种方法来绑定它们。

假设我有 CarService 和 EngineService。我现在拥有的是:

public class GuiceServices {

    public static void main(String[] args) {
        ServicesModule servicesModule = new ServicesModule();

        CarService carService = new CarServiceImpl();
        servicesModule.setCarService(carService);
        System.out.println("progress: 50%");

        EngineService engineService = new EngineServiceImpl();
        servicesModule.setEngineService(engineService);
        System.out.println("Progress: 100%");

        Injector i = Guice.createInjector(new GuiModule(), servicesModule);
        i.getInstance(MainView.class).show();
    }

    class ServicesModule extends AbstractModule {
        private CarService carService;
        private EngineService engineService;
        @Override
        protected void configure() {
            
        }
        
        public void setCarService(CarService carService) {
            this.carService = carService;
        }
        
        public void setEngineService(EngineService engineService) {
            this.engineService = engineService;
        }
        
        @Provides
        public CarService getCarService() {
            return carService;
        }
        
        @Provides
        public EngineService getEngineService() {
            return engineService;
        }
    }
}

但由于这些服务很多,这有点痛苦。

有没有办法避免这种情况?

理想情况下,地图更方便。比如:

public class GuiceServices {

    public static void main(String[] args) {
        ServicesMap servicesMap = new ServicesMap();

        CarService carService = new CarServiceImpl();
        servicesMap.put(CarService.class, carService);
        System.out.println("progress: 50%");

        EngineService engineService = new EngineServiceImpl();
        servicesMap.put(EngineService.class, engineService);
        System.out.println("Progress: 100%");

        Injector i = Guice.createInjector(new GuiModule(), new ServicesModule(servicesMap));
        i.getInstance(MainView.class).show();
    }

    class ServicesModule extends AbstractModule {
        private ServicesMap services;
        public SerrvicesModule(ServicesMap services) {
            this.services = services;
        }
        @Override
        protected void configure() {
            for (Class<?> serviceType : services.keySet())
            {
                bind(serviceType).toInstance(services.get(serviceType));
            }
        }       
    }
}

但我找不到创建-实现这个“servicesMap”的方法。因为 bind 方法返回一个通用的构建器。 Guice(或 Guava)是否为这种情况提供了一些东西?

我知道我可以使用 Guice 创建服务并使用注入/类型侦听器发布进度,但是包含所有服务的业务包(模块)没有 javax.inject 依赖项。另外,这些服务的创建很复杂,因此最好手动创建。此外,在 Guice 模块中发布 GUI 进度听起来过于复杂,无法在 Guice 模块中使用。

那么,有什么办法吗?除了上面 sn-ps 中的System.out.println,还有一个手动创建的启动画面。

【问题讨论】:

    标签: java dependency-injection guava guice


    【解决方案1】:

    只需将构建每个服务 impl 的代码移到相应服务接口的 @Provides 方法主体内即可。您提到希望这些是单例,因此您还需要使用 @Singleton 注释提供程序方法。

    至于地图,你可以用 Multibinder 做类似的事情,但我想在推荐之前更好地了解你的设计。

    【讨论】:

    • 依赖其他服务的服务呢?我将不得不手动调用@Provides 方法。这听起来是 Guice 模块的复杂用法。
    • 是否是循环依赖,例如服务 A 使用 B 而 B 使用 A?还是只是 A 取决于 B?
    • 不存在循环依赖。 A 依赖于 B. B 依赖于 C. 等等。
    【解决方案2】:

    实际上是我自己实现的。

    我所做的是在模块中创建一个List&lt;Consumer&lt;Binder&gt;&gt;,然后在configure() 方法中使用它们。

    class ServicesModule extends AbstractModule {
        private List<Consumer<Binder>> consumers = new ArrayList<>();
    
        ServicesModule() {
        }
    
        @Override
        protected void configure() {
            consumers.forEach(c -> c.accept(binder()));
        }
    
        <T> void putService(Class<T> clazz, T instance) {
            consumers.add(t -> t.bind(clazz).toInstance(instance));
        }
    
    }
    

    然后,在应用程序启动期间,我可以逐步提供服务依赖项:

    public static void main(String[] args) {
        ServicesModule services = new ServicesModule();
        
        CarService carService = new CarServiceImpl();
        serviceModule.putService(CarService.class, carService);
        publishProgress(35);
        
        EngineService engineService = new EngineServiceImpl(carService);
        serviceModule.putService(EngineService.class, engineService);
        publishProgres(50);
        //...
        Injector i = Guice.createInjector(new GuiModule(), services);
        i.getInstance(MainView.class).show();
    }
    

    【讨论】:

    • 这仍然不是使用依赖注入的“正确方法”。请参阅我提出的替代答案以获得更自然的方法。
    【解决方案3】:

    您提出的答案需要将有关 service-impl 类的依赖关系的知识传播到这些类之外。依赖注入的整个思想是封装实现依赖。例如,只有EngineServiceImpl 应该知道它依赖于CarService。封装使您的代码更容易推理和测试。

    class ServiceModule extends AbstractModule {
      protected void configure() {
        bind(CarService.class).to(CarServiceImpl.class).in(Singleton.class);
        bind(EngineService.class).to(EngineServiceImpl.class).in(Singleton.class);
      }
    }
    
    class CarServiceImpl implements CarService {
      @Inject
      CarServiceImpl() {} // Not necessary, but adds clarity
    
      // ...
    }
    
    class EngineServiceImpl implements EngineService {
      private final CarService carService;
    
      @Inject
      EngineServiceImpl(CarService carService) {
        this.carService = carService;
      }
    
      // ...
    }
    
    class Main {
      public static void main(String[] args) {
        // I find it clearer to get the starting-point class instance from the injector
        // creation chain, then operate on that instance.
        MainView mainView = 
            Guice.createInjector(new GuiModule(), new ServiceModule())
                .getInstance(MainView.class);
        mainView.show();
      }
    }
    

    【讨论】:

    • 我知道这一点。如何发布这些对象的创建进度?我真的很想要一个SSCCE。可能是一个 TypeListener?另外,在我最初的问题中,我纯粹提到服务位于没有 javax.inject 依赖项的 jar 中。此外,手动创建这些服务是一个复杂的过程,我不得不忍受。有奇怪的异常处理(服务的构造函数抛出异常)。最后,我还提到我仅将 Guice 用于 GUI。我将它们放在一个模块中的原因是因为 GUI 必须能够使用它们。
    • 我已经给了你我所知道的最好的建议。祝你好运。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多