回答:
コンテナーへのアクセスが必要なオブジェクトがコンテナー内のBeanである場合は、BeanFactoryAwareまたはApplicationContextAwareインターフェースを実装するだけです。
コンテナー外のオブジェクトがコンテナーにアクセスする必要がある場合は、Springコンテナーに標準のGoFシングルトンパターンを使用しました。このように、アプリケーションにはシングルトンが1つだけあり、残りはすべてコンテナー内のシングルトンBeanです。
instance()
メソッドをファクトリとして使用するようにSpringコンテナに指示した場合は、@ Antoninでうまくいくかもしれません。ただし、私は、コンテナーの外部にあるすべてのコードが最初にコンテナーにアクセスできるようにするだけだと思います。次に、そのコードはコンテナからオブジェクトをリクエストできます。
実装するApplicationContextAware
か、単に使用することができます@Autowired
:
public class SpringBean {
@Autowired
private ApplicationContext appContext;
}
SpringBean
きますApplicationContext
。このBeanがインスタンス化された範囲内で、注入されました。たとえば、かなり標準的なコンテキスト階層を持つWebアプリケーションがあるとします。
main application context <- (child) MVC context
そしてSpringBean
メインのコンテキスト内で宣言され、それがメインのコンテキストが注入されています。それ以外の場合、MVCコンテキスト内で宣言されていると、MVCコンテキストが挿入されます。
@Inject
できます
これは良い方法です(私のものではなく、元の参照はここにあります:http : //sujitpal.blogspot.com/2007/03/accessing-spring-beans-from-legacy-code.html
私はこのアプローチを使用しましたが、うまく機能します。基本的には、アプリケーションコンテキストへの(静的)参照を保持する単純なBeanです。Spring設定でそれを参照することにより、初期化されます。
元の参照を見てください。非常に明確です。
getBean
ユニットテスト中に実行するコードから呼び出す場合、要求する前にSpringコンテキストが設定されないため、このアプローチは失敗する可能性があります。このアプローチを2年間成功裏に使用してから今日に至ったレースコンディション。
SingletonBeanFactoryLocatorを使用できると思います。beanRefFactory.xmlファイルは実際のapplicationContextを保持し、次のようになります。
<bean id="mainContext" class="org.springframework.context.support.ClassPathXmlApplicationContext">
<constructor-arg>
<list>
<value>../applicationContext.xml</value>
</list>
</constructor-arg>
</bean>
そして、アプリケーションコンテキストからBeanを取得するコードは、次のようになります。
BeanFactoryLocator bfl = SingletonBeanFactoryLocator.getInstance();
BeanFactoryReference bf = bfl.useBeanFactory("mainContext");
SomeService someService = (SomeService) bf.getFactory().getBean("someService");
Springチームは、このクラスとyadayadaの使用を推奨していませんが、私がそれを使用したところにはとても適しています。
他の提案を実装する前に、次の質問を自問してください...
これらの質問に対する回答は、特定の種類のアプリケーション(Webアプリなど)の方が他の種類の場合よりも簡単ですが、とにかく質問する価値があります。
ApplicationContextへのアクセスは、依存関係注入の原則全体にある程度違反しますが、多くの選択肢がない場合があります。
Webアプリを使用する場合は、サーブレットフィルターとThreadLocalを使用して、シングルトンを使用せずにアプリケーションコンテキストにアクセスする別の方法もあります。フィルターでは、WebApplicationContextUtilsを使用してアプリケーションコンテキストにアクセスし、アプリケーションコンテキストまたは必要なBeanをTheadLocalに保存できます。
注意:ThreadLocalの設定を解除するのを忘れると、アプリケーションをアンデプロイしようとすると厄介な問題が発生します。したがって、それを設定して、すぐに、finally-partのThreadLocalの設定を解除する試行を開始する必要があります。
もちろん、これはまだシングルトンを使用しています:ThreadLocal。しかし、実際の豆はもう必要ありません。はリクエストスコープにすることもでき、このソリューションは、EARのライブラリを使用するアプリケーションに複数のWARがある場合にも機能します。それでも、このThreadLocalの使用は、単純なシングルトンの使用と同じくらい悪いと考えるかもしれません。;-)
おそらくSpringはすでに同様のソリューションを提供していますか?見つかりませんでしたが、よくわかりません。
SpringApplicationContext.java
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
/**
* Wrapper to always return a reference to the Spring Application
Context from
* within non-Spring enabled beans. Unlike Spring MVC's
WebApplicationContextUtils
* we do not need a reference to the Servlet context for this. All we need is
* for this bean to be initialized during application startup.
*/
public class SpringApplicationContext implements
ApplicationContextAware {
private static ApplicationContext CONTEXT;
/**
* This method is called from within the ApplicationContext once it is
* done starting up, it will stick a reference to itself into this bean.
* @param context a reference to the ApplicationContext.
*/
public void setApplicationContext(ApplicationContext context) throws BeansException {
CONTEXT = context;
}
/**
* This is about the same as context.getBean("beanName"), except it has its
* own static handle to the Spring context, so calling this method statically
* will give access to the beans by name in the Spring application context.
* As in the context.getBean("beanName") call, the caller must cast to the
* appropriate target class. If the bean does not exist, then a Runtime error
* will be thrown.
* @param beanName the name of the bean to get.
* @return an Object reference to the named bean.
*/
public static Object getBean(String beanName) {
return CONTEXT.getBean(beanName);
}
}
出典:http : //sujitpal.blogspot.de/2007/03/accessing-spring-beans-from-legacy-code.html
ContextSingletonBeanFactoryLocatorを見てください。特定の方法で登録されていると想定して、Springのコンテキストを取得する静的アクセサーを提供します。
それはきれいではなく、おそらくあなたが望むよりも複雑ですが、動作します。
Springアプリケーションでアプリケーションコンテキストを取得するには多くの方法があります。それらは以下のとおりです。
ApplicationContextAware経由:
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
public class AppContextProvider implements ApplicationContextAware {
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
ここで、setApplicationContext(ApplicationContext applicationContext)
メソッドはapplicationContextを取得します
ApplicationContextAware:
実行するApplicationContextの通知を希望するオブジェクトによって実装されるインターフェース。このインターフェースの実装は、たとえば、オブジェクトが一連の共同Beanへのアクセスを必要とする場合に意味があります。
Autowired経由:
@Autowired
private ApplicationContext applicationContext;
ここで、@Autowired
キーワードはapplicationContextを提供します。Autowiredに問題があります。単体テスト中に問題が発生します。
たとえば、シングルトンパターンを使用して、現在の状態ApplicationContext
またはApplicationContext
それ自体を静的変数に格納すると、Spring-testを使用している場合、テストが不安定になり、予測不可能になることに注意してください。これは、Spring-testが同じJVM内のアプリケーションコンテキストをキャッシュして再利用するためです。例えば:
@ContextConfiguration({"classpath:foo.xml"})
ます。@ContextConfiguration({"classpath:foo.xml", "classpath:bar.xml})
@ContextConfiguration({"classpath:foo.xml"})
テストAを実行すると、ApplicationContext
が作成され、実装ApplicationContextAware
または自動配線を行うBean ApplicationContext
が静的変数に書き込む場合があります。
テストBを実行すると同じことが起こり、静的変数がテストBを指すようになります。 ApplicationContext
テストCが実行されると、テストAのTestContext
(ここではApplicationContext
)が再利用されるため、Beanは作成されません。これApplicationContext
で、テスト用のBeanを現在保持している変数とは別の変数を指す静的変数が取得されました。
これがどれほど役立つかわかりませんが、アプリを初期化するときにコンテキストを取得することもできます。これは、の前であっても、コンテキストを取得できる最も早い時刻です@Autowire
。
@SpringBootApplication
public class Application extends SpringBootServletInitializer {
private static ApplicationContext context;
// I believe this only runs during an embedded Tomcat with `mvn spring-boot:run`.
// I don't believe it runs when deploying to Tomcat on AWS.
public static void main(String[] args) {
context = SpringApplication.run(Application.class, args);
DataSource dataSource = context.getBean(javax.sql.DataSource.class);
Logger.getLogger("Application").info("DATASOURCE = " + dataSource);
その点に注意してください; 以下のコードは、すでにロードされているものを使用する代わりに、新しいアプリケーションコンテキストを作成します。
private static final ApplicationContext context =
new ClassPathXmlApplicationContext("beans.xml");
また、戦争beans.xml
のsrc/main/resources
手段の一部である必要があることに注意してください。WEB_INF/classes
実際のアプリケーションはでapplicationContext.xml
言及されているとおりにロードされWeb.xml
ます。
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>META-INF/spring/applicationContext.xml</param-value>
</context-param>
コンストラクタでパスに言及することは困難です。ファイルを見つけることができません。applicationContext.xml
ClassPathXmlApplicationContext
ClassPathXmlApplicationContext("META-INF/spring/applicationContext.xml")
したがって、アノテーションを使用して既存のapplicationContextを使用することをお勧めします。
@Component
public class OperatorRequestHandlerFactory {
public static ApplicationContext context;
@Autowired
public void setApplicationContext(ApplicationContext applicationContext) {
context = applicationContext;
}
}
この質問への回答はわかっていますが、Spring Contextを取得するために実行したKotlinコードを共有したいと思います。
私は専門家ではないので、批評家、レビュー、アドバイスを受け入れます。
https://gist.github.com/edpichler/9e22309a86b97dbd4cb1ffe011aa69dd
package com.company.web.spring
import com.company.jpa.spring.MyBusinessAppConfig
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.context.ApplicationContext
import org.springframework.context.annotation.AnnotationConfigApplicationContext
import org.springframework.context.annotation.ComponentScan
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Import
import org.springframework.stereotype.Component
import org.springframework.web.context.ContextLoader
import org.springframework.web.context.WebApplicationContext
import org.springframework.web.context.support.WebApplicationContextUtils
import javax.servlet.http.HttpServlet
@Configuration
@Import(value = [MyBusinessAppConfig::class])
@ComponentScan(basePackageClasses = [SpringUtils::class])
open class WebAppConfig {
}
/**
*
* Singleton object to create (only if necessary), return and reuse a Spring Application Context.
*
* When you instantiates a class by yourself, spring context does not autowire its properties, but you can wire by yourself.
* This class helps to find a context or create a new one, so you can wire properties inside objects that are not
* created by Spring (e.g.: Servlets, usually created by the web server).
*
* Sometimes a SpringContext is created inside jUnit tests, or in the application server, or just manually. Independent
* where it was created, I recommend you to configure your spring configuration to scan this SpringUtils package, so the 'springAppContext'
* property will be used and autowired at the SpringUtils object the start of your spring context, and you will have just one instance of spring context public available.
*
*Ps: Even if your spring configuration doesn't include the SpringUtils @Component, it will works tto, but it will create a second Spring Context o your application.
*/
@Component
object SpringUtils {
var springAppContext: ApplicationContext? = null
@Autowired
set(value) {
field = value
}
/**
* Tries to find and reuse the Application Spring Context. If none found, creates one and save for reuse.
* @return returns a Spring Context.
*/
fun ctx(): ApplicationContext {
if (springAppContext!= null) {
println("achou")
return springAppContext as ApplicationContext;
}
//springcontext not autowired. Trying to find on the thread...
val webContext = ContextLoader.getCurrentWebApplicationContext()
if (webContext != null) {
springAppContext = webContext;
println("achou no servidor")
return springAppContext as WebApplicationContext;
}
println("nao achou, vai criar")
//None spring context found. Start creating a new one...
val applicationContext = AnnotationConfigApplicationContext ( WebAppConfig::class.java )
//saving the context for reusing next time
springAppContext = applicationContext
return applicationContext
}
/**
* @return a Spring context of the WebApplication.
* @param createNewWhenNotFound when true, creates a new Spring Context to return, when no one found in the ServletContext.
* @param httpServlet the @WebServlet.
*/
fun ctx(httpServlet: HttpServlet, createNewWhenNotFound: Boolean): ApplicationContext {
try {
val webContext = WebApplicationContextUtils.findWebApplicationContext(httpServlet.servletContext)
if (webContext != null) {
return webContext
}
if (createNewWhenNotFound) {
//creates a new one
return ctx()
} else {
throw NullPointerException("Cannot found a Spring Application Context.");
}
}catch (er: IllegalStateException){
if (createNewWhenNotFound) {
//creates a new one
return ctx()
}
throw er;
}
}
}
これで、Springコンテキストが公開され、このJavaサーブレットのように、コンテキスト(junitテスト、Bean、手動でインスタンス化されたクラス)に関係なく同じメソッドを呼び出すことができます。
@WebServlet(name = "MyWebHook", value = "/WebHook")
public class MyWebServlet extends HttpServlet {
private MyBean byBean
= SpringUtils.INSTANCE.ctx(this, true).getBean(MyBean.class);
public MyWebServlet() {
}
}
以下のようにSpring Beanでautowireを実行します。@Autowired private ApplicationContext appContext;
あなたはapplicationcontextオブジェクトになります。
アプローチ1: ApplicationContextAwareインターフェースを実装することにより、ApplicationContextを注入できます。参照リンク。
@Component
public class ApplicationContextProvider implements ApplicationContextAware {
private ApplicationContext applicationContext;
public ApplicationContext getApplicationContext() {
return applicationContext;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
アプローチ2:任意のSpringマネージドBeanのAutowireアプリケーションコンテキスト。
@Component
public class SpringBean {
@Autowired
private ApplicationContext appContext;
}
参照リンク。