Vårens nettkontekster

1. Introduksjon

Når du bruker Spring i et webapplikasjon, har vi flere muligheter for å organisere applikasjonskontekstene som kobler det hele sammen.

I denne artikkelen skal vi analysere og forklare de vanligste alternativene som Spring tilbyr.

2. Root Web Application Context

Hver vår-webapp har en tilknyttet applikasjonskontekst som er knyttet til livssyklusen: rotnettapplikasjonskonteksten.

Dette er en gammel funksjon som er forut for Spring Web MVC, så den er ikke bundet spesifikt til noen web-rammeteknologi.

Konteksten startes når applikasjonen starter, og den blir ødelagt når den stopper, takket være en servlet-kontekstlytter. De vanligste typene av sammenhenger kan også oppdateres ved kjøretid, men ikke alle ApplicationContext implementeringer har denne muligheten.

Konteksten i et webapplikasjon er alltid en forekomst av WebApplicationContext. Det er et grensesnitt som utvider seg ApplicationContext med en kontrakt for tilgang til ServletContext.

Uansett, applikasjoner bør vanligvis ikke være bekymret for disse implementeringsdetaljene: rotnettapplikasjonskonteksten er rett og slett et sentralisert sted å definere delte bønner.

2.1. De ContextLoaderListener

Rotnettapplikasjonskonteksten beskrevet i forrige avsnitt administreres av en lytter i klassen org.springframework.web.context.ContextLoaderListener, som er en del av vår-nett modul.

Som standard laster lytteren inn en XML-applikasjonskontekst fra /WEB-INF/applicationContext.xml. Disse standardene kan imidlertid endres. Vi kan for eksempel bruke Java-merknader i stedet for XML.

Vi kan konfigurere denne lytteren enten i webapp-deskriptoren (web.xml fil) eller programmatisk i Servlet 3.x-miljøer.

I de følgende avsnittene vil vi se på hvert av disse alternativene i detalj.

2.2. Ved hjelp av web.xml og en XML-applikasjonskontekst

Når du bruker web.xml, konfigurerer vi lytteren som vanlig:

  org.springframework.web.context.ContextLoaderListener 

Vi kan spesifisere en alternativ plassering av XML-kontekstkonfigurasjonen med contextConfigLocation parameter:

 contextConfigLocation /WEB-INF/rootApplicationContext.xml 

Eller mer enn ett sted, atskilt med komma:

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

Vi kan til og med bruke mønstre:

 contextConfigLocation /WEB-INF/*-context.xml 

I alle fall, bare en kontekst er definert, ved å kombinere alle bønnedefinisjonene som er lastet fra de angitte stedene.

2.3. Ved hjelp av web.xml og en Java Application Context

Vi kan også spesifisere andre typer sammenhenger i tillegg til den standard XML-baserte. La oss for eksempel se hvordan du bruker Java-merknadskonfigurasjon i stedet.

Vi bruker contextClass parameter for å fortelle lytteren hvilken type kontekst som skal startes:

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

Hver type kontekst kan ha en standard konfigurasjonsplassering. I vårt tilfelle er AnnotationConfigWebApplicationContext har ikke en, så vi må gi den.

Vi kan dermed liste opp en eller flere merkede klasser:

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

Eller vi kan fortelle sammenhengen å skanne en eller flere pakker:

 contextConfigLocation com.baeldung.bean.config 

Og selvfølgelig kan vi blande og matche de to alternativene.

2.4. Programmatisk konfigurasjon med Servlet 3.x

Versjon 3 av Servlet API har konfigurert gjennom web.xml filen er helt valgfri. Biblioteker kan tilby sine webfragmenter, som er deler av XML-konfigurasjon som kan registrere lyttere, filtre, servlets og så videre.

Dessuten har brukere tilgang til et API som gjør det mulig å definere programmatisk hvert element i en servletbasert applikasjon.

De vår-nett modulen bruker disse funksjonene og tilbyr sin API for å registrere komponenter i applikasjonen når den starter.

Spring skanner applikasjonens klassesti for forekomster av org.springframework.web.WebApplicationInitializer klasse. Dette er et grensesnitt med en enkelt metode, ugyldig onStartup (ServletContext servletContext) kaster ServletException, som blir påkalt ved oppstart av applikasjonen.

La oss nå se på hvordan vi kan bruke dette anlegget til å lage de samme typene av rotnettapplikasjonskontekster som vi har sett tidligere.

2.5. Bruker Servlet 3.x og en XML-applikasjonskontekst

La oss starte med en XML-kontekst, akkurat som i avsnitt 2.2.

Vi implementerer det ovennevnte ved oppstart metode:

offentlig klasse ApplicationInitializer implementerer WebApplicationInitializer {@Override public void onStartup (ServletContext servletContext) kaster ServletException {// ...}}

La oss bryte gjennomføringen linje for linje.

Vi oppretter først en rotkontekst. Siden vi ønsker å bruke XML, må det være en XML-basert applikasjonskontekst, og siden vi er i et webmiljø, må det implementeres WebApplicationContext også.

Første linje er altså den eksplisitte versjonen av contextClass parameter som vi har opplevd tidligere, som vi bestemmer hvilken spesifikk kontekstimplementering vi skal bruke:

XmlWebApplicationContext rootContext = ny XmlWebApplicationContext ();

Så, i andre linje, forteller vi konteksten hvor du skal laste bønnedefinisjonene fra. En gang til, setConfigLocations er den programmatiske analoge av contextConfigLocation parameter i web.xml:

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

Til slutt lager vi en ContextLoaderListener med rotkonteksten og registrer den med servletbeholderen. Som vi kan se, ContextLoaderListener har en passende konstruktør som tar en WebApplicationContext og gjør det tilgjengelig for applikasjonen:

servletContext.addListener (ny ContextLoaderListener (rootContext));

2.6. Bruker Servlet 3.x og en Java Application Context

Hvis vi vil bruke en kommentarbasert kontekst, kan vi endre kodebiten i forrige seksjon for å gjøre det øyeblikkelig AnnotationConfigWebApplicationContext i stedet.

La oss imidlertid se en mer spesialisert tilnærming for å oppnå det samme resultatet.

De WebApplicationInitializer klasse som vi har sett tidligere er et generelt grensesnitt. Det viser seg at Spring gir noen mer spesifikke implementeringer, inkludert en abstrakt klasse kalt AbstractContextLoaderInitializer.

Dens jobb, som navnet tilsier, er å lage en ContextLoaderListener og registrer den med servletbeholderen.

Vi må bare fortelle det hvordan man bygger rotkonteksten:

offentlig klasse AnnotationsBasedApplicationInitializer utvider AbstractContextLoaderInitializer {@Override-beskyttet WebApplicationContext createRootApplicationContext () {AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext (); rootContext.register (RootApplicationConfig.class); return rootContext; }}

Her kan vi se at vi ikke lenger trenger å registrere ContextLoaderListener, som sparer oss for en liten bit av kokerplatekoden.

Legg også merke til bruken av registrere metode som er spesifikk for AnnotationConfigWebApplicationContext i stedet for det mer generiske setConfigLocations: ved å påkalle det, kan vi registrere enkeltpersoner @Konfigurasjon kommenterte klasser med konteksten, og dermed unngås pakkeskanning.

3. Dispatcher Servlet-sammenhenger

La oss nå fokusere på en annen type applikasjonskontekst. Denne gangen vil vi referere til en funksjon som er spesifikk for Spring MVC, i stedet for en del av Spring generiske webapplikasjonsstøtte.

Spring MVC-applikasjoner har minst en Dispatcher Servlet konfigurert (men muligens mer enn en, vi vil snakke om den saken senere). Dette er servletten som mottar innkommende forespørsler, sender dem til riktig kontrollermetode og returnerer visningen.

Hver DispatcherServlet har en tilknyttet applikasjonskontekst. Bønner som er definert i slike sammenhenger konfigurerer servletten og definerer MVC-objekter som kontrollere og visningsoppløsere.

La oss se hvordan du konfigurerer servlets kontekst først. Vi vil se på noen grundige detaljer senere.

3.1. Ved hjelp av web.xml og en XML-applikasjonskontekst

DispatcherServlet er vanligvis erklært i web.xml med navn og kartlegging:

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

Hvis ikke annet er spesifisert, brukes navnet på servletten til å bestemme XML-filen som skal lastes inn. I vårt eksempel bruker vi filen WEB-INF / normal-webapp-servlet.xml.

Vi kan også spesifisere en eller flere baner til XML-filer, på en lignende måte som ContextLoaderListener:

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

3.2. Ved hjelp av web.xml og en Java Application Context

Når vi ønsker å bruke en annen type kontekst, går vi frem som med ContextLoaderListener, en gang til. Det vil si at vi spesifiserer en contextClass parameter sammen med en passende contextConfigLocation:

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

3.3. Bruker Servlet 3.x og en XML-applikasjonskontekst

Igjen vil vi se på to forskjellige metoder for å programmere en DispatcherServlet, og vi bruker den ene til en XML-kontekst og den andre for en Java-kontekst.

Så la oss starte med en generisk WebApplicationInitializer og en XML-applikasjonskontekst.

Som vi har sett tidligere, må vi implementere ved oppstart metode. Denne gangen oppretter og registrerer vi imidlertid også en avsenderservlet:

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

Vi kan enkelt trekke en parallell mellom ovennevnte kode og tilsvarende web.xml konfigurasjonselementer.

3.4. Bruker Servlet 3.x og en Java Application Context

Denne gangen konfigurerer vi en kommentarbasert kontekst ved hjelp av en spesialisert implementering av WebApplicationInitializer: AbstractDispatcherServletInitializer.

Det er en abstrakt klasse som, i tillegg til å lage en rotnettapplikasjonskontekst som tidligere sett, tillater oss å registrere en dispatcherservlet med minimum kjeleplate:

@ Override beskyttet WebApplicationContext createServletApplicationContext () {AnnotationConfigWebApplicationContext secureWebAppContext = new AnnotationConfigWebApplicationContext (); secureWebAppContext.register (SecureWebAppConfig.class); returner secureWebAppContext; } @ Override-beskyttet streng [] getServletMappings () {returner ny streng [] {"/ s / api / *"}; }

Her kan vi se en metode for å lage konteksten assosiert med servletten, akkurat som vi har sett før for rotkonteksten. Vi har også en metode for å spesifisere servlets tilordninger, som i web.xml.

4. Foreldre- og barnsammenheng

Så langt har vi sett to hovedtyper av sammenhenger: rotnettapplikasjonskonteksten og ekspedisjonsservletsammenheng. Så kan vi ha et spørsmål: er disse sammenhenger relatert?

Det viser seg at ja, det er de. Faktisk, rotkonteksten er overordnet til hver senderservlet-kontekst. Dermed er bønner som er definert i rotnettapplikasjonssammenheng synlige for hver senderservletsammenheng, men ikke omvendt.

Så, vanligvis brukes rotkonteksten til å definere tjenestebønner, mens ekspedisjonskonteksten inneholder de bønner som er spesifikt relatert til MVC.

Vær oppmerksom på at vi også har sett måter å lage dispatcherservlet-konteksten programmatisk på. Hvis vi setter foreldrene manuelt, overstyrer ikke Spring vår beslutning, og denne delen gjelder ikke lenger.

I enklere MVC-applikasjoner er det tilstrekkelig å ha en enkelt kontekst knyttet til den eneste senderen. Det er ikke behov for altfor komplekse løsninger!

Forholdet mellom foreldre og barn blir likevel nyttig når vi har konfigurert flere senderservlets. Men når skal vi gidder å ha mer enn en?

Generelt, vi erklærer flere senderservlets når vi trenger flere sett med MVC-konfigurasjon. For eksempel kan vi ha en REST API ved siden av et tradisjonelt MVC-program eller en usikret og sikker del av et nettsted:

Merk: når vi utvider AbstractDispatcherServletInitializer (se avsnitt 3.4), registrerer vi både en rotnettapplikasjonskontekst og en enkelt senderservlet.

Så hvis vi vil ha mer enn en servlet, trenger vi flere AbstractDispatcherServletInitializer implementeringer. Vi kan imidlertid bare definere en rotkontekst, ellers starter ikke applikasjonen.

Heldigvis, den createRootApplicationContext metoden kan komme tilbake null. Dermed kan vi ha en AbstractContextLoaderInitializer og mange AbstractDispatcherServletInitializer implementeringer som ikke skaper rotkontekst. I et slikt scenario anbefales det å bestille initialiseringene med @Rekkefølge eksplisitt.

Vær også oppmerksom på at AbstractDispatcherServletInitializer registrerer servletten under et gitt navn (avsender) og selvfølgelig kan vi ikke ha flere servlets med samme navn. Så vi må overstyre getServletName:

@ Override-beskyttet streng getServletName () {returner "en annen dispatcher"; }

5. A Foreldre og barns kontekst Eksempel

Anta at vi har to områder av applikasjonen vår, for eksempel en offentlig som er tilgjengelig for hele verden og en sikret, med forskjellige MVC-konfigurasjoner. Her definerer vi bare to kontrollere som sender en annen melding.

Anta også at noen av kontrollerne trenger en tjeneste som har betydelige ressurser; en allestedsnærværende sak er utholdenhet. Deretter vil vi bare starte denne tjenesten en gang, for å unngå å doble ressursbruken, og fordi vi tror på ikke gjenta deg selv-prinsippet!

La oss nå gå videre med eksemplet.

5.1. Den delte tjenesten

I vårt hei-verdenseksempel avgjorde vi en enklere greeter-tjeneste i stedet for utholdenhet:

pakke com.baeldung.contexts.tjenester; @Service offentlig klasse GreeterService {@Ressurs privat hilsenhilsen; public String greet () {return greeting.getMessage (); }}

Vi erklærer tjenesten i rotnettprogrammets kontekst ved hjelp av komponentskanning:

@Configuration @ComponentScan (basePackages = {"com.baeldung.contexts.services"}) offentlig klasse RootApplicationConfig {// ...}

Vi foretrekker kanskje XML i stedet:

5.2. Kontrollerne

La oss definere to enkle kontrollere som bruker tjenesten og sender en hilsen:

pakke com.baeldung.contexts.normal; @Controller offentlig klasse HelloWorldController {@Autowired private GreeterService greeterService; @RequestMapping (path = "/ welcome") offentlig ModelAndView helloWorld () {Strengmelding = "

Normal "+ greeterService.greet () +"

"; returner ny ModelAndView (" velkommen "," melding ", melding);}} //" Sikker "Controller-pakke com.baeldung.contexts.secure; Strengmelding ="

Sikker "+ greeterService.greet () +"

";

Som vi ser, ligger kontrollerne i to forskjellige pakker og skriver ut forskjellige meldinger: den ene sier "normal", den andre "sikker".

5.3. Dispatcher Servlet-sammenhenger

Som vi sa tidligere, skal vi ha to forskjellige dispatcherservlet-kontekster, en for hver kontroller. Så la oss definere dem i Java:

// Normal kontekst @Configuration @EnableWebMvc @ComponentScan (basePackages = {"com.baeldung.contexts.normal"}) offentlig klasse NormalWebAppConfig implementerer WebMvcConfigurer {// ...} // "Sikker" kontekst @Configuration @EnableWebMvc @ComponentScan basePackages = {"com.baeldung.contexts.secure"}) offentlig klasse SecureWebAppConfig implementerer WebMvcConfigurer {// ...}

Eller, hvis vi foretrekker det, i XML:

5.4. Sette alt sammen

Nå som vi har alle delene, trenger vi bare å be Spring om å koble dem opp. Husk at vi trenger å laste rotkonteksten og definere de to dispatcher-servlets. Selv om vi har sett flere måter å gjøre det på, fokuserer vi nå på to scenarier, en Java og en XML. La oss starte med Java.

Vi definerer en AbstractContextLoaderInitializer for å laste rotkonteksten:

@ Override beskyttet WebApplicationContext createRootApplicationContext () {AnnotationConfigWebApplicationContext rootContext = ny AnnotationConfigWebApplicationContext (); rootContext.register (RootApplicationConfig.class); return rootContext; } 

Deretter må vi opprette de to servlets, slik at vi definerer to underklasser av AbstractDispatcherServletInitializer. For det første den “normale”:

@ Override beskyttet WebApplicationContext createServletApplicationContext () {AnnotationConfigWebApplicationContext normalWebAppContext = new AnnotationConfigWebApplicationContext (); normalWebAppContext.register (NormalWebAppConfig.class); returner normalWebAppContext; } @ Override-beskyttet streng [] getServletMappings () {returner ny streng [] {"/ api / *"}; } @ Override-beskyttet streng getServletName () {return "normal-dispatcher"; } 

Deretter den "sikre", som laster inn en annen kontekst og er kartlagt til en annen vei:

@ Override beskyttet WebApplicationContext createServletApplicationContext () {AnnotationConfigWebApplicationContext secureWebAppContext = new AnnotationConfigWebApplicationContext (); secureWebAppContext.register (SecureWebAppConfig.class); returner secureWebAppContext; } @ Override-beskyttet streng [] getServletMappings () {returner ny streng [] {"/ s / api / *"}; } @ Override-beskyttet streng getServletName () {return "secure-dispatcher"; }

Og vi er ferdige! Vi har nettopp brukt det vi berørte i forrige seksjoner.

Vi kan gjøre det samme med web.xml, bare ved å kombinere brikkene vi har diskutert så langt.

Definere en rotapplikasjonskontekst:

  org.springframework.web.context.ContextLoaderListener 

En "normal" kontekst:

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

Og til slutt en "sikker" kontekst:

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

6. Kombinere flere sammenhenger

Det er andre måter enn foreldre-barn å kombinere flere konfigurasjonssteder, å splitte store sammenhenger og bedre skille forskjellige bekymringer. Vi har allerede sett ett eksempel: når vi spesifiserer contextConfigLocation med flere baner eller pakker, bygger Spring en enkelt kontekst ved å kombinere alle bønnedefinisjonene, som om de ble skrevet i en enkelt XML-fil eller Java-klasse, i rekkefølge.

Imidlertid kan vi oppnå en lignende effekt med andre midler og til og med bruke forskjellige tilnærminger sammen. La oss undersøke alternativene våre.

En mulighet er komponentskanning, som vi forklarer i en annen artikkel.

6.1. Importere en kontekst til en annen

Alternativt kan vi få en kontekstdefinisjon til å importere en annen. Avhengig av scenariet har vi forskjellige typer import.

Importerer en @Konfigurasjon klasse i Java:

@Configuration @Import (SomeOtherConfiguration.class) offentlig klasse Config {...}

Laster inn en annen type ressurs, for eksempel en XML-kontekstdefinisjon, i Java:

@Configuration @ImportResource ("classpath: basicConfigForPropertiesTwo.xml") offentlig klasse Config {...}

Til slutt, inkludert en XML-fil i en annen:

Dermed har vi mange måter å organisere tjenestene, komponentene, kontrollere, etc., som samarbeider for å lage vår fantastiske applikasjon. Og det fine er at IDE forstår dem alle!

7. Spring Boot Web Applications

Spring Boot konfigurerer automatisk komponentene i applikasjonen, så generelt er det mindre behov for å tenke på hvordan de skal organiseres.

Fortsatt, under panseret, bruker Boot vårfunksjoner, inkludert de vi har sett så langt. La oss se et par bemerkelsesverdige forskjeller.

Spring Boot-webapplikasjoner som kjører i en innebygd container, kjører ikke noen WebApplicationInitializer av design.

Skulle det være nødvendig, kan vi skrive den samme logikken i a SpringBootServletInitializer eller a ServletContextInitializer i stedet, avhengig av valgt distribusjonsstrategi.

For å legge til servlets, filtre og lyttere som vist i denne artikkelen, er det imidlertid ikke nødvendig å gjøre det. Faktisk registrerer Spring Boot automatisk alle servletrelaterte bønner i containeren:

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

Objektene som er definert blir kartlagt i henhold til konvensjoner: filtre blir automatisk kartlagt til / *, det vil si til hver forespørsel. Hvis vi registrerer en enkelt servlet, blir den kartlagt til /, ellers blir hver servlet tilordnet bønnenavnet.

Hvis de ovennevnte konvensjonene ikke fungerer for oss, kan vi definere en FilterRegistrationBean, ServletRegistreringBønne, eller ServletListenerRegistrationBean i stedet. Disse klassene lar oss kontrollere de fine sidene ved registreringen.

8. Konklusjoner

I denne artikkelen har vi gitt en grundig oversikt over de forskjellige alternativene som er tilgjengelige for å strukturere og organisere en vår-webapplikasjon.

Vi har utelatt noen funksjoner, særlig støtten til en delt kontekst i bedriftsapplikasjoner, som i skrivende stund fortsatt mangler fra vår 5.

Implementeringen av alle disse eksemplene og kodebiter finner du i GitHub-prosjektet.


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