Prilagođeni opseg u proljeće

1. Pregled

Iz kutije Spring nudi dva standardna područja boba ("Singleton" i "prototip") koji se može koristiti u bilo kojoj proljetnoj aplikaciji, plus tri dodatna opsega graha ("zahtjev", "sjednica", i “GlobalSession”) za uporabu samo u web-svjesnim aplikacijama.

Standardni opseg graha ne može se nadjačati, a općenito se smatra lošom praksom nadjačati opsege koji su svjesni weba. Međutim, možda imate aplikaciju koja zahtijeva različite ili dodatne mogućnosti od onih koje se nalaze u priloženim opsezima.

Na primjer, ako razvijate sustav više stanara, možda ćete htjeti pružiti zasebnu instancu određenog graha ili skupa graha za svakog stanara. Spring pruža mehanizam za stvaranje prilagođenih opsega za ovakve scenarije.

U ovom ćemo brzom vodiču pokazati kako stvoriti, registrirati i koristiti prilagođeni opseg u proljetnoj aplikaciji.

2. Stvaranje klase prilagođenog opsega

Da biste stvorili prilagođeni opseg, moramo provesti Opseg sučelje. Pritom moramo i mi osigurati da implementacija bude sigurna u nitima jer opsege mogu istodobno koristiti više tvornica graha.

2.1. Upravljanje obuhvaćenim objektima i povratnim pozivima

Jedna od prvih stvari koje treba uzeti u obzir prilikom implementacije običaja Opseg klasa je način na koji ćete pohranjivati ​​i upravljati objektima s ograničenim opsegom i povratnim pozivima uništenja. To se može učiniti na primjer pomoću karte ili namjenskog predavanja.

Za ovaj ćemo članak to učiniti na siguran način pomoću sinkroniziranih karata.

Počnimo definirati našu prilagođenu klasu opsega:

javna klasa TenantScope implementira Scope {private map scopedObjects = Collections.synchronizedMap (new HashMap ()); private MapstructionCallbacks = Collections.synchronizedMap (novi HashMap ()); ...}

2.2. Dohvaćanje predmeta iz opsega

Da dohvatimo objekt po imenu iz našeg opsega, implementiramo getObject metoda. Kao što JavaDoc navodi, ako imenovani objekt ne postoji u opsegu, ova metoda mora stvoriti i vratiti novi objekt.

U našoj implementaciji provjeravamo je li imenovani objekt na našoj karti. Ako jest, vraćamo ga, a ako nije, koristimo ObjectFactory da biste stvorili novi objekt, dodajte ga na našu kartu i vratite:

@Override public Object get (String name, ObjectFactory objectFactory) {if (! ScopedObjects.containsKey (name)) {scopedObjects.put (name, objectFactory.getObject ()); } return scopedObjects.get (ime); }

Od pet metoda definiranih Opseg sučelje, samo dobiti metoda je potrebna za potpunu provedbu opisanog ponašanja. Ostale su četiri metode neobavezne i mogu baciti UnsupportedOperationException ako ne trebaju ili ne mogu podržati neku funkcionalnost.

2.3. Registriranje povratnog poziva za uništavanje

Moramo također provesti registerDestructionCallback metoda. Ova metoda pruža povratni poziv koji se treba izvršiti kada je imenovani objekt uništen ili ako aplikacija uništi sam opseg:

@Override public void registerDestructionCallback (naziv niza, povratni poziv koji se može izvoditi) {structionCallbacks.put (ime, povratni poziv); }

2.4. Uklanjanje predmeta iz opsega

Dalje, provedimo ukloniti metoda koja uklanja imenovani objekt iz opsega i uklanja i njegov registrirani povratni poziv za uništavanje, vraćajući uklonjeni objekt:

@Preuzmi javni objekt ukloni (naziv niza) {structionCallbacks.remove (ime); vratiti scopedObjects.remove (ime); }

Imajte na umu da odgovornost je pozivatelja da stvarno izvrši povratni poziv i uništi uklonjeni objekt.

2.5. Dohvaćanje ID-a razgovora

Sada, provedimo getConversationId metoda. Ako vaš opseg podržava koncept ID-a razgovora, vratili biste ga ovdje. Inače, konvencija se treba vratiti null:

@Override public String getConversationId () {return "stanar"; }

2.6. Rješavanje kontekstualnih objekata

Napokon, provedimo resolContextualObject metoda. Ako vaš opseg podržava više kontekstualnih objekata, svaki biste povezali s vrijednošću ključa i vratili biste objekt koji odgovara navedenom ključ parametar. Inače, konvencija se treba vratiti null:

@Override public Object resolContextualObject (String key) {return null; }

3. Registriranje prilagođenog opsega

Da biste osvijestili spremnik Spring za vaš novi opseg, to trebate registrirajte ga putem registerScope metoda na a ConfigurableBeanFactory primjer. Pogledajmo definiciju ove metode:

void registerScope (Niz opsegaName, Opseg opsega);

Prvi parametar, opsegName, koristi se za identificiranje / specificiranje opsega pomoću njegovog jedinstvenog naziva. Drugi parametar, opseg, stvarna je instanca običaja Opseg implementaciju koju želite registrirati i koristiti.

Stvorimo običaj BeanFactoryPostProcessor i registrirajte naš prilagođeni opseg pomoću a ConfigurableListableBeanFactory:

javna klasa TenantBeanFactoryPostProcessor implementira BeanFactoryPostProcessor {@Override public void postProcessBeanFactory (ConfigurableListableBeanFactory factory) baca BeansException {factory.registerScope ("stanar", novi TenantScope ()); }}

Sada, napišite klasu Spring konfiguracije koja učitava naš BeanFactoryPostProcessor provedba:

@Configuration javna klasa TenantScopeConfig {@Bean public static BeanFactoryPostProcessor beanFactoryPostProcessor () {return new TenantBeanFactoryPostProcessor (); }}

4. Korištenje prilagođenog opsega

Sad kad smo registrirali svoj prilagođeni opseg, možemo ga primijeniti na bilo koji naš grah, baš kao što bismo to učinili i na bilo kojem drugom grahu koji koristi opseg koji nije jednokrevetna (zadani opseg) - pomoću @Skop napomena i specificiranje našeg prilagođenog opsega po imenu.

Stvorimo jednostavan StanarBean klase - za trenutak ćemo proglasiti grah obuhvaćen stanarima ove vrste:

javna klasa TenantBean {privatni konačni naziv niza; javni TenantBean (ime niza) {this.name = name; } javna praznina sayHello () {System.out.println (String.format ("Pozdrav iz% s tipa% s", this.name, this.getClass (). getName ())); }}

Imajte na umu da nismo koristili razinu klase @Komponenta i @Skop bilješke na ovom predavanju.

Sada, definirajmo neke grahove s opsegom stanara u klasi konfiguracije:

@Configuration javna klasa TenantBeansConfig {@Scope (scopeName = "tenant") @Bean public TenantBean foo () {return new TenantBean ("foo"); } @Scope (scopeName = "tenant") @Bean public TenantBean bar () {return new TenantBean ("bar"); }}

5. Testiranje prilagođenog opsega

Napišimo test za vježbanje naše prilagođene konfiguracije opsega učitavanjem datoteke ApplicationContext, registrirajući naš Konfiguracija klase i dohvaćanje graha s opsegom stanara:

@Test javna konačna void whenRegisterScopeAndBeans_thenContextContainsFooAndBar () {AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext (); isprobajte {ctx.register (TenantScopeConfig.class); ctx.register (TenantBeansConfig.class); ctx.refresh (); TenantBean foo = (TenantBean) ctx.getBean ("foo", TenantBean.class); foo.sayHello (); TenantBean bar = (TenantBean) ctx.getBean ("bar", TenantBean.class); bar.sayHello (); Mapa foos = ctx.getBeansOfType (TenantBean.class); assertThat (foo, not (jednakTo (bar))); assertThat (foos.size (), jednakTo (2)); assertTrue (foos.containsValue (foo)); assertTrue (foos.containsValue (bar)); BeanDefinition fooDefinition = ctx.getBeanDefinition ("foo"); BeanDefinition barDefinition = ctx.getBeanDefinition ("traka"); assertThat (fooDefinition.getScope (), jednakTo ("stanar")); assertThat (barDefinition.getScope (), jednakTo ("stanar")); } napokon {ctx.close (); }}

A rezultat našeg testa je:

Pozdrav iz foo tipa org.baeldung.customscope.TenantBean Pozdrav iz šipke tipa org.baeldung.customscope.TenantBean

6. Zaključak

U ovom smo brzom vodiču pokazali kako definirati, registrirati i koristiti prilagođeni opseg u proljeće.

Više o prilagođenim opsezima možete pročitati u Spring Framework Referenceu. Također možete pogledati različite implementacije Springa Opseg klase u spremištu Spring Framework na GitHubu.

Kao i obično, uzorke koda korištene u ovom članku možete pronaći na projektu GitHub.