Vra

Is daar 'n manier om staties / 'n afskrif van die ApplicationContext wêreldwyd versoek in 'n lente aansoek?

Die aanvaarding van die hoofklas begin en initialisatie die aansoek konteks, beteken dit nodig om af te slaag wat deur die oproep stapel om enige klasse wat dit nodig het, of is daar 'n manier vir 'n klas te vra vir die voorheen geskep konteks? (Wat ek aanvaar het om 'n Singleton wees?)

Was dit nuttig?

Oplossing

As die voorwerp wat toegang moet die houer is 'n boontjie in die houer, net implementeer die beanFactoryAware of ApplicationContextAware koppelvlakke.

As 'n voorwerp buite die houer toegang tot die houer moet, ek het 'n standaard GOF Singleton gebruik patroon vir die lente houer. Op dié manier, jy het net een Singleton in jou aansoek, die res is almal Singleton bone in die houer.

Ander wenke

Jy kan ApplicationContextAware implementeer of net gebruik @Autowired:

public class SpringBean {
  @Autowired
  private ApplicationContext appContext;
}

SpringBean sal het ApplicationContext ingespuit, waarbinne hierdie boontjie is aangehaal. Byvoorbeeld, as jy het 'n web aansoek met 'n redelik standaard konteks hiërargie:

main application context <- (child) MVC context

en SpringBean verklaar binne belangrikste konteks, sal dit vernaamste konteks ingespuit het; anders, as dit binne MVC konteks is verklaar, sal dit MVC konteks ingespuit.

Hier is 'n mooi manier (nie myne nie, die oorspronklike verwysing is hier: http://sujitpal.blogspot.com/2007 /03/accessing-spring-beans-from-legacy-code.html

Ek het hierdie benadering gebruik en dit werk goed. Eintlik is dit is 'n eenvoudige boontjie dat 'n (statiese) verwysing na die aansoek verband hou. Deur verwysing dit in die lente config dit geïnisialiseer.

Neem 'n blik op die oorspronklike ref, is dit baie duidelik.

Ek glo jy kan gebruik SingletonBeanFactoryLocator . Die beanRefFactory.xml lêer sal die werklike applicationContext hou, sou dit iets soos hierdie gaan:

<bean id="mainContext" class="org.springframework.context.support.ClassPathXmlApplicationContext">
     <constructor-arg>
        <list>
            <value>../applicationContext.xml</value>
        </list>
     </constructor-arg>
 </bean>

En die kode om 'n boontjie kry uit die applicationcontext van whereever sou so iets wees:

BeanFactoryLocator bfl = SingletonBeanFactoryLocator.getInstance();
BeanFactoryReference bf = bfl.useBeanFactory("mainContext");
SomeService someService = (SomeService) bf.getFactory().getBean("someService");

Die Lente span ontmoedig die gebruik van hierdie klas en yadayada, maar dit het my gepas goed waar ek dit gebruik het.

Voordat jy enige van die ander voorstelle te implementeer, vra jouself die volgende vrae ...

  • Hoekom moet ek probeer om die ApplicationContext? Kry
  • Is ek doeltreffend deur middel van die ApplicationContext as 'n diens locator?
  • Kan ek vermy toegang tot die ApplicationContext glad?

Die antwoorde op hierdie vrae is makliker in sekere vorme van aansoeke (Web programme, byvoorbeeld) as hulle in ander, maar is die moeite werd om te vra in elk geval.

Toegang tot die ApplicationContext beteken soort in stryd met die hele afhanklikheid inspuiting beginsel, maar soms jy het nie veel keuse.

As jy 'n web-app daar is ook 'n ander manier om toegang te verkry tot die aansoek konteks sonder die gebruik van single deur gebruik te maak van 'n servletfilter en 'n ThreadLocal gebruik. In die filter kan jy toegang tot die aansoek konteks met behulp van WebApplicationContextUtils en stoor óf die aansoek konteks of die nodige bone in die TheadLocal.

Let op: as jy vergeet om die ThreadLocal ontstel jy sal nare probleme kry wanneer ek probeer om die aansoek undeploy! So, moet jy dit stel en onmiddellik 'n drie wat die ThreadLocal unsets in die finaal-deel begin.

Natuurlik is dit gebruik nog 'n Singleton: die ThreadLocal. Maar die werklike bone hoef nie meer te wees. Die kan selfs versoek-scoped wees, en hierdie oplossing werk ook as jy meer oorloë in 'n aansoek by die biblioteek-in die aar. Tog, kan jy dit gebruik van ThreadLocal so erg soos die gebruik van gewone single oorweeg. ; -)

Miskien Lente bied reeds 'n soortgelyke oplossing? Ek het dit nie vind een, maar ek weet nie vir seker nie.

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

Bron: http: // sujitpal. blogspot.de/2007/03/accessing-spring-beans-from-legacy-code.html

Neem 'n blik op ContextSingletonBeanFactoryLocator . Dit bied statiese Toegangers te hou van konteks Lente kry, in die veronderstelling dat hulle is in sekere maniere geregistreer.

Dit is nie mooi, en nog baie meer kompleks as miskien jy wil, maar dit werk.

Let daarop dat deur die stoor van enige staat van die huidige ApplicationContext, of die ApplicationContext self in 'n statiese veranderlike - byvoorbeeld met behulp van die Singleton patroon - jy sal jou toetse onstabiel en onvoorspelbaar maak as jy die gebruik van die lente-toets. Dit is omdat die lente-toets caches en hergebruik aansoek kontekste in dieselfde JVM. Byvoorbeeld:

  1. Toets A run en dit is aangeteken met @ContextConfiguration({"classpath:foo.xml"}).
  2. Toets B hardloop en dit is aangeteken met @ContextConfiguration({"classpath:foo.xml", "classpath:bar.xml})
  3. Toets C hardloop en dit is aangeteken met @ContextConfiguration({"classpath:foo.xml"})

Wanneer Toets A loop, 'n ApplicationContext is geskep, en enige bone implemeting ApplicationContextAware of autowiring ApplicationContext kan skrywe aan die statiese veranderlike.

Wanneer Toets B loop dieselfde ding gebeur, en die statiese veranderlike wys nou na B se ApplicationContext Toets

Wanneer Toets C loop, geen boontjies geskep as die TestContext (en hierin die ApplicationContext) van toets A resused. Nou het jy 'n statiese veranderlike wys na 'n ander ApplicationContext as die een wat tans die bone vir jou toets.

Daar is baie manier om aansoek konteks kry in die lente aansoek. Dit is geloei gegee:

  1. Via 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;
    }
    }
    

Hier setApplicationContext(ApplicationContext applicationContext) metode wat jy sal die applicationContext kry

  

ApplicationContextAware :

     

Interface geïmplementeer moet word deur enige voorwerp wat wil in kennis gestel word   van die ApplicationContext dat dit loop in. Implementering van hierdie koppelvlak   sinvol byvoorbeeld wanneer 'n voorwerp toegang tot 'n stel van vereis   saam bone.

  1. Via Autowired :

    @Autowired
    private ApplicationContext applicationContext;
    

Hier @Autowired navraag sal die applicationContext voorsien. Autowired het 'n paar probleme. Dit sal probleem tydens eenheid-toetsing te skep.

Nie seker hoe nuttig dit sal wees nie, maar jy kan ook die konteks wanneer jy die app inisialiseer. Dit is die afgelope kan jy die konteks kry, selfs voor 'n @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);

Let asseblief daarop dat, die onderstaande kode sal 'n nuwe aansoek konteks te skep in plaas van die gebruik van die reeds gelaai een.

private static final ApplicationContext context = 
               new ClassPathXmlApplicationContext("beans.xml");

Let ook daarop dat beans.xml moet deel van src/main/resources wees beteken in oorlog is dit deel van WEB_INF/classes, waar as die werklike aansoek deur by applicationContext.xml genoem Web.xml sal gelaai word nie.

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>META-INF/spring/applicationContext.xml</param-value>
</context-param>

Dit is moeilik om applicationContext.xml pad in ClassPathXmlApplicationContext constructor noem. ClassPathXmlApplicationContext("META-INF/spring/applicationContext.xml") nie in staat wees om die lêer op te spoor.

Dit is dus beter om bestaande applicationContext gebruik deur die gebruik van notas.

@Component
public class OperatorRequestHandlerFactory {

    public static ApplicationContext context;

    @Autowired
    public void setApplicationContext(ApplicationContext applicationContext) {
        context = applicationContext;
    }
}

Ek weet hierdie vraag beantwoord word, maar ek wil graag die Kotlin kode wat ek gedoen het om die lente konteks haal deel.

Ek is nie 'n spesialis, so ek is oop vir kritiek, resensies en advies:

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;
        }
    }
}

Nou, 'n lente konteks is publiek sigbaar, in staat is om dieselfde metode onafhanklik van die konteks (Junit toetse, bone, hand aangehaal klasse) soos 'n beroep op hierdie Java Servlet:

@WebServlet(name = "MyWebHook", value = "/WebHook")
public class MyWebServlet extends HttpServlet {


    private MyBean byBean
            = SpringUtils.INSTANCE.ctx(this, true).getBean(MyBean.class);


    public MyWebServlet() {

    }
}

Doen autowire in die lente boontjie soos hieronder:   @Autowired   private ApplicationContext appContext;

Jy kry die applicationcontext voorwerp.

Gelisensieer onder: CC-BY-SA met toeskrywing
Nie verbonde aan StackOverflow
scroll top