Jarné webové kontexty

1. Úvod

Pri použití Spring vo webovej aplikácii máme niekoľko možností na usporiadanie kontextov aplikácií, ktoré to všetko spájajú.

V tomto článku sa chystáme analyzovať a vysvetliť najbežnejšie možnosti, ktoré Spring ponúka.

2. Kontext koreňovej webovej aplikácie

Každá webová aplikácia Spring má priradený kontext aplikácie, ktorý je viazaný na jej životný cyklus: kontext koreňovej webovej aplikácie.

Toto je stará funkcia, ktorá predchádza Spring Web MVC, takže nie je viazaná konkrétne na žiadnu technológiu webového rámca.

Kontext sa spustí pri spustení aplikácie a pri ukončení sa zničí vďaka poslucháčovi kontextu servletu. Najbežnejšie typy kontextov je možné aktualizovať aj za behu, aj keď nie všetky ApplicationContext implementácie túto schopnosť majú.

Kontext vo webovej aplikácii je vždy inštanciou WebApplicationContext. Toto je rozširujúce rozhranie ApplicationContext so zmluvou o prístupe k internetu ServletContext.

Aplikácie by sa spravidla nemali zaujímať o tieto podrobnosti implementácie: kontext koreňovej webovej aplikácie je jednoducho centralizované miesto na definovanie zdieľaných bôbov.

2.1. The ContextLoaderListener

Kontext koreňovej webovej aplikácie popísaný v predchádzajúcej časti spravuje poslucháč triedy org.springframework.web.context.ContextLoaderListener, ktorá je súčasťou jarný web modul.

V predvolenom nastavení poslucháč načíta kontext aplikácie XML z /WEB-INF/applicationContext.xml. Tieto predvolené hodnoty sa však dajú zmeniť. Namiesto XML môžeme použiť napríklad anotácie Java.

Tento poslucháč môžeme nakonfigurovať buď v deskriptore webapp (web.xml súbor) alebo programovo v prostrediach Servlet 3.x.

V nasledujúcich častiach sa podrobne pozrieme na každú z týchto možností.

2.2. Použitím web.xml a kontext aplikácie XML

Pri použití web.xml, konfigurujeme poslucháča ako obvykle:

  org.springframework.web.context.ContextLoaderListener 

Môžeme určiť alternatívne umiestnenie konfigurácie kontextu XML pomocou contextConfigLocation parameter:

 contextConfigLocation /WEB-INF/rootApplicationContext.xml 

Alebo viac ako jedno miesto oddelené čiarkami:

 contextConfigLocation /WEB-INF/context1.xml, /WEB-INF/context2.xml 

Môžeme dokonca použiť vzory:

 contextConfigLocation /WEB-INF/*-context.xml 

V každom prípade, je definovaný iba jeden kontext, kombináciou všetkých definícií fazule načítaných zo zadaných umiestnení.

2.3. Použitím web.xml a kontext aplikácií Java

Môžeme tiež určiť ďalšie typy kontextov okrem predvoleného založeného na XML. Pozrime sa napríklad, ako namiesto toho použiť konfiguráciu anotácií Java.

Používame contextClass parameter povedať poslucháčovi, ktorý typ kontextu má vytvoriť inštanciu:

 contextClass org.springframework.web.context.support.AnnotationConfigWebApplicationContext 

Každý typ kontextu môže mať predvolené umiestnenie konfigurácie. V našom prípade AnnotationConfigWebApplicationContext žiadny nemá, takže ho musíme poskytnúť.

Môžeme tak uviesť jednu alebo viac anotovaných tried:

 contextConfigLocation com.baeldung.contexts.config.RootApplicationConfig, com.baeldung.contexts.config.NormalWebAppConfig 

Alebo môžeme povedať kontextu, aby prehľadal jeden alebo viac balíkov:

 contextConfigLocation com.baeldung.bean.config 

A samozrejme, môžeme tieto dve možnosti kombinovať.

2.4. Programová konfigurácia pomocou Servlet 3.x

Verzia 3 rozhrania Servlet API sa konfigurovala prostredníctvom servera web.xml súbor úplne voliteľný. Knižnice môžu poskytovať svoje webové fragmenty, čo sú časti konfigurácie XML, ktoré môžu registrovať poslucháčov, filtre, servlety atď.

Používatelia majú tiež prístup k API, ktoré umožňuje programovo definovať každý prvok aplikácie založenej na servlete.

The jarný web modul využíva tieto funkcie a ponúka svoje API na registráciu komponentov aplikácie pri jej spustení.

Jaro prehľadáva triednu cestu aplikácie, či neobsahuje prípady org.springframework.web.WebApplicationInitializer trieda. Toto je rozhranie s jedinou metódou, void onStartup (ServletContext servletContext) vyvolá ServletException, ktorý je vyvolaný pri štarte aplikácie.

Poďme sa teraz pozrieť na to, ako môžeme túto funkciu použiť na vytvorenie rovnakých typov kontextov koreňových webových aplikácií, ktoré sme videli už skôr.

2.5. Pomocou Servletu 3.xa kontextu aplikácie XML

Začnime kontextom XML, rovnako ako v časti 2.2.

Vyššie uvedené implementujeme na začiatku metóda:

verejná trieda ApplicationInitializer implementuje WebApplicationInitializer {@Override public void onStartup (ServletContext servletContext) vyvolá ServletException {// ...}}

Poďme si implementáciu rozdeliť po riadkoch.

Najskôr vytvoríme koreňový kontext. Pretože chceme používať XML, musí to byť aplikačný kontext založený na XML a keďže sme vo webovom prostredí, musí implementovať WebApplicationContext tiež.

Prvý riadok je teda explicitnou verziou contextClass parameter, s ktorým sme sa už skôr stretli, s ktorým sa rozhodujeme, ktorú konkrétnu implementáciu kontextu použijeme:

XmlWebApplicationContext rootContext = nový XmlWebApplicationContext ();

Potom v druhom riadku povieme kontextu, odkiaľ sa majú načítať jeho definície fazule. Opäť setConfigLocations je programový analogický program contextConfigLocation parameter v web.xml:

rootContext.setConfigLocations ("/ WEB-INF / rootApplicationContext.xml");

Nakoniec vytvoríme a ContextLoaderListener s koreňovým kontextom a zaregistrujte ho v kontajneri servletu. Ako vidíme, ContextLoaderListener má vhodného konštruktora, ktorý berie a WebApplicationContext a sprístupňuje ju aplikácii:

servletContext.addListener (nový ContextLoaderListener (rootContext));

2.6. Používanie Servlet 3.xa aplikačný kontext Java

Ak chceme použiť kontext založený na anotáciách, mohli by sme zmeniť útržok kódu v predchádzajúcej časti, aby bol AnnotationConfigWebApplicationContext namiesto toho.

Pozrime sa však na špecializovanejší prístup k dosiahnutiu rovnakého výsledku.

The WebApplicationInitializer triedy, ktorú sme už videli skôr, je univerzálne rozhranie. Ukazuje sa, že Spring poskytuje niekoľko konkrétnejších implementácií vrátane abstraktnej triedy s názvom AbstractContextLoaderInitializer.

Jeho úlohou, ako už z názvu vyplýva, je vytvoriť a ContextLoaderListener a zaregistrujte ho v servletovom kontajneri.

Musíme mu iba povedať, ako vytvoriť koreňový kontext:

verejná trieda AnnotationsBasedApplicationInitializer rozširuje AbstractContextLoaderInitializer {@Override chránený WebApplicationContext createRootApplicationContext () {AnnotationConfigWebApplicationContext rootContext = nový AnnotationConfigWebApplicationContext) rootContext.register (RootApplicationConfig.class); návrat rootContext; }}

Tu vidíme, že už nie je potrebné registrovať ContextLoaderListener, čo nás ušetrí od trochu štandardného kódu.

Všimnite si tiež použitie Registrovať metóda, ktorá je špecifická pre AnnotationConfigWebApplicationContext namiesto všeobecnejších setConfigLocations: jeho vyvolaním môžeme zaregistrovať jednotlivca @ Konfigurácia anotované triedy kontextom, čím sa zabráni skenovaniu balíkov.

3. Kontexty servletu dispečera

Poďme sa teraz zamerať na iný typ kontextu aplikácie. Tentokrát budeme hovoriť o funkcii, ktorá je špecifická pre Spring MVC, a nie ako súčasť všeobecnej podpory webových aplikácií Spring.

Jarné aplikácie MVC majú nakonfigurovaný najmenej jeden dispečerský servlet (ale možno aj viacerých, o tom prípade si povieme neskôr). Toto je servlet, ktorý prijíma prichádzajúce požiadavky, odosiela ich do príslušnej metódy radiča a vracia zobrazenie.

Každý DispatcherServlet má súvisiaci kontext aplikácie. Fazuľa definovaná v takýchto kontextoch konfiguruje servlet a definuje objekty MVC, ako sú radiče a prekladače pohľadov.

Pozrime sa, ako najskôr nakonfigurovať kontext servletu. Neskôr sa pozrieme na niektoré podrobné informácie.

3.1. Použitím web.xml a kontext aplikácie XML

DispatcherServlet je zvyčajne deklarované v web.xml s menom a mapou:

 normal-webapp org.springframework.web.servlet.DispatcherServlet 1 normal-webapp / api / * 

Ak nie je uvedené inak, na určenie súboru XML, ktorý sa má načítať, sa použije názov servletu. V našom príklade použijeme súbor WEB-INF / normal-webapp-servlet.xml.

Môžeme tiež určiť jednu alebo viac ciest k súborom XML podobným spôsobom ako ContextLoaderListener:

 ... contextConfigLocation /WEB-INF/normal/*.xml 

3.2. Použitím web.xml a kontext aplikácií Java

Ak chceme použiť iný typ kontextu, postupujeme ako v prípade ContextLoaderListener, opäť. To znamená, že špecifikujeme a contextClass parameter spolu s vhodným contextConfigLocation:

 normal-webapp-annotations org.springframework.web.servlet.DispatcherServlet contextClass org.springframework.web.context.support.AnnotationConfigWebApplicationContext contextConfigLocation com.baeldung.contexts.config.NormalWebAppConfig 1 

3.3. Pomocou Servletu 3.xa kontextu aplikácie XML

Opäť sa pozrieme na dve rôzne metódy programového vyhlásenia a DispatcherServleta jeden použijeme na kontext XML a druhý na kontext Java.

Začnime teda s generikom WebApplicationInitializer a kontext aplikácie XML.

Ako sme už videli predtým, musíme implementovať na začiatku metóda. Tentokrát však tiež vytvoríme a zaregistrujeme servlet dispečera:

XmlWebApplicationContext normalWebAppContext = nový XmlWebApplicationContext (); normalWebAppContext.setConfigLocation ("/ WEB-INF / normal-webapp-servlet.xml"); ServletRegistration.Dynamic normal = servletContext.addServlet ("normal-webapp", nový DispatcherServlet (normalWebAppContext)); normal.setLoadOnStartup (1); normal.addMapping ("/ api / *");

Medzi vyššie uvedeným kódom a ekvivalentom môžeme ľahko nakresliť paralelu web.xml konfiguračné prvky.

3.4. Používanie Servlet 3.xa aplikačný kontext Java

Tentokrát nakonfigurujeme kontext založený na anotáciách pomocou špecializovanej implementácie WebApplicationInitializer: AbstractDispatcherServletInitializer.

Toto je abstraktná trieda, ktorá okrem vytvorenia kontextu koreňovej webovej aplikácie, ako sme už videli, umožňuje zaregistrovať jeden servlet dispečera s minimálnym štandardom:

@Override chránený WebApplicationContext createServletApplicationContext () {AnnotationConfigWebApplicationContext secureWebAppContext = nový AnnotationConfigWebApplicationContext (); secureWebAppContext.register (SecureWebAppConfig.class); vrátiť secureWebAppContext; } @Override chránený reťazec [] getServletMappings () {vrátiť nový reťazec [] {"/ s / api / *"}; }

Tu môžeme vidieť metódu na vytvorenie kontextu spojeného s servletom, presne tak, ako sme to už videli pre koreňový kontext. Máme tiež metódu na určenie mapovania servletu, ako v web.xml.

4. Kontext rodičov a detí

Doteraz sme videli dva hlavné typy kontextov: kontext koreňovej webovej aplikácie a kontexty servletu dispečera. Potom by sme mohli mať otázku: súvisia tieto kontexty?

Ukázalo sa, že áno, sú. V skutočnosti, koreňový kontext je rodičom každého kontextu servletu dispečera. Preto sú zrná definované v kontexte koreňovej webovej aplikácie viditeľné pre každý kontext servletu dispečera, ale nie naopak.

Typicky sa teda koreňový kontext používa na definovanie obslužných bôbov, zatiaľ čo kontext dispečera obsahuje tie bôby, ktoré súvisia konkrétne s MVC.

Všimnite si, že sme tiež videli spôsoby, ako programovo vytvoriť kontext servletu dispečera. Ak nastavíme manuálne jeho rodič, potom jar neprepíše naše rozhodnutie a táto časť už nebude platiť.

V jednoduchších aplikáciách MVC stačí mať jediný kontext asociovaný s jediným servletom dispečera. Nie sú potrebné príliš zložité riešenia!

Stále je vzťah rodič - dieťa užitočný, keď máme nakonfigurovaných viac servletov dispečera. Kedy by sme sa však mali namáhať, aby sme ich mali viac?

Všeobecne, deklarujeme viac servletov dispečera keď potrebujeme viac sád konfigurácie MVC. Napríklad môžeme mať REST API popri tradičnej aplikácii MVC alebo nezabezpečenej a zabezpečenej sekcii webu:

Poznámka: keď predĺžime AbstractDispatcherServletInitializer (pozri časť 3.4), zaregistrujeme kontext koreňovej webovej aplikácie aj jeden servlet dispečera.

Ak teda chceme mať viac ako jeden servlet, potrebujeme ich viac AbstractDispatcherServletInitializer implementácie. Môžeme však definovať iba jeden koreňový kontext, inak sa aplikácia nespustí.

Našťastie createRootApplicationContext metóda sa môže vrátiť nulový. Môžeme teda mať jeden AbstractContextLoaderInitializer a veľa AbstractDispatcherServletInitializer implementácie, ktoré nevytvárajú koreňový kontext. V takomto scenári je vhodné objednať inicializátory s @Objednať výslovne.

Pamätajte tiež na to AbstractDispatcherServletInitializer zaregistruje servlet pod menom (dispečer) a samozrejme nemôžeme mať viac servletov s rovnakým názvom. Musíme to teda prekonať getServletName:

@Override chránený reťazec getServletName () {return "another-dispečer"; }

5. A Kontext rodič a dieťa Príklad

Predpokladajme, že máme dve oblasti našej aplikácie, napríklad verejnú, ktorá je svetovo prístupná a zabezpečenú, s rôznymi konfiguráciami MVC. Tu iba definujeme dva radiče, ktoré vydávajú inú správu.

Predpokladajme tiež, že niektorí kontrolóri potrebujú službu, ktorá obsahuje značné zdroje; všadeprítomným prípadom je vytrvalosť. Potom budeme chcieť inštanciu tejto služby vytvoriť iba raz, aby sme zabránili zdvojnásobeniu využívania zdrojov a pretože veríme v princíp Neopakujte sa!

Poďme teraz k príkladu.

5.1. Zdieľaná služba

V našom príklade ahoj vo svete sme sa namiesto vytrvalosti uspokojili s jednoduchšou pozdravnou službou:

balíček com.baeldung.contexts.services; @Service verejná trieda GreeterService {@Resource súkromné ​​Pozdrav pozdrav; public String greet () {návrat pozdrav.getMessage (); }}

Službu deklarujeme v kontexte koreňovej webovej aplikácie pomocou skenovania komponentov:

@Configuration @ComponentScan (basePackages = {"com.baeldung.contexts.services"}) verejná trieda RootApplicationConfig {// ...}

Namiesto toho môžeme uprednostniť XML:

5.2. Kontrolóri

Poďme definovať dva jednoduché radiče, ktoré používajú službu a vydávajú pozdrav:

balík com.baeldung.contexts.normal; @Controller verejná trieda HelloWorldController {@Autowired private GreeterService greeterService; @RequestMapping (path = "/ welcome") public ModelAndView helloWorld () {String message = "

Normálne „+ greeterService.greet () +“

"; vrátiť nový ModelAndView (" Vitajte "," správa ", správa);}} //" Zabezpečený "balík radiča com.baeldung.contexts.secure; reťazec message ="

Zabezpečiť „+ greeterService.greet () +“

";

Ako vidíme, radiče ležia v dvoch rôznych balíkoch a tlačia rôzne správy: jeden hovorí „normálny“, druhý „zabezpečený“.

5.3. Kontexty servletu dispečera

Ako sme už povedali, budeme mať dva rôzne kontexty servletov dispečera, jeden pre každý radič. Definujme ich teda v Jave:

// Normálny kontext @Configuration @EnableWebMvc @ComponentScan (basePackages = {"com.baeldung.contexts.normal"}) verejná trieda NormalWebAppConfig implementuje WebMvcConfigurer {// ...} // "Zabezpečený" kontext @Configuration @EnableWebMvc @ComponentScan ( basePackages = {"com.baeldung.contexts.secure"}) verejná trieda SecureWebAppConfig implementuje WebMvcConfigurer {// ...}

Alebo, ak chceme, v XML:

5.4. Dávať to všetko dokopy

Teraz, keď máme všetky kúsky, stačí povedať Spring, aby ich zapojila. Pripomeňme, že musíme načítať koreňový kontext a definovať dva servlety dispečera. Aj keď sme už videli niekoľko spôsobov, ako to dosiahnuť, zameriame sa teraz na dva scenáre, Java a XML. Začnime s Java.

Definujeme AbstractContextLoaderInitializer načítať koreňový kontext:

@Override chránený WebApplicationContext createRootApplicationContext () {AnnotationConfigWebApplicationContext rootContext = nový AnnotationConfigWebApplicationContext (); rootContext.register (RootApplicationConfig.class); návrat rootContext; } 

Potom musíme vytvoriť dva servlety, a tak definujeme dve podtriedy AbstractDispatcherServletInitializer. Po prvé, „normálny“:

@Override chránený WebApplicationContext createServletApplicationContext () {AnnotationConfigWebApplicationContext normalWebAppContext = nový AnnotationConfigWebApplicationContext (); normalWebAppContext.register (NormalWebAppConfig.class); vrátiť normalWebAppContext; } @Override chránený reťazec [] getServletMappings () {vrátiť nový reťazec [] {"/ api / *"}; } @Override chránený reťazec getServletName () {návrat "normálny dispečer"; } 

Potom „zabezpečený“, ktorý načíta iný kontext a je namapovaný na inú cestu:

@Override chránený WebApplicationContext createServletApplicationContext () {AnnotationConfigWebApplicationContext secureWebAppContext = nový AnnotationConfigWebApplicationContext (); secureWebAppContext.register (SecureWebAppConfig.class); vrátiť secureWebAppContext; } @Override chránený reťazec [] getServletMappings () {vrátiť nový reťazec [] {"/ s / api / *"}; } @Override chránený reťazec getServletName () {návrat "secure-dispečer"; }

A sme hotoví! Práve sme aplikovali to, čoho sme sa dotkli v predchádzajúcich častiach.

To isté môžeme urobiť s web.xml, opäť len kombináciou častí, o ktorých sme doteraz hovorili.

Definujte kontext koreňovej aplikácie:

  org.springframework.web.context.ContextLoaderListener 

„Normálny“ kontext dispečera:

 normal-webapp org.springframework.web.servlet.DispatcherServlet 1 normal-webapp / api / * 

A nakoniec „bezpečný“ kontext:

 secure-webapp org.springframework.web.servlet.DispatcherServlet 1 secure-webapp / s / api / * 

6. Kombinácia viacerých kontextov

Existujú aj iné spôsoby, ako kombinovať viac umiestnení konfigurácie ako rodič-dieťa, rozdeliť veľké kontexty a lepšie oddeliť rôzne záujmy. Jeden príklad sme už videli: keď špecifikujeme contextConfigLocation s viacerými cestami alebo balíčkami vytvára Spring jeden kontext kombináciou všetkých definícií fazule, akoby boli napísané v jednom súbore XML alebo triede Java.

Podobný efekt však môžeme dosiahnuť inými prostriedkami a dokonca môžeme spoločne použiť rôzne prístupy. Poďme preskúmať naše možnosti.

Jednou z možností je skenovanie komponentov, ktoré vysvetľujeme v inom článku.

6.1. Importovanie kontextu do iného

Alternatívne môžeme mať definíciu kontextu, ktorá importuje inú. V závislosti od scenára máme rôzne druhy dovozu.

Importuje sa a @ Konfigurácia trieda v Jave:

@Configuration @Import (SomeOtherConfiguration.class) konfigurácia verejnej triedy {...}

Načítanie iného typu zdroja, napríklad definície kontextu XML, v Jave:

@Configuration @ImportResource ("classpath: basicConfigForPropertiesTwo.xml") konfigurácia verejnej triedy {...}

Nakoniec vrátane súboru XML v inom:

Existuje teda veľa spôsobov, ako organizovať služby, komponenty, ovládače atď., Ktoré spolupracujú na vytvorení našej úžasnej aplikácie. A pekné je, že IDE im všetkým rozumejú!

7. Spring Boot Web Applications

Spring Boot automaticky konfiguruje komponenty aplikácie, takže všeobecne nie je potrebné uvažovať o ich usporiadaní.

Napriek tomu Boot pod kapotou využíva funkcie Spring, vrátane tých, ktoré sme doteraz videli. Pozrime sa na niekoľko pozoruhodných rozdielov.

Webové aplikácie Spring Boot bežiace vo vloženom kontajneri nespúšťajú žiadne WebApplicationInitializer zámerne.

Ak je to potrebné, môžeme rovnakú logiku zapísať do a SpringBootServletInitializer alebo a ServletContextInitializer namiesto toho v závislosti od zvolenej stratégie nasadenia.

Pre pridanie servletov, filtrov a poslucháčov, ako sú vidieť v tomto článku, to však nie je potrebné. V skutočnosti Spring Boot automaticky zaregistruje každú fazuľu súvisiacu s servletom do kontajnera:

@Bean public Servlet myServlet () {...}

Takto definované objekty sa mapujú podľa konvencií: filtre sa automaticky mapujú na / *, to znamená na každú požiadavku. Ak zaregistrujeme jeden servlet, je namapovaný na /, inak je každý servlet namapovaný na jeho názov fazule.

Ak vyššie uvedené konvencie pre nás nefungujú, môžeme definovať a FilterRegistrationBean, ServletRegistrationBean, alebo ServletListenerRegistrationBean namiesto toho. Tieto triedy nám umožňujú kontrolovať jemné aspekty registrácie.

8. Závery

V tomto článku sme poskytli podrobný pohľad na rôzne možnosti dostupné pre štruktúru a organizáciu webovej aplikácie Spring.

Vynechali sme niektoré funkcie, najmä podporu zdieľaného kontextu v podnikových aplikáciách, ktorý v čase písania tohto článku ešte na jar 5 chýba.

Implementáciu všetkých týchto príkladov a útržkov kódu nájdete v projekte GitHub.


$config[zx-auto] not found$config[zx-overlay] not found