Введение в Spring DispatcherServlet

Введение в Spring DispatcherServlet

1. Вступление

Проще говоря, в шаблоне проектированияFront Controller, единственный контроллер -responsible for directing incoming HttpRequests to all of an application’s other controllers and handlers.

DispatcherServlet Spring реализует этот шаблон и, следовательно, отвечает за правильную координациюHttpRequests их правым обработчикам.

В этой статье мы рассмотримexamine the Spring DispatcherServlet’s request processing workflow и то, как реализовать несколько интерфейсов, участвующих в этом рабочем процессе.

2. DispatcherServlet Обработка запроса

По сути,DispatcherServlet handles an incoming HttpRequest, delegates the request, and processes that request according to the configured HandlerAdapter interfaces, которые были реализованы в приложении Spring вместе с сопутствующими аннотациями, определяющими обработчики, конечные точки контроллера и объекты ответа.

Давайте подробнее рассмотрим, какDispatcherServlet обрабатывает компонент:

  • WebApplicationContext, связанный сDispatcherServlet под ключомDispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE, ищется и становится доступным для всех элементов процесса

  • DispatcherServlet находит все реализации интерфейсаHandlerAdapter, настроенные для вашего диспетчера, используяgetHandler() –, каждая найденная и настроенная реализация обрабатывает запрос черезhandle() в оставшейся части процесса.

  • LocaleResolver необязательно привязан к запросу, чтобы включить элементы в процессе для разрешения локали

  • ThemeResolver необязательно привязан к запросу, чтобы позволить элементам, таким как представления, определять, какую тему использовать

  • если указанMultipartResolver, запрос проверяется на предметMultipartFiles - все найденные упаковываются вMultipartHttpServletRequest для дальнейшей обработки

  • РеализацииHandlerExceptionResolver, объявленные вWebApplicationContext, принимают исключения, которые возникают во время обработки запроса.

Вы можете узнать больше обо всех способах регистрации и настройкиDispatcherServlethere.

3. HandlerAdapter Интерфейсы

ИнтерфейсHandlerAdapter упрощает использование контроллеров, сервлетов,HttpRequests и путей HTTP через несколько определенных интерфейсов. The HandlerAdapter interface thus plays an essential role through the many stages of the DispatcherServlet request processing workflow.

Сначала каждая реализацияHandlerAdapter помещается вHandlerExecutionChain из методаgetHandler() диспетчера. Затем каждая из этих реализацийhandle() является объектомHttpServletRequest по мере выполнения цепочки выполнения.

В следующих разделах мы более подробно рассмотрим некоторые из наиболее важных и часто используемыхHandlerAdapters.

3.1. Отображения

Чтобы понять сопоставления, нам нужно сначала взглянуть на то, как аннотировать контроллеры, поскольку контроллеры очень важны для интерфейсаHandlerMapping.

SimpleControllerHandlerAdapter позволяет явно реализовать контроллер без аннотации@Controller.

RequestMappingHandlerAdapter поддерживает методы, аннотированные аннотацией@RequestMapping.

Здесь мы сосредоточимся на аннотации@Controller, но также доступен полезный ресурс с несколькимиexamples using the SimpleControllerHandlerAdapter.

The @RequestMapping annotation sets the specific endpoint at which a handler will be available в связанном с нимWebApplicationContext.

Давайте посмотрим на примерController, который предоставляет и обрабатывает конечную точку‘/user/example':

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

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

Пути, указанные в аннотации@RequestMapping, управляются внутренне через интерфейсHandlerMapping.

Структура URL-адресов, естественно, зависит от самогоDispatcherServlet и определяется отображением сервлета.

Таким образом, еслиDispatcherServlet отображается в «/», то все отображения будут покрыты этим отображением.

Если, однако, отображение сервлета - «/dispatcher», то любые аннотации @RequestMapping будут относиться к этому корневому URL.

Remember that ‘/' is not the same as ‘/ '* для отображений сервлетов! ‘/' 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. Он не указывает, что все пути с одним и тем же контекстом URL находятся в зоне ответственности диспетчера. Вместо этого он переопределяет и игнорирует другие отображения диспетчера. Таким образом, «/ пример» будет выглядеть как 404!

По этой причине‘/ 'не следует использовать, за исключением очень ограниченных обстоятельств * (например, при настройке фильтра).

3.2. Обработка HTTP-запросов

The core responsibility of a DispatcherServlet is to dispatch incoming HttpRequests to the correct handlers указан с аннотациями@Controller или@RestController.

В качестве примечания: основное различие между@Controller и@RestController заключается в том, как генерируется ответ -@RestController также по умолчанию определяет@ResponseBody.

Запись, в которой мы более подробно рассмотрим контроллеры Spring, можно найти вhere.

3.3. ИнтерфейсViewResolver

ViewResolver прикрепляется кDispatcherServlet как параметр конфигурации для объектаApplicationContext.

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

Вот пример конфигурации, которую мы поместим в нашAppConfig  для рендеринга страниц JSP:

@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;
    }
}

Очень просто! Есть три основные части этого:

  1. установка префикса, который задает путь URL по умолчанию для поиска установленных представлений в

  2. тип представления по умолчанию, который устанавливается через суффикс

  3. установка класса представления в преобразователе, который позволяет таким технологиям, как JSTL или Tiles, быть привязанными к отображаемым представлениям

One common question involves how precisely a dispatcher’s ViewResolverand the overall project directory structure are related. Давайте посмотрим на основы.

Вот пример конфигурации пути дляInternalViewResolver с использованием конфигурации Spring XML:

Для нашего примера предположим, что наше приложение размещено на:

http://localhost:8080/

Это адрес и порт по умолчанию для локально размещенного сервера Apache Tomcat.

Предполагая, что наше приложение называетсяdispatcherexample-1.0.0, наши представления JSP будут доступны из:

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

Путь к этим представлениям в обычном Spring-проекте с Maven следующий:

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

Расположение по умолчанию для представлений находится в WEB-INF. Путь, указанный для нашегоInternalViewResolver в приведенном выше фрагменте, определяет подкаталог «src / main / webapp», в котором будут доступны ваши представления.

3.4. ИнтерфейсLocaleResolver

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

CookieLocaleResolver - это реализация, позволяющая настраивать свойства приложения без сохранения состояния с помощью файлов cookie. Добавим его вAppConfig.

@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 допускает настройку для конкретного сеанса в приложении с отслеживанием состояния.

МетодsetDefaultLocale () представляет географический, политический или культурный регион, тогда какsetDefaultTimeZone () determines the relevant TimeZone object for the application Bean in question.

Оба метода доступны в каждой из вышеупомянутых реализацийLocaleResolver.

3.5. ИнтерфейсThemeResolver

Весна обеспечивает стилистическую тематику для наших взглядов.

Давайте посмотрим, как настроить диспетчер для обработки тем.

Во-первых,let’s set up all the configuration necessary to find and use our static theme files. Нам нужно установить статическое расположение ресурса для нашегоThemeSource, чтобы настроить сами фактическиеThemes (объектыTheme содержат всю информацию о конфигурации, предусмотренную в этих файлах). Добавьте это вAppConfig:

@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;
}

Запросы, управляемыеDispatcherServlet, могут изменять тему с помощью указанного параметра, переданного вsetParamName (), доступному в объектеThemeChangeInterceptor. Добавить вAppConfig:

@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());
}

Следующий тег JSP добавлен к нашему представлению для отображения правильного стиля:

Следующий URL-запрос отображает темуexample с использованием параметра «theme», переданного в настроенный намиThemeChangeIntercepter:

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

3.6. ИнтерфейсMultipartResolver

РеализацияMultipartResolver проверяет запрос на множественные части и помещает их вMultipartHttpServletRequest для дальнейшей обработки другими элементами в процессе, если найдена хотя бы одна составная часть. Добавить вAppConfig:

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

Теперь, когда мы настроили наш bean-компонентMultipartResolver, давайте настроим контроллер для обработки запросовMultipartFile:

@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;
    }
}

Мы можем использовать обычную форму для отправки файла в указанную конечную точку. Загруженные файлы будут доступны в «CATALINA_HOME / bin / uploads».

3.7. ИнтерфейсHandlerExceptionResolver

HandlerExceptionResolver Spring обеспечивает единообразную обработку ошибок для всего веб-приложения, одного контроллера или набора контроллеров.

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

@ControllerAdvice
public class ExampleGlobalExceptionHandler {

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

Любые методы в этом классе, помеченные@ExceptionHandler, будут доступны на каждом контроллере в зоне ответственности диспетчера.

Реализации интерфейсаHandlerExceptionResolver вDispatcherServlet’s ApplicationContext доступны дляintercept a specific controller в зоне ответственности этого диспетчераwhenever @ExceptionHandler is used as an annotation, и правильный класс передается в качестве параметра:

@Controller
public class FooController{

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

МетодhandleException() теперь будет служить обработчиком исключений дляFooController в нашем примере выше, если возникает исключениеCustomException1 илиCustomException2.

Here’s - статья, в которой более подробно рассматривается обработка исключений в веб-приложении Spring.

4. Заключение

В этом руководстве мы рассмотрелиDispatcherServlet Spring и несколько способов его настройки.

Как всегда, исходный код, используемый в этом руководстве, доступенover on Github.