静的メソッド内から使用する必要がある@Autowired
サービスがあります。私はこれが間違っていることを知っていますが、多くの作業を必要とするため、現在のデザインを変更することはできません。 randomMethod()
を非静的に変更することはできません。この自動配線されたBeanを使用する必要があります。それを行う方法の手がかりはありますか?
@Service
public class Foo {
public int doStuff() {
return 1;
}
}
public class Boo {
@Autowired
Foo foo;
public static void randomMethod() {
foo.doStuff();
}
}
これを行うには、次の解決策のいずれかを実行します。
このアプローチは、コンストラクターパラメーターとしていくつかのBeanを必要とするBeanを構築します。コンストラクターコード内で、コンストラクター実行のパラメーターとして取得した値で静的フィールドを設定します。サンプル:
@Component
public class Boo {
private static Foo foo;
@Autowired
public Boo(Foo foo) {
Boo.foo = foo;
}
public static void randomMethod() {
foo.doStuff();
}
}
ここでの考え方は、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();
}
}
静的アプリケーションコンテキストアクセサーアプローチを介してこれを回避する必要があります。
@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();
}
}
できることは、@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;
}
}
これは @ 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
は、同期されるときにコード内の競合ポイントになることに注意してください。アプリケーションが高度に並列化されている場合(例:トラフィックの多いサイトのバックエンド)、これは適切なソリューションではない可能性があります。