Een inleiding tot de Spring DispatcherServlet

1. Inleiding

Simpel gezegd, in de Voorste controller ontwerp patroon, een enkele controller is verantwoordelijk voor het aansturen van inkomende HttpRequests naar alle andere controllers en handlers van een applicatie.

Lente DispatcherServlet implementeert dit patroon en is daarom verantwoordelijk voor het correct coördineren van het HttpRequests aan hun rechterhandlers.

In dit artikel zullen we dat doen onderzoek de lente DispatcherServlet verzoek verwerkingswerkstroom en hoe u verschillende interfaces implementeert die aan deze workflow deelnemen.

2. DispatcherServlet Verzoek om verwerking

In wezen een DispatcherServlet behandelt een inkomend HttpRequest, delegeert het verzoek en verwerkt dat verzoek volgens het geconfigureerde HandlerAdapter interfaces die zijn geïmplementeerd in de Spring-applicatie, samen met bijbehorende annotaties die handlers, controller-eindpunten en responsobjecten specificeren.

Laten we dieper ingaan op hoe a DispatcherServlet verwerkt een component:

  • de WebApplicationContext geassocieerd met een DispatcherServlet onder de sleutel DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE wordt gezocht naar en beschikbaar gesteld aan alle elementen van het proces
  • De DispatcherServlet vindt alle implementaties van de HandlerAdapter interface geconfigureerd voor uw coördinator met getHandler () - elke gevonden en geconfigureerde implementatie behandelt het verzoek via handvat() door de rest van het proces
  • de LocaleResolver is optioneel gebonden aan het verzoek om elementen in het proces in staat te stellen de landinstelling op te lossen
  • de ThemeResolver is optioneel gebonden aan het verzoek om elementen, zoals weergaven, te laten bepalen welk thema moet worden gebruikt
  • als een MultipartResolver is gespecificeerd, wordt het verzoek geïnspecteerd MultipartFiles - alle gevonden zijn verpakt in een MultipartHttpServletRequest voor verdere verwerking
  • HandlerExceptionResolver implementaties gedeclareerd in het WebApplicationContext pikt uitzonderingen op die worden gegenereerd tijdens het verwerken van het verzoek

U kunt meer te weten komen over alle manieren om u te registreren en een DispatcherServlet hier.

3. HandlerAdapter Interfaces

De HandlerAdapter interface vergemakkelijkt het gebruik van controllers, servlets, HttpRequests, en HTTP-paden door verschillende specifieke interfaces. De HandlerAdapter interface speelt dus een essentiële rol tijdens de vele fasen van het DispatcherServlet verzoek verwerkingswerkstroom.

Ten eerste elk HandlerAdapter implementatie wordt in de HandlerExecutionChain van uw coördinator getHandler () methode. Vervolgens elk van die implementaties handvat() de HttpServletRequest object naarmate de uitvoeringsketen vordert.

In de volgende secties zullen we enkele van de belangrijkste en meest gebruikte onderzoeken HandlerAdapters in meer detail.

3.1. Toewijzingen

Om toewijzingen te begrijpen, moeten we eerst kijken hoe we controllers kunnen annoteren, aangezien controllers zo essentieel zijn voor het HandlerMapping koppel.

De SimpleControllerHandlerAdapter maakt de implementatie van een controller expliciet mogelijk zonder een @Controller annotatie.

De RequestMappingHandlerAdapter ondersteunt methoden die zijn geannoteerd met de @RequestMapping annotatie.

We zullen ons concentreren op de @Controller annotatie hier, maar een nuttige bron met verschillende voorbeelden met behulp van de SimpleControllerHandlerAdapter is ook beschikbaar.

De @RequestMapping annotatie stelt het specifieke eindpunt in waarop een handler beschikbaar zal zijn binnen de WebApplicationContext ermee geassocieerd.

Laten we eens kijken naar een voorbeeld van een Controller dat de ‘/ Gebruiker / voorbeeld ' eindpunt:

@Controller @RequestMapping ("/ gebruiker") @ResponseBody openbare klasse UserController {@GetMapping ("/ voorbeeld") openbare gebruiker fetchUserExample () {// ...}}

De paden die zijn opgegeven door het @RequestMapping annotaties worden intern beheerd via de HandlerMapping koppel.

De URL-structuur is natuurlijk relatief ten opzichte van de DispatcherServlet zelf - en bepaald door de servlet-mapping.

Dus als het DispatcherServlet wordt toegewezen aan ‘/ ', dan worden alle toewijzingen gedekt door die toewijzing.

Als de servlet-mapping echter ‘/ dispatcher‘In plaats daarvan, dan een @RequestMapping annotaties zullen relatief zijn ten opzichte van die root-URL.

Onthoud dat ‘/ 'niet hetzelfde is als‘ / *' voor servlet-toewijzingen! ‘/ 'Is de standaardtoewijzing en stelt alle URL's bloot aan het verantwoordelijkheidsgebied van de verzender.

‘/ * 'Is voor veel nieuwere Spring-ontwikkelaars verwarrend. Het specificeert niet dat alle paden met dezelfde URL-context onder de verantwoordelijkheid van de verzender vallen. In plaats daarvan overschrijft en negeert het de andere toewijzingen van de dispatcher. Dus ‘/ example 'wordt weergegeven als een 404!

Om die reden, ‘/ * 'Mag niet worden gebruikt, behalve in zeer beperkte omstandigheden (zoals het configureren van een filter).

3.2. Afhandeling van HTTP-verzoeken

De kernverantwoordelijkheid van a DispatcherServlet is om inkomend te verzenden HttpRequests naar de juiste handlers gespecificeerd met de @Controller of @RestController annotaties.

Als een kanttekening, het belangrijkste verschil tussen @Controller en @RestController is hoe het antwoord wordt gegenereerd - de @RestController definieert ook @ResponseBody standaard.

Een artikel waarin we veel dieper ingaan op de controllers van Spring is hier te vinden.

3.3. De ViewResolver Koppel

EEN ViewResolver is bevestigd aan een DispatcherServlet als een configuratie-instelling op een ApplicationContext voorwerp.

EEN ViewResolver bepaalt zowel wat voor soort weergaven door de coördinator worden bediend als van waar ze worden bediend.

Hier is een voorbeeldconfiguratie die we in ons AppConfig voor het renderen van JSP-pagina's:

@Configuration @EnableWebMvc @ComponentScan ("com.baeldung.springdispatcherservlet") openbare klasse AppConfig implementeert WebMvcConfigurer {@Bean openbare UrlBasedViewResolver viewResolver () {UrlBasedViewResolver resolver = nieuwe UrlBasedViewResolver = nieuwe UrlBasedViewResolver; resolver.setPrefix ("/ WEB-INF / view /"); resolver.setSuffix (". jsp"); resolver.setViewClass (JstlView.class); return resolver; }}

Heel eenvoudig! Dit bestaat uit drie hoofdonderdelen:

  1. het voorvoegsel instellen, waarmee het standaard URL-pad wordt ingesteld om de ingestelde weergaven binnen te vinden
  2. het standaard weergavetype dat wordt ingesteld via het achtervoegsel
  3. het instellen van een view class op de resolver waarmee technologieën zoals JSTL of Tiles kunnen worden geassocieerd met de gerenderde views

Een veel voorkomende vraag betreft hoe precies een dispatcher ViewResolveren de algemene structuur van de projectdirectory zijn gerelateerd. Laten we de basis eens bekijken.

Hier is een voorbeeld van een padconfiguratie voor een InternalViewResolver met behulp van de XML-configuratie van Spring:

Omwille van ons voorbeeld gaan we ervan uit dat onze applicatie wordt gehost op:

// localhost: 8080 /

Dit is het standaardadres en de standaardpoort voor een lokaal gehoste Apache Tomcat-server.

Ervan uitgaande dat onze applicatie wordt gebeld dispatcherexample-1.0.0, zijn onze JSP-weergaven toegankelijk vanuit:

//localhost:8080/dispatcherexample-1.0.0/jsp/

Het pad voor deze weergaven binnen een gewoon Spring-project met Maven is dit:

src - | belangrijkste - | webapp voor Java-bronnen - | jsp WEB-INF

De standaardlocatie voor weergaven is binnen WEB-INF. Het pad dat is opgegeven voor onze InternalViewResolver in het bovenstaande fragment bepaalt de subdirectory van ‘src / main / webapp 'waarin uw weergaven beschikbaar zullen zijn.

3.4. De LocaleResolver Koppel

De belangrijkste manier om sessie-, verzoek- of cookie-informatie voor onze coördinator aan te passen, is via de LocaleResolver koppel.

CookieLocaleResolver is een implementatie die de configuratie van stateless applicatie-eigenschappen met behulp van cookies mogelijk maakt. Laten we het toevoegen aan AppConfig.

@Bean openbare CookieLocaleResolver cookieLocaleResolverExample () {CookieLocaleResolver localeResolver = nieuwe CookieLocaleResolver (); localeResolver.setDefaultLocale (Locale.ENGLISH); localeResolver.setCookieName ("locale-cookie-resolver-voorbeeld"); localeResolver.setCookieMaxAge (3600); retourneer localeResolver; } @Bean openbare LocaleResolver sessionLocaleResolver () {SessionLocaleResolver localeResolver = nieuwe SessionLocaleResolver (); localeResolver.setDefaultLocale (Locale.US); localResolver.setDefaultTimeZone (TimeZone.getTimeZone ("UTC")); retourneer localeResolver; } 

SessionLocaleResolver maakt sessiespecifieke configuratie in een stateful applicatie mogelijk.

De setDefaultLocale() methode vertegenwoordigt een geografische, politieke of culturele regio, terwijl setDefaultTimeZone() bepaalt de relevante Tijdzone object voor de aanvraag Boon in kwestie.

Beide methoden zijn beschikbaar voor elk van de bovenstaande implementaties van LocaleResolver.

3.5. De ThemeResolver Koppel

De lente biedt een stilistisch thema voor onze opvattingen.

Laten we eens kijken hoe we onze dispatcher kunnen configureren om thema's af te handelen.

Eerste, laten we alle configuratie opzetten die nodig is om onze statische themabestanden te vinden en te gebruiken. We moeten een statische bronlocatie instellen voor onze ThemeSource om de feitelijke Thema's zelf (Thema objecten bevatten alle configuratie-informatie die in die bestanden is opgegeven). Voeg dit toe aan AppConfig:

@Override public void addResourceHandlers (register ResourceHandlerRegistry) {registry.addResourceHandler ("/ resources / **") .addResourceLocations ("/", "/ resources /") .setCachePeriod (3600) .resourceChain (true) .addResourceLocations ("/", "/ resources /") .setCachePeriod (3600) .resourceChain (true) .addResourceLocations ("/", "/ resources /"). ()); } @Bean openbare ResourceBundleThemeSource themeSource () {ResourceBundleThemeSource themeSource = nieuwe ResourceBundleThemeSource (); themeSource.setDefaultEncoding ("UTF-8"); themeSource.setBasenamePrefix ("thema's."); terug themeSource; } 

Verzoeken beheerd door het DispatcherServlet kan het thema wijzigen door middel van een opgegeven parameter die is doorgegeven aan setParamName() beschikbaar op de ThemeChangeInterceptor voorwerp. Toevoegen aan Appconfiguratie:

@Bean openbaar CookieThemeResolver themeResolver () {CookieThemeResolver resolver = nieuwe CookieThemeResolver (); resolver.setDefaultThemeName ("voorbeeld"); resolver.setCookieName ("voorbeeldthema-cookie"); return resolver; } @Bean openbaar ThemeChangeInterceptor themeChangeInterceptor () {ThemeChangeInterceptor interceptor = nieuw ThemeChangeInterceptor (); interceptor.setParamName ("thema"); terug interceptor; } @Override openbare ongeldige addInterceptors (InterceptorRegistry-register) {registry.addInterceptor (themeChangeInterceptor ()); } 

De volgende JSP-tag is aan onze weergave toegevoegd om de juiste stijl te laten verschijnen:

Het volgende URL-verzoek geeft het voorbeeld thema met behulp van de ‘theme'-parameter die is doorgegeven aan onze geconfigureerde ThemeChange Intercepter:

//localhost:8080/dispatcherexample-1.0.0/?theme=example

3.6. De MultipartResolver Koppel

EEN MultipartResolver implementatie inspecteert een aanvraag voor multiparts en verpakt ze in een MultipartHttpServletRequest voor verdere verwerking door andere elementen in het proces indien ten minste één multipart wordt gevonden. Toevoegen aan AppConfig:

@Bean public CommonsMultipartResolver multipartResolver () gooit IOException {CommonsMultipartResolver resolver = nieuwe CommonsMultipartResolver (); resolver.setMaxUploadSize (10000000); return resolver; } 

Nu we onze MultipartResolver Bean, laten we een controller instellen om te verwerken MultipartFile verzoeken:

@Controller openbare klasse MultipartController {@Autowired ServletContext-context; @PostMapping ("/ upload") openbaar ModelAndView FileuploadController (@RequestParam ("file") MultipartFile-bestand) gooit IOException {ModelAndView modelAndView = nieuw ModelAndView ("index"); InputStream in = file.getInputStream (); String path = nieuw bestand ("."). GetAbsolutePath (); FileOutputStream f = nieuwe FileOutputStream (path.substring (0, path.length () - 1) + "/ uploads /" + file.getOriginalFilename ()); int ch; while ((ch = in.read ())! = -1) {f.write (ch); } f.flush (); f.close (); in.close (); modelAndView.getModel () .put ("bericht", "Bestand geüpload!"); retour modelAndView; }}

We kunnen een normaal formulier gebruiken om een ​​bestand naar het opgegeven eindpunt te verzenden. Geüploade bestanden zijn beschikbaar in ‘CATALINA_HOME / bin / uploads '.

3.7. De HandlerExceptionResolver Koppel

Lente HandlerExceptionResolver biedt uniforme foutafhandeling voor een volledige webtoepassing, een enkele controller of een set controllers.

Als u toepassingsbrede aangepaste uitzonderingsafhandeling wilt bieden, maakt u een klasse met de annotatie @ControllerAdvice:

@ControllerAdvice openbare klasse ExampleGlobalExceptionHandler {@ExceptionHandler @ResponseBody openbare String handleExampleException (uitzondering e) {// ...}}

Alle methoden binnen die klasse zijn geannoteerd met @BuienRadarNL zal beschikbaar zijn op elke controller binnen het verantwoordelijkheidsgebied van de dispatcher.

Implementaties van de HandlerExceptionResolver interface in het ApplicationContext van DispatcherServlet zijn beschikbaar voor onderscheppen een specifieke controller onder het verantwoordelijkheidsgebied van die verzender wanneer dan ook @BuienRadarNL wordt gebruikt als annotatie, en de juiste klasse wordt als parameter doorgegeven:

@Controller openbare klasse FooController {@ExceptionHandler ({CustomException1.class, CustomException2.class}) openbare ongeldige handleException () {// ...} // ...}

De handleException () methode zal nu dienen als een exception handler voor FooController in ons voorbeeld hierboven als een van de uitzonderingen CustomException1 of CustomException2 treedt op.

Hier is een artikel dat dieper ingaat op het afhandelen van uitzonderingen in een Spring-webtoepassing.

4. Conclusie

In deze tutorial hebben we Spring's besproken DispatcherServlet en verschillende manieren om het te configureren.

Zoals altijd is de broncode die in deze tutorial wordt gebruikt, beschikbaar op Github.