Eine Einführung in das Spring DispatcherServlet

Eine Einführung in das Spring DispatcherServlet

1. Einführung

Einfach ausgedrückt ist inFront Controller Entwurfsmuster, ein einzelner Controllerresponsible for directing incoming HttpRequests to all of an application’s other controllers and handlers.

DispatcherServletvon Spring implementiert dieses Muster und ist daher dafür verantwortlich, dieHttpRequests richtig auf ihre rechten Handler abzustimmen.

In diesem Artikel werdenexamine the Spring DispatcherServlet’s request processing workflow und die Implementierung mehrerer Schnittstellen beschrieben, die an diesem Workflow teilnehmen.

2. DispatcherServlet Anforderungsverarbeitung

Im Wesentlichen aDispatcherServlet handles an incoming HttpRequest, delegates the request, and processes that request according to the configured HandlerAdapter interfaces, die in der Spring-Anwendung implementiert wurden, zusammen mit zugehörigen Anmerkungen, in denen Handler, Controller-Endpunkte und Antwortobjekte angegeben sind.

Lassen Sie uns genauer untersuchen, wie einDispatcherServleteine Komponente verarbeitet:

  • DieWebApplicationContext, die einemDispatcherServlet unter dem SchlüsselDispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE zugeordnet sind, werden gesucht und allen Elementen des Prozesses zur Verfügung gestellt

  • DispatcherServlet findet alle Implementierungen derHandlerAdapter-Schnittstelle, die für Ihren Dispatcher konfiguriert wurden, unter Verwendung vongetHandler() –. Jede gefundene und konfigurierte Implementierung verarbeitet die Anforderung überhandle() über den Rest des Prozesses

  • LocaleResolver ist optional an die Anforderung gebunden, damit Elemente im Prozess das Gebietsschema auflösen können

  • ThemeResolver ist optional an die Anforderung gebunden, Elemente wie Ansichten bestimmen zu lassen, welches Thema verwendet werden soll

  • Wenn einMultipartResolver angegeben ist, wird die Anforderung aufMultipartFiles überprüft. Alle gefundenen Werte werden zur weiteren Verarbeitung inMultipartHttpServletRequest eingeschlossen

  • HandlerExceptionResolver Implementierungen, die inWebApplicationContextdeklariert sind, nehmen Ausnahmen auf, die während der Verarbeitung der Anforderung ausgelöst werden

Sie können mehr über alle Möglichkeiten zum Registrieren und Einrichten vonDispatcherServlethere erfahren.

3. HandlerAdapter Schnittstellen

DieHandlerAdapter-Schnittstelle erleichtert die Verwendung von Controllern, Servlets,HttpRequests und HTTP-Pfaden über mehrere spezifische Schnittstellen. The HandlerAdapter interface thus plays an essential role through the many stages of the DispatcherServlet request processing workflow.

Zunächst wird jedeHandlerAdapter-Implementierung in dieHandlerExecutionChain dergetHandler()-Methode Ihres Dispatchers eingefügt. Dann ist jede dieser Implementierungenhandle()dasHttpServletRequest-Objekt, während die Ausführungskette fortschreitet.

In den folgenden Abschnitten werden wir einige der wichtigsten und am häufigsten verwendetenHandlerAdapters genauer untersuchen.

3.1. Zuordnungen

Um Zuordnungen zu verstehen, müssen wir uns zunächst ansehen, wie Controller mit Anmerkungen versehen werden, da Controller für dieHandlerMapping-Schnittstelle so wichtig sind.

DasSimpleControllerHandlerAdapter ermöglicht die explizite Implementierung eines Controllers ohne eine@Controller-Annotation.

DieRequestMappingHandlerAdapter unterstützen Methoden, die mit der@RequestMapping-Annotation. annotiert sind

Wir werden uns hier auf die Annotation von@Controllerkonzentrieren, aber es ist auch eine hilfreiche Ressource mit mehrerenexamples using the SimpleControllerHandlerAdapter verfügbar.

The @RequestMapping annotation sets the specific endpoint at which a handler will be available innerhalb der damit verbundenenWebApplicationContext.

Sehen wir uns ein Beispiel für einControlleran, das den Endpunkt von‘/user/example'verfügbar macht und behandelt:

@Controller
@RequestMapping("/user")
@ResponseBody
public class UserController {

    @GetMapping("/example")
    public User fetchUserExample() {
        // ...
    }
}

Die durch die Annotation@RequestMappingangegebenen Pfade werden intern über die SchnittstelleHandlerMappingverwaltet.

Die URL-Struktur ist natürlich relativ zuDispatcherServlet selbst - und wird durch die Servlet-Zuordnung bestimmt.

Wenn alsoDispatcherServlet auf '/' abgebildet wird, werden alle Zuordnungen von dieser Zuordnung abgedeckt.

Wenn die Servlet-Zuordnung jedoch stattdessen "/dispatcher" lautet, werden alle @RequestMapping-Anmerkungen relativ zu dieser Stamm-URL sein.

Remember that ‘/' is not the same as ‘/ '* für Servlet-Zuordnungen! ‘/' is the default mapping and exposes all URL’s to the dispatcher’s area of responsibility.

‘/*' is confusing to a lot of newer Spring developers. Es wird nicht angegeben, dass alle Pfade mit demselben URL-Kontext im Verantwortungsbereich des Dispatchers liegen. Stattdessen werden die anderen Dispatcher-Zuordnungen überschrieben und ignoriert. Also wird '/ example' als 404 erscheinen!

Aus diesem Grund sollte‘/ 'nur unter sehr begrenzten Umständen * verwendet werden (z. B. beim Konfigurieren eines Filters).

3.2. HTTP-Anforderungsbearbeitung

The core responsibility of a DispatcherServlet is to dispatch incoming HttpRequests to the correct handlers, angegeben mit den Anmerkungen@Controller oder@RestController.

Als Nebenbemerkung besteht der Hauptunterschied zwischen@Controller und@RestController darin, wie die Antwort generiert wird -@RestController definiert standardmäßig auch@ResponseBody.

Eine Beschreibung, in der wir uns eingehender mit den Controllern von Spring befassen, finden Sie inhere.

3.3. DieViewResolver-Schnittstelle

EinViewResolver wird als Konfigurationseinstellung für einApplicationContext-Objekt an einDispatcherServlet angehängt.

A ViewResolver determines both what kind of views are served by the dispatcher and from where they are served.

Hier ist eine Beispielkonfiguration, die wir in unsereAppConfig  zum Rendern von JSP-Seiten einfügen:

@Configuration
@EnableWebMvc
@ComponentScan("com.example.springdispatcherservlet")
public class AppConfig implements WebMvcConfigurer {

    @Bean
    public UrlBasedViewResolver viewResolver() {
        UrlBasedViewResolver resolver
          = new UrlBasedViewResolver();
        resolver.setPrefix("/WEB-INF/jsp/");
        resolver.setSuffix(".jsp");
        resolver.setViewClass(JstlView.class);
        return resolver;
    }
}

Sehr einfach! Dazu gibt es drei Hauptteile:

  1. Festlegen des Präfixes, mit dem der Standard-URL-Pfad zum Auffinden der festgelegten Ansichten festgelegt wird

  2. Der Standard-Ansichtstyp, der über das Suffix festgelegt wird

  3. Festlegen einer Ansichtsklasse auf dem Resolver, mit der Technologien wie JSTL oder Kacheln den gerenderten Ansichten zugeordnet werden können

One common question involves how precisely a dispatcher’s ViewResolverand the overall project directory structure are related. Werfen wir einen Blick auf die Grundlagen.

Hier ist ein Beispiel für eine Pfadkonfiguration fürInternalViewResolver unter Verwendung der XML-Konfiguration von Spring:

In unserem Beispiel wird davon ausgegangen, dass unsere Anwendung gehostet wird auf:

http://localhost:8080/

Dies ist die Standardadresse und der Standardport für einen lokal gehosteten Apache Tomcat-Server.

Unter der Annahme, dass unsere Anwendungdispatcherexample-1.0.0 heißt, können Sie auf unsere JSP-Ansichten zugreifen über:

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

Der Pfad für diese Ansichten in einem gewöhnlichen Spring-Projekt mit Maven lautet wie folgt:

src -|
     main -|
            java
            resources
            webapp -|
                    jsp
                    WEB-INF

Der Standardspeicherort für Ansichten liegt in WEB-INF. Der im obigen Snippet für unsereInternalViewResolver angegebene Pfad bestimmt das Unterverzeichnis von "src / main / webapp", in dem Ihre Ansichten verfügbar sind.

3.4. DieLocaleResolver-Schnittstelle

The primary way to customize session, request, or cookie information for our dispatcher is through the LocaleResolver interface.

CookieLocaleResolver ist eine Implementierung, die die Konfiguration zustandsloser Anwendungseigenschaften mithilfe von Cookies ermöglicht. Fügen wir es zuAppConfig hinzu.

@Bean
public CookieLocaleResolver cookieLocaleResolverExample() {
    CookieLocaleResolver localeResolver
      = new CookieLocaleResolver();
    localeResolver.setDefaultLocale(Locale.ENGLISH);
    localeResolver.setCookieName("locale-cookie-resolver-example");
    localeResolver.setCookieMaxAge(3600);
    return localeResolver;
}

@Bean
public LocaleResolver sessionLocaleResolver() {
    SessionLocaleResolver localeResolver = new SessionLocaleResolver();
    localeResolver.setDefaultLocale(Locale.US);
    localResolver.setDefaultTimeZone(TimeZone.getTimeZone("UTC"));
    return localeResolver;
}

SessionLocaleResolver ermöglicht eine sitzungsspezifische Konfiguration in einer statusbehafteten Anwendung.

Die MethodesetDefaultLocale () repräsentiert eine geografische, politische oder kulturelle Region, währendsetDefaultTimeZone () determines the relevant TimeZone object for the application Bean in question.

Beide Methoden sind für jede der obigen Implementierungen vonLocaleResolver verfügbar.

3.5. DieThemeResolver-Schnittstelle

Der Frühling bietet stilistische Themen für unsere Ansichten.

Schauen wir uns an, wie Sie unseren Dispatcher für die Bearbeitung von Themen konfigurieren.

Erstenslet’s set up all the configuration necessary to find and use our static theme files. Wir müssen einen statischen Ressourcenspeicherort für unsereThemeSource festlegen, um die tatsächlichenThemes selbst zu konfigurieren (Theme Objekte enthalten alle in diesen Dateien festgelegten Konfigurationsinformationen). Addiere dies zuAppConfig:

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
    registry.addResourceHandler("/resources/**")
      .addResourceLocations("/", "/resources/")
      .setCachePeriod(3600)
      .resourceChain(true)
      .addResolver(new PathResourceResolver());
}

@Bean
public ResourceBundleThemeSource themeSource() {
    ResourceBundleThemeSource themeSource
      = new ResourceBundleThemeSource();
    themeSource.setDefaultEncoding("UTF-8");
    themeSource.setBasenamePrefix("themes.");
    return themeSource;
}

VonDispatcherServlet verwaltete Anforderungen können das Thema über einen angegebenen Parameter ändern, der ansetParamName () übergeben wird, das für dasThemeChangeInterceptor-Objekt. verfügbar ist. ZuAppConfig: hinzufügen

@Bean
public CookieThemeResolver themeResolver() {
    CookieThemeResolver resolver = new CookieThemeResolver();
    resolver.setDefaultThemeName("example");
    resolver.setCookieName("example-theme-cookie");
    return resolver;
}

@Bean
public ThemeChangeInterceptor themeChangeInterceptor() {
   ThemeChangeInterceptor interceptor
     = new ThemeChangeInterceptor();
   interceptor.setParamName("theme");
   return interceptor;
}

@Override
public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(themeChangeInterceptor());
}

Das folgende JSP-Tag wird unserer Ansicht hinzugefügt, damit das richtige Design angezeigt wird:

Die folgende URL-Anforderung rendert dasexample-Thema mithilfe des Parameters 'theme', der an unsere konfiguriertenThemeChangeIntercepter: übergeben wird

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

3.6. DieMultipartResolver-Schnittstelle

Die Implementierung vonMultipartResolverprüft eine Anforderung für mehrteilige Teile und verpackt sie inMultipartHttpServletRequest zur weiteren Verarbeitung durch andere Elemente im Prozess, wenn mindestens ein mehrteiliger Teil gefunden wird. ZuAppConfig hinzufügen:

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

Nachdem wir unsereMultipartResolver-Bohne konfiguriert haben, richten wir einen Controller ein, der die Anforderungen vonMultipartFileverarbeitet:

@Controller
public class MultipartController {

    @Autowired
    ServletContext context;

    @PostMapping("/upload")
    public ModelAndView FileuploadController(
      @RequestParam("file") MultipartFile file)
      throws IOException {
        ModelAndView modelAndView = new ModelAndView("index");
        InputStream in = file.getInputStream();
        String path = new File(".").getAbsolutePath();
        FileOutputStream f = new 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("message", "File uploaded successfully!");
        return modelAndView;
    }
}

Wir können ein normales Formular verwenden, um eine Datei an den angegebenen Endpunkt zu senden. Hochgeladene Dateien sind in CATALINA_HOME / bin / uploads verfügbar.

3.7. DieHandlerExceptionResolver-Schnittstelle

HandlerExceptionResolvervon Spring bietet eine einheitliche Fehlerbehandlung für eine gesamte Webanwendung, einen einzelnen Controller oder eine Reihe von Controllern.

To provide application-wide custom exception handling, create a class annotated with @ControllerAdvice:

@ControllerAdvice
public class ExampleGlobalExceptionHandler {

    @ExceptionHandler
    @ResponseBody
    public String handleExampleException(Exception e) {
        // ...
    }
}

Alle Methoden innerhalb dieser Klasse, die mit@ExceptionHandler versehen sind, sind auf jedem Controller im Verantwortungsbereich des Dispatchers verfügbar.

Implementierungen derHandlerExceptionResolver-Schnittstelle inDispatcherServlet’s ApplicationContext stehenintercept a specific controller im Verantwortungsbereich des Dispatcherswhenever @ExceptionHandler is used as an annotation zur Verfügung, und die richtige Klasse wird als Parameter übergeben:

@Controller
public class FooController{

    @ExceptionHandler({ CustomException1.class, CustomException2.class })
    public void handleException() {
        // ...
    }
    // ...
}

Die MethodehandleException() dient jetzt als Ausnahmebehandlungsroutine fürFooController in unserem obigen Beispiel, wenn entweder die AusnahmeCustomException1 oderCustomException2 auftritt.

Here’sist ein Artikel, der sich eingehender mit der Ausnahmebehandlung in einer Spring-Webanwendung befasst.

4. Fazit

In diesem Tutorial haben wir dieDispatcherServletvon Spring und verschiedene Möglichkeiten zur Konfiguration überprüft.

Wie immer ist der in diesem Tutorial verwendete Quellcodeover on Github verfügbar.