الحصول على الربيع سياق التطبيق
-
02-07-2019 - |
سؤال
هل هناك طريقة محليا/عالميا طلب نسخة من ApplicationContext في الربيع ؟
على افتراض الطبقة الرئيسية يبدأ تهيئة التطبيق السياق أنها لا تحتاج إلى أن تمر عبر الاستدعاءات إلى أي من الفئات التي تحتاج إليها ، أو هل هناك طريقة لفئة لطلب إنشاؤها مسبقا السياق ؟ (والتي أفترض أن يكون المفرد?)
المحلول
إذا كان الكائن الذي يحتاج إلى الوصول إلى حاوية الفول في وعاء ، فقط تنفيذ BeanFactoryAware أو ApplicationContextAware الواجهات.
إذا كان كائن خارج الحاوية يحتاج إلى الوصول إلى الحاوية ، لقد استعملت معيار صندوق القناص نمط المفرد عن الربيع الحاوية.بهذه الطريقة يكون لديك واحد فقط المفرد في التطبيق الخاص بك ، والباقي كلها المفرد الفاصوليا في وعاء.
نصائح أخرى
يمكنك تنفيذ ApplicationContextAware
أو مجرد استخدام @Autowired
:
public class SpringBean {
@Autowired
private ApplicationContext appContext;
}
SpringBean
سوف يكون ApplicationContext
حقن داخل هذا الفول هو مثيل.على سبيل المثال إذا كان لديك تطبيق ويب مع معيار جميلة السياقات الهرمي:
main application context <- (child) MVC context
و SpringBean
أعلن الرئيسية داخل السياق ، سيكون لها الرئيسية السياق حقن;وإلا لو كان أعلن في MVC السياق ، سيكون لها MVC السياق حقن.
هنا طريقة لطيفة (ليس لي ، المرجع الأصلي هنا:http://sujitpal.blogspot.com/2007/03/accessing-spring-beans-from-legacy-code.html
لقد استعملت هذا النهج وأنه يعمل بشكل جيد.أساسا انها بسيطة الفول الذي يحمل (ثابت) إشارة إلى سياق التطبيق.قبل الرجوع في الربيع التكوين هو تهيئة.
نلقي نظرة على المرجع الأصلي, انها واضحة جدا.
أعتقد أنك يمكن أن تستخدم SingletonBeanFactoryLocator.على beanRefFactory.xml الملف يعقد الفعلية applicationContext ، فإنه يذهب شيء من هذا القبيل:
<bean id="mainContext" class="org.springframework.context.support.ClassPathXmlApplicationContext">
<constructor-arg>
<list>
<value>../applicationContext.xml</value>
</list>
</constructor-arg>
</bean>
و رمز للحصول على الفول من applicationcontext من أينما سيكون شيئا مثل هذا:
BeanFactoryLocator bfl = SingletonBeanFactoryLocator.getInstance();
BeanFactoryReference bf = bfl.useBeanFactory("mainContext");
SomeService someService = (SomeService) bf.getFactory().getBean("someService");
الربيع فريق تشجيع استخدام هذه الفئة و yadayada ، لكنه لم يناسبني جيدا حيث كنت قد استخدمت.
قبل تنفيذ أي من الاقتراحات الأخرى ، اسأل نفسك هذه الأسئلة...
- لماذا أنا أحاول الحصول على ApplicationContext?
- أنا فعليا باستخدام ApplicationContext كخدمة محدد ؟
- يمكن تجنب الوصول إلى ApplicationContext في كل شيء ؟
الأجوبة على هذه الأسئلة هي أسهل في أنواع معينة من التطبيقات (تطبيقات الويب ، على سبيل المثال) مما هي في بلدان أخرى ، ولكن تستحق يسأل على أي حال.
الوصول إلى ApplicationContext هل نوع من انتهاك كله حقن التبعية من حيث المبدأ ، ولكن في بعض الأحيان كنت قد حصلت على الكثير من الخيارات.
إذا كنت تستخدم التطبيق على شبكة الإنترنت كما أن هناك طريقة أخرى للوصول إلى التطبيق السياق دون استخدام ووحدانية باستخدام servletfilter و ThreadLocal.في فلتر يمكنك الوصول إلى التطبيق السياق باستخدام WebApplicationContextUtils وتخزينها إما تطبيق السياق أو حاجة الفول في TheadLocal.
تنبيه:إذا كنت قد نسيت أن ضبطه ThreadLocal سوف تحصل على المشاكل سيئة عند محاولة undeploy التطبيق!وبالتالي ، يجب تعيين والبدء فورا في محاولة أن unsets على ThreadLocal في النهاية-الجزء.
بالطبع, هذا لا يزال يستخدم المفرد:على ThreadLocal.ولكن الفعلي الفول لا تحتاج إلى أن تكون أكثر.يمكن حتى يكون الطلب-راقب, و هذا الحل يعمل أيضا إذا كان لديك العديد من الحروب في التطبيق مع libaries في الأذن.لا يزال, قد تفكر في استخدام هذه ThreadLocal بسوء استخدام عادي ووحدانية.;-)
ربما الربيع يوفر بالفعل مماثل الحل ؟ لم أجد واحد, ولكن أنا لا أعرف على وجه اليقين.
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.فإنه يوفر ثابت accessors للحصول على عقد من الربيع السياقات ، على افتراض أنها كانت مسجلة في طرق معينة.
انها ليست جميلة و أكثر تعقيدا من ربما كنت ترغب ، لكنه يعمل.
علما بأن طريق تخزين أي دولة من الحالي ApplicationContext
, أو ApplicationContext
نفسها في متغير ثابت - على سبيل المثال باستخدام نمط المفرد - سوف تجعل الخاصة بك الاختبارات غير مستقر ولا يمكن التنبؤ بها إذا كنت تستخدم الربيع الاختبار.لأن هذا هو الربيع-اختبار مخابئ وإعادة استعمال تطبيق السياقات في نفس JVM.على سبيل المثال:
- اختبار تشغيل و هو مشروح مع
@ContextConfiguration({"classpath:foo.xml"})
. - ب اختبار تشغيل و هو مشروح مع
@ContextConfiguration({"classpath:foo.xml", "classpath:bar.xml})
- اختبار التشغيل C و هو مشروح مع
@ContextConfiguration({"classpath:foo.xml"})
عند اختبار أشواط ، ApplicationContext
يتم إنشاء أي حبوب implemeting ApplicationContextAware
أو autowiring ApplicationContext
قد يكتب إلى متغير ثابت.
عندما الاختبار ب يعمل نفس الشيء يحدث متغير ثابت يشير الآن إلى اختبار B ApplicationContext
عند اختبار ج أشواط ، لا الفاصوليا يتم إنشاؤها كما TestContext
(وهنا ApplicationContext
) من اختبار resused.الآن لديك متغير ثابت مشيرا إلى أخرى ApplicationContext
من واحد حاليا عقد الفول لاختبار الخاص بك.
هناك العديد من الطرق للحصول على التطبيق في سياق الربيع التطبيق.تلك هي بالنظر إلى رفع الصوت عاليا:
عبر 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 أنه يعمل في.تنفيذ هذه الواجهة من المنطقي على سبيل المثال عندما كائن يتطلب الوصول إلى مجموعة من التعاون الفاصوليا.
عبر Autowired:
@Autowired private ApplicationContext applicationContext;
هنا @Autowired
الكلمة سوف نقدم applicationContext.Autowired لديه بعض المشاكل.فإنه سيتم إنشاء المشكلة خلال وحدة الفحص.
ليس متأكدا من مدى فائدة هذا سوف يكون ، ولكن يمكنك أيضا الحصول على السياق عند تهيئة التطبيق.هذا هو أقرب يمكنك الحصول على السياق ، حتى قبل @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;
}
}
أنا أعرف الإجابة على هذا السؤال, ولكن أود أن حصة هذه 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;
}
}
}
الآن, الربيع السياق متاحة للجمهور ، أن تكون قادرة على استدعاء نفس طريقة مستقلة عن السياق (junit الاختبارات والفاصوليا يدويا مثيل الطبقات) مثل على هذا بريمج جافا:
@WebServlet(name = "MyWebHook", value = "/WebHook")
public class MyWebServlet extends HttpServlet {
private MyBean byBean
= SpringUtils.INSTANCE.ctx(this, true).getBean(MyBean.class);
public MyWebServlet() {
}
}
هل autowire في الربيع الفول على النحو التالي :@Autowired خاصة ApplicationContext appContext;
سوف applicationcontext الكائن.