Java 向单例类注入依赖项

Java 向单例类注入依赖项,java,dependency-injection,singleton,Java,Dependency Injection,Singleton,我有一个定义如下的单例实例: public class Singleton { private static Singleton INSTANCE = new Singleton(); private Singleton() { } public Singleton getInstance() { return INSTANCE; } } 现在,由于一些变化,这个类必须依赖于几个(3)依赖项。因此,这些依赖关系必须在这里注入 我们如

我有一个定义如下的单例实例:

public class Singleton {
    private static Singleton INSTANCE = new Singleton();

    private Singleton() {

    }

    public Singleton getInstance() {
        return INSTANCE;
    }
}
现在,由于一些变化,这个类必须依赖于几个(3)依赖项。因此,这些依赖关系必须在这里注入

我们如何为这样设计的单例类实现依赖注入

问题是,
Singleton.getInstance()
上已经有很多调用者,因此无法使getInstance方法接受依赖项

附言:我明白使用单例并不总是一种更干净的方式:) (这是现有的代码,我不得不接受它:))


附言:我正在使用Guice进行依赖注入。

你可以这样做

public class Singleton {

    private DependencyClass1 dependency1;

    private DependencyClass2 dependency2;

    private DependencyClass3 dependency3;

    private static Singleton INSTANCE = new Singleton(
            new DependencyClass1(...),
            new DependencyClass2(...),
            new DependencyClass3(...)
    );

    private Singleton(
            DependencyClass1 dependency1,
            DependencyClass2 dependency2,
            DependencyClass3 dependency3
    ) {
        this.dependency1 = dependency1;
        this.dependency2 = dependency2;
        this.dependency3 = dependency3;
    }

    public Singleton getInstance() {
        return INSTANCE;
    }
}
它与任何IOC容器一样


若所有注入的依赖项都有一个单例作用域,那个么您可以这样做,否则您需要在每次需要时创建注入类的实例

有一个将Protype bean注入单例bean的示例:


您可以将类重构为依赖项,同时为客户端保留相同的API。
以Spring为例:

@Component
public class Singleton {

  private Foo foo;
  private Bar bar;
  private Any any;

  // inject dependencies 
  @Autowired
  // annotation not required in recent Spring versions
  public Singleton (Foo foo, Bar bar, Any any){
     this.foo = foo;
     this.bar = bar;
     this.any = any;
  }

  public Singleton getInstance() {
      return this;
  }
}
从客户端,您可以注入bean,如果客户端类不是bean,也可以从bean容器中检索bean

从bean类访问的示例:

public class SingletonClient{

    @Autowired
    private Singleton singleton;

    public void foo(){
        singleton.getInstance().method();
    } 
}

避免更改客户端类的想法。
免责声明:我不提倡这种方式,因为它既违反直觉,又容易出错,而且最重要的是维护了单身人士静态访问的技术债务。
作为临时解决方案,这是可以接受的,但应该尽快对现有代码进行重构。

因此,我们的想法是在构造函数调用期间将bean实例存储在静态字段中。
这样,
Singleton.getInstance()
返回bean

@Component
public class Singleton {

    private Foo foo;
    private Bar bar;
    private Any any;

    private static Singleton instance;

    // inject dependencies
    @Autowired
    // annotation not required in recent Spring versions
    public Singleton(Foo foo, Bar bar, Any any) {
      this.foo = foo;
      this.bar = bar;
      this.any = any;
      instance = this;
    }

    public static Singleton getInstance() {
      return instance;
    }

}

您可以在Guice中使用@Singleton注释来告诉注入器只创建一个在整个应用程序中共享的实例。然后,您可以通过注释变量或选择构造函数,以通常的方式注入依赖项

    @Singleton
    public class Singleton {

      @Inject // Choose to put the annotation here OR on a constructor, not both
      private Foo foo;

      @Inject // only put this constructor here if you don't annotate the variable
      public Singleton (Foo foo){
         this.foo = foo;
      }

      public Singleton getInstance() {
          return this;
      }

...
    }

如果使用Spring,即使没有公共构造函数,也可以实现依赖项注入。只需将依赖项标记为@Autowired。Spring将通过反射注入它们。:)

您正在使用哪个依赖注入框架?或者您必须能够通过静态方法使依赖项可用;或者,您必须咬紧牙关,使用guice@AndyTurner你说我通过静态方法提供依赖项到底是什么意思?无论如何,当客户端调用静态getInstance()时,其他依赖项将保持未初始化状态。请编辑问题以指定这是一个GUI应用程序。这样做没有多大意义:您只是在许多地方重复
DependencyClass1 dependency1
<代码>私有DependencyClass1=新DependencyClass1(…);/*…*/INSTANCE=newsingleton()是等效的且更短。不幸的是,我不能这样做,因为我不能
硬代码
一个具体的依赖项,因为它必须被注入。我认为非默认构造函数显然表明这个类依赖于其他一些类。但这需要更改所有客户端(因为它们调用静态getInstance方法)。我想知道除了使singleton类成为一个普通的依赖项/classI之外,是否还有其他方法可以实现这一点。我假设它不应该是一个重要的重构(用singleton.getInstance()替换
singleton.getInstance()
。具有静态访问权限的singleton很难成为依赖项。静态和依赖项注入根本不以相同的方式工作。我同意你的观点。因此,你说如果不改变客户端调用它的方式,就无法使用这样的静态方法将可注入依赖项添加到这样的singleton类中(这里单例本身必须传递/注入到每个客户机)?我不是说这是不可能的,但这将是一种违反直觉且容易出错的方法。我对此进行了编辑。它在Spring中完成,以与我最初的答案保持一致,但您可以对Guice执行完全相同的操作。