“辅助”工厂的输出应该是@Singleton

时间:2012-08-07 02:56:28

标签: java guice inject

我目前正在尝试在小型应用程序中使用Google Guice-3.0。

运行此应用程序时,系统会提示用户输入他的姓名和密码。由于在运行时之前不知道此信息,因此我使用AssistedInject来实现我的User实例化。

我的UserFactory就在这里。它提供User方法

public User create(@Assisted("Username") String username, 
                   @Assisted("Password") String password);

User类在程序开始时(用户输入后)通过

启动一次
User user = getInjector().getInstance(UserFactory.class).create(username, password);

我希望在应用程序的生命周期内使用此实例。我已将范围设置为@Singleton

但问题是,我只会遇到错误。 Guice抱怨我在调用

时没有传递任何变量
User user = getInjector().getInstance( User.class );

如果我在bind( User.class );方法中添加configure,则会发生错误,即注释@Assisted没有声明(因为您可以将参数前面的注释添加到唯一识别它们 - Guice可能认为它是其中之一并要求设置依赖(如

bind( String.class ).annotatedWith( Assisted.class ).toInstance( username );

但这不是一件可行的事情(可能会通过静态参考,但为什么要使用Guice?

以下是您可以编译的示例。注释的代码会导致错误。特别是最后两行对我来说非常恼火。

public class KSKB {

    @Singleton
    public static class User {

        public final String name;

        @Inject
        public User(@Assisted("Username") String username) {
            this.name = username;
        }

        public static interface Factory {
            public User create(@Assisted("Username") String username);
        }
    }

    public static void main(String... args) {
        Injector injector = Guice.createInjector(new AbstractModule() {

            @Override
            protected void configure() {
                install(new FactoryModuleBuilder().build(User.Factory.class));
                // bind( User.class );
            }

        });
        User user = injector.getInstance(User.Factory.class).create("Guice");
        System.out.println(user.name);

        // user = injector.getInstance( User.class ); // doesn't work - throws Exception!!
        // System.out.println( user.name );
    }
}

2 个答案:

答案 0 :(得分:3)

我不认为可以得到你想要的东西,因为你想破坏Guice。

Guice希望自己管理实例及其创建。您可以使用Provider和自定义工厂(如Assisted)自行创建对象 - 即使有Guice的支持。但是你无法将这些实例反馈给Guice。这意味着,您不能告诉Guice使用特定对象作为单例实例。 1

可行且简单的解决方案:

  • 在创建注入器之前确定用户名,并在模块中使用bindConstant()来绑定用户名。使用User中的相应对应项。然后,您可以使用User标记@Singleton,而Guice会尊重它。

  • 用户bind(User.class).toInstance(myUser);。这与第一个提案类似,因为必须在Guice启动并运行之前获取所需的用户名。

  • 不要注入User,而是注入单身的中间对象UserHolder(或UserManager)。该对象具有存储实际用户并获取它的方法。您的初始化代码将获取该持有者(当时为空)并将创建的User放入持有者。您的应用程序的其他部分也将注入UserHolder


1 至少没有技巧。

答案 1 :(得分:0)

辅助注射的输出不是注射 - 它是自动连接的工厂(该设施的名字很可怕,令人遗憾)。

AssistedInject产生的是一个工厂 - 该工厂可以是单件。但它是一个工厂,所以它的工作是创建对象,比如User对象。 Factory的内部设置不是为了让它创建Singleton值对象。一般来说,值对象不是单例对象 - 你在这里使用的是“上下文”而不是数据,所以你有一个特例。

此外,您的情况更加特殊,因为您希望在guice运行之前预先验证数据库中的数据。此用户完全是上下文,而不是数据,因此请勿使用工厂来创建它。手动创建它,如果你需要在guice绑定系统的其他地方使用它,那么在Guice外部创建它,并将它作为DatabaseCredentials对象或类似物传递给模块。

为了正确地帮助你,我们需要看到一个更大的设计概念,但我的感觉是,你没有清楚地思考协作者/服务,价值类型,背景/配置等之间的区别,以及混淆很大程度上是因为“用户”这个词在这里超载了。是的,它是一个“用户”,但数据库用户与系统中的其他用户不是同一类用户 - 即使它在结构上相同,它意味着不同的东西,具有不同的生命周期等等。