这是在Guice中加载插件的正确方法吗?

时间:2017-02-28 06:55:23

标签: java dependency-injection guice

我正在使用Guice进行DI,并尝试以插件样式加载模块。 想象一下,我有一个UserProvider,它需要一个IDataSourceIDataSource可以是DatabaseDataSourceLdapDataSource,但不能同时包含两者(同时拥有2个真值来源是否有意义,是吗?)。然后我将使用从文件中的配置加载的类名字符串来选择IDataSource(例如,部署人员可以配置要使用的数据源)。

我已经列出了这样的代码

guice
   |- + user
      |- + datasource
         |- + database
            |- DatabaseDataSourceConfig.java
            |- DatabaseDataSource.java
            |- DatabaseDataSourceModule.java
         |- + ldap
            |- LdapDataSourceConfig.java
            |- LdapDataSource.java
            |- LdapDataSourceModule.java
         |- IDataSource
      |- UserProvider.java
      |- UserProviderConfig.java
      |- UserProviderModule.java
   |- Factory.java
   |- FactoryConfig.java

Factory.java

public class Factory {
    public static UserProvider create(FactoryConfig settings) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        Injector injector = Guice.createInjector(
                new AbstractModule() {
                    @Override
                    public void configure() {
                        bind(FactoryConfig.class).toInstance(settings);
                    }
                },
                (Module)Class.forName(settings.getUserProviderModuleClassName()).newInstance());

        return injector.getInstance(UserProvider.class);
    }

    public static void main(String[] args) throws Exception {
        // simiulate building config from file
        String modClass = "guice.main.user.UserProviderModule"; // or a stub that doesn't hit the data source
        String subModClass = "guice.main.user.datasource.ldap.LdapDataSourceModule";
        Config config = ConfigFactory.empty()
                .withValue("user.module", ConfigValueFactory.fromAnyRef(modClass))
                .withValue("user.dataSource.module", ConfigValueFactory.fromAnyRef(subModClass));

        FactoryConfig factoryConfig = new FactoryConfig(config);
        IUserProvider provider = Factory.create(factoryConfig);

        provider.work();
    }
}

FactoryConfig.java

public class FactoryConfig {
    private interface Keys {
        String USER_PROVIDER_MODULE = "user.module";
        String USER_PROVIDER_CONFIG = "user";
    }

    private Config config;

    public FactoryConfig(Config config) {
        this.config = config;
    }

    public String getUserProviderModuleClassName() {
        return config.getString(Keys.USER_PROVIDER_MODULE);
    }

    public Config getUserProviderConfig() {
        return config.getConfig(Keys.USER_PROVIDER_CONFIG);
    }
}

UserProvider.java

public class UserProvider implements IUserProvider {
    private UserProviderConfig config;
    private IDataSource dataSource;

    public UserProvider(UserProviderConfig config, IDataSource dataSource) {
        this.config = config;
        this.dataSource = dataSource;
    }

    @Override
    public void work() {
        System.out.println("UserProvider");
        dataSource.work();
    }
}

UserProviderConfig.java

public class UserProviderConfig {
    private interface Keys {
        String DATA_SOURCE_PROVIDER_MODULE = "dataSource.module";
        String DATA_SOURCE_PROVIDER_CONFIG = "dataSource";
    }

    private Config config;

    public UserProviderConfig(Config config) {
        this.config = config;
    }

    public String getDataSourceModuleClassName() {
        return config.getString(Keys.DATA_SOURCE_PROVIDER_MODULE);
    }

    public Config getDataSourceConfig() {
        return config.getConfig(Keys.DATA_SOURCE_PROVIDER_CONFIG);
    }
}

UserProviderModule.java

public class UserProviderModule extends PrivateModule {
    @Override
    protected void configure() {
    }

    @Provides
    public UserProviderConfig userProviderConfig(FactoryConfig settings) {
        return new UserProviderConfig(settings.getUserProviderConfig());
    }

    @Provides
    @Exposed
    public IUserProvider userProvider(UserProviderConfig config, Injector injector) throws Exception {
        Injector childInjector = injector.createChildInjector((Module)Class.forName(config.getDataSourceModuleClassName()).newInstance());

        return new UserProvider(config, childInjector.getInstance(IDataSource.class));
    }
}

然后我认为DatabaseDataSourceLdapDataSource在这里并不重要。

在实践中,我测试过子模块注射是否有效。但是,我想知道这是否是在子模块中加载插件的推荐方法。

我看到过只需在IDataSource方法中加载Factory.create的建议,但这意味着现在Factory必须知道此IDataSource存在并且必须对其进行管理。从理论上讲,Factory应该只知道它所关心的顶级提供者,例如只有UserProvider并让UserProvider使用DI来提供自己。

0 个答案:

没有答案