web-dev-qa-db-ja.com

@Autowiredおよび静的メソッド

静的メソッド内から使用する必要がある@Autowiredサービスがあります。私はこれが間違っていることを知っていますが、多くの作業を必要とするため、現在のデザインを変更することはできません。 randomMethod()を非静的に変更することはできません。この自動配線されたBeanを使用する必要があります。それを行う方法の手がかりはありますか?

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

public class Boo {
    @Autowired
    Foo foo;

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

これを行うには、次の解決策のいずれかを実行します。

コンストラクター@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();
    }
}
124

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

@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();
    }

}
40
Pavel Horal

できることは、@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()引数リストに挿入します。これは、静的フィールドの設定に使用されます。

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

ひどいですが、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;
    }
}
3

これは @ Pavel's answer に基づいて構築され、静的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は、同期されるときにコード内の競合ポイントになることに注意してください。アプリケーションが高度に並列化されている場合(例:トラフィックの多いサイトのバックエンド)、これは適切なソリューションではない可能性があります。

0
Hashken