@Autowiredおよび静的メソッド


100

@Autowired静的メソッド内から使用する必要があるサービスがあります。これは間違っていることはわかっていますが、現在の設計を変更することはできません。多くの作業が必要になるため、簡単なハックが必要です。randomMethod()非静的に変更することはできません。この自動ワイヤードBeanを使用する必要があります。それを行う方法の手がかりはありますか?

@Service
public class Foo {
    public int doStuff() {
        return 1;
    }
}

public class Boo {
    @Autowired
    Foo foo;

    public static void randomMethod() {
         foo.doStuff();
    }
}

4
静的メソッドは、非静的/インスタンスフィールドを参照できません。
Sotirios Delimanolis 2013

18
これが私がこのスレッドを作成した理由です、静的メソッド内からAutowiredインスタンスにアクセスできる方法があります...
Taks

静的メソッドで@Autowiredを使用すると間違っているのはなぜですか?
user59290

回答:


150

これを行うには、次のいずれかのソリューションを実行します。

コンストラクタ@Autowiredの使用

このアプローチは、コンストラクター・パラメーターとしていくつかのBeanを必要とするBeanを構成します。コンストラクターコード内で、コンストラクター実行のパラメーターとして取得した値を静的フィールドに設定します。サンプル:

@Component
public class Boo {

    private static Foo foo;

    @Autowired
    public Boo(Foo foo) {
        Boo.foo = foo;
    }

    public static void randomMethod() {
         foo.doStuff();
    }
}

@PostConstructを使用して静的フィールドに値を渡す

ここでのアイデアは、Beanがスプリングによって構成された後、静的フィールドにBeanを渡すことです。

@Component
public class Boo {

    private static Foo foo;
    @Autowired
    private Foo tFoo;

    @PostConstruct
    public void init() {
        Boo.foo = tFoo;
    }

    public static void randomMethod() {
         foo.doStuff();
    }
}

3
これは安全な解決策ですか?
2013

2
私は最初のソリューションを使用しましたが、それは魅力のように機能しました、ありがとう!
victorleduc 2016

1
最初のソリューションは、@ Qualifierの使用をサポートしていません。複数のリポジトリを使用する場合、問題が残ります。
user1767316

14
静的メソッドがアクセスされる前にコンストラクタが呼び出されることを保証するものは何ですか?
David Dombrowsky 2017

1
非静的メソッドは静的フィールドを変更するため、initメソッドはSonarQubeバグを引き起こします。
jDub9 2018年

45

静的アプリケーションコンテキストアクセサーアプローチでこれを回避する必要があります。

@Component
public class StaticContextAccessor {

    private static StaticContextAccessor instance;

    @Autowired
    private ApplicationContext applicationContext;

    @PostConstruct
    public void registerInstance() {
        instance = this;
    }

    public static <T> T getBean(Class<T> clazz) {
        return instance.applicationContext.getBean(clazz);
    }

}

その後、静的な方法でBeanインスタンスにアクセスできます。

public class Boo {

    public static void randomMethod() {
         StaticContextAccessor.getBean(Foo.class).doStuff();
    }

}

私は実際にこのソリューションが好きですが、完全には理解できません。
2013

2
静的な呼び出しがあなたの管理下にある場合、それはかなり安全です。最も明白な否定的な側面はgetBean、コンテキストが初期化される前(NPE)またはそのBeanのコンテキストが破棄された後に呼び出される可能性があることです。このアプローチには、「醜い」静的コンテキストアクセスが1つのメソッド/クラスに含まれるという利点があります
Pavel Horal 2013

1
これは私の命を救った。他のアプローチよりも非常に便利です。
フェニックス2015年

6

あなたができることは@Autowired、setterメソッドとそれが新しい静的フィールドを設定することです。

public class Boo {
    @Autowired
    Foo foo;

    static Foo staticFoo;   

    @Autowired
    public void setStaticFoo(Foo foo) {
        Boo.staticFoo = foo;
    }

    public static void randomMethod() {
         staticFoo.doStuff();
    }
}

Beanが処理されると、SpringはFoo実装フィールドをインスタンスフィールドに挿入しますfoo。次に、同じFooインスタンスをsetStaticFoo()引数リストに挿入します。これは、静的フィールドを設定するために使用されます。

これはひどい回避策であり、randomMethod()Springがのインスタンスを処理する前に使用しようとすると失敗しますBoo


@PostConstructヘルプを使用しますか?
2013

@Taks確かに、それも機能します。でsetStaticFoo()それなしで、あるFooパラメータ。
Sotirios Delimanolis 2013

質問はそれがより安全になるだろうます。.. :)私は...私たちはあらゆる方法を実行するために許可する前に、すべてを処理するだろうと思った春
Taks

1
@Taks(疑似コードを表示していない限り)表示した方法は機能しません。それを行う方法の手がかりはありますか?取得した複数の回答は回避策ですが、Springがクラスを処理する(実際には副作用のある1つのインスタンスを処理する)まで静的フィールドを使用できないという同じ問題があります。その意味で、それは安全ではありません。
Sotirios Delimanolis 2013

3

面倒ですが、ApplicationContextAwareインターフェースを使用してBeanを取得できます。何かのようなもの :

public class Boo implements ApplicationContextAware {

    private static ApplicationContext appContext;

    @Autowired
    Foo foo;

    public static void randomMethod() {
         Foo fooInstance = appContext.getBean(Foo.class);
         fooInstance.doStuff();
    }

    @Override
    public void setApplicationContext(ApplicationContext appContext) {
        Boo.appContext = appContext;
    }
}

0

これは、上に構築さパヴェルの答え@静的getBeanメソッドからアクセスする際に初期化されていないSpringコンテキストの可能性を解決するために、:

@Component
public class Spring {
  private static final Logger LOG = LoggerFactory.getLogger (Spring.class);

  private static Spring spring;

  @Autowired
  private ApplicationContext context;

  @PostConstruct
  public void registerInstance () {
    spring = this;
  }

  private Spring (ApplicationContext context) {
    this.context = context;
  }

  private static synchronized void initContext () {
    if (spring == null) {
      LOG.info ("Initializing Spring Context...");
      ApplicationContext context = new AnnotationConfigApplicationContext (io.zeniq.spring.BaseConfig.class);
      spring = new Spring (context);
    }
  }

  public static <T> T getBean(String name, Class<T> className) throws BeansException {
    initContext();
    return spring.context.getBean(name, className);
  }

  public static <T> T getBean(Class<T> className) throws BeansException {
    initContext();
    return spring.context.getBean(className);
  }

  public static AutowireCapableBeanFactory getBeanFactory() throws IllegalStateException {
    initContext();
    return spring.context.getAutowireCapableBeanFactory ();
  }
}

ここで重要なのはinitContextメソッドです。これにより、コンテキストが常に初期化されます。ただし、initContext同期されると、コード内での競合のポイントになることに注意してください。アプリケーションが高度に並列化されている場合(トラフィックの多いサイトのバックエンドなど)、これは適切なソリューションではない可能性があります。


-2

AppContextを使用します。必ずコンテキストファイルにBeanを作成してください。

private final static Foo foo = AppContext.getApplicationContext().getBean(Foo.class);

public static void randomMethod() {
     foo.doStuff();
}

これは何ですか??@AutowiredとgetBeanの違いは何ですか
madhairsilence

通常、クラスを通常のSpring @Componentに変換できない場合は、レガシーコードでよく発生します。
carpinchosaurio 2017年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.