После использования SpringBoot мы больше не можем напрямую видеть использование DispatcherServlet на поверхности. Эта статья проведет вас от первоначального использования DispatcherServlet до автоматической настройки DispatcherServlet в исходном коде SpringBoot.
Введение в DispatcherServlet
DispatcherServlet — это реализация шаблона проектирования переднего контроллера, который обеспечивает централизованную точку доступа для Spring Web MVC, отвечает за распределение обязанностей и легко интегрируется с контейнером Spring Ioc, чтобы получить все преимущества Spring.
Роль DispatcherServlet
DispatcherServlet в основном используется для планирования ответственности и сам в основном используется для управления процессом.Основные обязанности заключаются в следующем:
- Парсинг выгрузки файлов, если тип запроса составной, парсинг выгрузки файлов будет выполняться через MultipartResolver;
- Через HandlerMapping запрос сопоставляется с процессором (возвращает HandlerExecutionChain, который включает в себя процессор и несколько перехватчиков HandlerInterceptor);
- Поддержка нескольких типов обработчиков (обработчиков в HandlerExecutionChain) через HandlerAdapter;
- Разобрать имя логического представления на конкретное представление с помощью ViewResolver;
- анализ локализации;
- Отрисовка конкретных видов и т.д.;
- Если во время выполнения возникает исключение, оно будет передано HandlerExceptionResolver для разрешения.
Рабочий процесс DispatcherServlet
Традиционная конфигурация DispatcherServlet
DispatcherServlet в качестве переднего контроллера, обычно настраиваемый в файле web.xml. Чтобы перехватывать совпадающие запросы, правила перехвата и сопоставления сервлетов должны быть определены сами по себе, а перехваченные запросы распределяются на целевой контроллер для обработки в соответствии с соответствующими правилами, что является первым шагом в настройке Spring MVC.
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/dispatcherServlet-servlet.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
DospatcherServlet на самом деле является сервлетом (он наследует HttpServlet). Запросы, обрабатываемые DispatcherServlet, должны быть сопоставлены с использованием сопоставления URL-адресов, определенного в том же файле web.xml. Это стандартная конфигурация сервлета J2EE.
В приведенной выше конфигурации:
- servlet-name используется для определения имени сервлета, здесь dispatcherServlet.
- servlet-class используется для определения конкретного класса реализации сервлета, определенного выше, здесь org.springframework.web.servlet.DispatcherServlet.
- init-param используется для определения параметров инициализации сервлета, здесь он указывает файл dispatcherServlet-servlet.xml в папке WEB-INF для инициализации. Если метод именования spring-mvc.xml заключается в том, чтобы определить servlet-name+"-servlet" ранее, вы не можете определить этот параметр инициализации (файл конфигурации Spring по умолчанию: "/WEB-INF/[servlet name]-servlet.xml "), Spring обработает этот файл конфигурации. Видно, что файлы конфигурации Spring также могут быть размещены в других местах, если они указаны здесь. Если определено несколько файлов конфигурации, их можно разделить знаком «,».
- servlet-mapping определяет, что все запросы, заканчивающиеся на .do, проходят через диспетчер.
Когда DispatcherServlet сконфигурирован, как только DispatcherServlet получает запрос, DispatcherServlet начинает обрабатывать запрос.
Поток обработки DispatcherServlet
Когда DispatcherServlet сконфигурирован, обработка начинается, когда DispatcherServlet получает соответствующий запрос. Процесс обработки выглядит следующим образом:
Найдите WebApplicationContext и привяжите его к свойству запроса, чтобы контроллеры и другие процессоры в цепочке могли использовать WebApplicationContext. Имя свойства по умолчанию — DispatcherServlet.WEB.APPLICATIONКОНТЕКСТ_АТРИБУТ.
Привяжите преобразователь локализации к запросу, чтобы процессоры в цепочке обработки могли выполнять локализацию при обработке запроса (подготовка данных, отображение представлений и т. д.). Если вам не нужен парсинг локализации, просто игнорируйте его.
Привяжите преобразователь темы к запросу, чтобы представление могло решить, какую тему использовать. Если вам не нужна тема, вы можете ее игнорировать.
Если вы укажете синтаксический анализатор загружаемых файлов, Spring проверит каждый полученный запрос, чтобы увидеть, есть ли загружаемый файл, и если да, то запрос будет инкапсулирован как MultipartHttpServletRequest для использования другими процессорами в цепочке. (Дополнительную информацию см. в разделе Поддержка многокомпонентной (загрузки файлов) Spring)
Найдите соответствующий обработчик и выполните цепочку выполнения, связанную с этим обработчиком (препроцессор, постпроцессор, контроллер), чтобы подготовить данные модели для представления.
Если возвращаются данные модели, представление отображается с помощью преобразователя представлений, настроенного в WebApplicationContext, в противном случае представление отображаться не будет. Существуют различные причины, по которым возвращаемая модель данных может быть пустой, например, препроцессор или постпроцессор может перехватить запрос, что может быть связано с безопасностью, или запрос может быть уже обработан и нет необходимости обрабатывать его снова.
Исходный код, связанный с DispatcherServlet
Часть исходного кода метода doService в org.springframework.web.servlet.DispatcherServlet:
protected void doService(HttpServletRequest request,
HttpServletResponse response) throws Exception {
// ......
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE,getWebApplicationContext());
// ......
}
В приведенном выше исходном коде DispatcherServlet найдет контекст WebApplicationContext (его указанный класс реализации — XmlWebApplicationContext) и привяжет его к атрибуту (имя атрибута по умолчанию — WEB).APPLICATIONCONTEXT_ATTRIBUTE), чтобы контроллер мог использовать WebApplicationContext.
Исходный код метода initStrategies выглядит следующим образом:
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
}
Как видно из приведенного выше кода, при запуске DispatcherServlet он настроит нужные нам bean-компоненты Web-уровня, такие как HandlerMapping, HandlerAdapter и т. д., а если мы его не настроим, то он также предоставит нам конфигурацию по умолчанию.
Автоконфигурация DispatcherServlet SpringBoot
Автоматическая настройка DispatcherServlet в Spring Boot выполняется с помощью класса DispatcherServletAutoConfiguration.
Сначала посмотрите на закомментированную часть кода:
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass(DispatcherServlet.class)
@AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)
public class DispatcherServletAutoConfiguration {
...
}
@AutoConfigureOrder указывает приоритет автоконфигурации; @Configuration указывает, что этот класс является классом автоконфигурации; @ConditionalOnWebApplication указывает, что автоконфигурация должна быть веб-приложением на основе SERVLET; @ConditionalOnClass указывает, что должен быть DispatcherServlet в пути к классам; @AutoConfigureAfter указывает, что автоматическая настройка должна быть основана на автоматической настройке ServletWebServerFactoryAutoConfiguration.
Код для создания экземпляра DispatcherServlet в DispatcherServletAutoConfiguration выглядит следующим образом:
@Configuration(proxyBeanMethods = false) // 实例化配置类
@Conditional(DefaultDispatcherServletCondition.class) // 实例化条件:通过该类来判断
@ConditionalOnClass(ServletRegistration.class) // 存在指定的ServletRegistration类
// 加载HttpProperties和WebMvcProperties
@EnableConfigurationProperties({ HttpProperties.class, WebMvcProperties.class })
protected static class DispatcherServletConfiguration {
@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServlet dispatcherServlet(HttpProperties httpProperties, WebMvcProperties webMvcProperties) {
// 创建DispatcherServlet
DispatcherServlet dispatcherServlet = new DispatcherServlet();
// 初始化DispatcherServlet各项配置
dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest());
dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest());
dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound());
dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents());
dispatcherServlet.setEnableLoggingRequestDetails(httpProperties.isLogRequestDetails());
return dispatcherServlet;
}
// 初始化上传文件的解析器
@Bean
@ConditionalOnBean(MultipartResolver.class)
@ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)
public MultipartResolver multipartResolver(MultipartResolver resolver) {
// Detect if the user has created a MultipartResolver but named it incorrectly
return resolver;
}
}
Внутренний класс DispatcherServletConfiguration также должен соответствовать указанным условиям для инициализации. Подробности см. в комментариях к коду.
Метод dispatcherServlet реализует экземпляр DispatcherServlet и устанавливает основные параметры. Традиционная конфигурация — это конфигурация DispatcherServlet в файле web.xml.
Другой метод, multipartResolver, используется для инициализации распознавателя для загрузки файлов.Основная функция заключается в изменении имени определяемого пользователем MultipartResolver на «multipartResolver», если это не «multipartResolver», что эквивалентно переименованию.
Аннотация @Conditional для DispatcherServletConfiguration ограничивает автоматическую настройку только в том случае, если она соответствует условиям соответствия, определенным в DefaultDispatcherServletCondition. Класс DefaultDispatcherServletCondition также является внутренним классом.
@Order(Ordered.LOWEST_PRECEDENCE - 10)
private static class DefaultDispatcherServletCondition extends SpringBootCondition {
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
ConditionMessage.Builder message = ConditionMessage.forCondition("Default DispatcherServlet");
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
List<String> dispatchServletBeans = Arrays
.asList(beanFactory.getBeanNamesForType(DispatcherServlet.class, false, false));
if (dispatchServletBeans.contains(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)) {
return ConditionOutcome
.noMatch(message.found("dispatcher servlet bean").items(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME));
}
if (beanFactory.containsBean(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)) {
return ConditionOutcome.noMatch(
message.found("non dispatcher servlet bean").items(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME));
}
if (dispatchServletBeans.isEmpty()) {
return ConditionOutcome.match(message.didNotFind("dispatcher servlet beans").atAll());
}
return ConditionOutcome.match(message.found("dispatcher servlet bean", "dispatcher servlet beans")
.items(Style.QUOTE, dispatchServletBeans)
.append("and none is named " + DEFAULT_DISPATCHER_SERVLET_BEAN_NAME));
}
}
Основная функция этого класса может быть резюмирована следующим образом: проверить, существует ли уже DispatcherServlet с именем «dispatcherServlet» в контейнере Spring.Если он не существует, условие выполнено.
Также в этом классе автоконфигурации есть внутренний класс для создания экземпляра ServletRegistrationBean:
@Configuration(proxyBeanMethods = false)
@Conditional(DispatcherServletRegistrationCondition.class)
@ConditionalOnClass(ServletRegistration.class)
@EnableConfigurationProperties(WebMvcProperties.class)
@Import(DispatcherServletConfiguration.class)
protected static class DispatcherServletRegistrationConfiguration {
@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
@ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet,
WebMvcProperties webMvcProperties, ObjectProvider<MultipartConfigElement> multipartConfig) {
// 通过ServletRegistrationBean将dispatcherServlet注册为servlet,这样servlet才会生效。
DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet,
webMvcProperties.getServlet().getPath());
// 设置名称为dispatcherServlet
registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
// 设置加载优先级,设置值默认为-1,存在于WebMvcProperties类中
registration.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());
multipartConfig.ifAvailable(registration::setMultipartConfig);
return registration;
}
}
Основная функция класса DispatcherServletRegistrationConfiguration состоит в том, чтобы зарегистрировать диспетчерский сервлет, чтобы сделать его эффективным, и установить некоторые параметры инициализации.
Среди них DispatcherServletRegistrationBean наследуется от ServletRegistrationBean и в основном предоставляет услуги для DispatcherServlet. И DispatcherServletRegistrationBean, и DispatcherServlet предоставляют функции для регистрации сервлетов и предоставления информации DispatcherServletPath.
Spring Boot завершает предыдущие операции настройки в файле web.xml с помощью указанного выше класса автоматической настройки. Это еще и его удобство.
Справочная статья:
https://www.cnblogs.com/wql025/p/4805634.html
https://juejin.cn/post/6844903917487128583
Оригинальная ссылка: "Подробное объяснение и анализ исходного кода DispatcherServlet SpringBoot.》
Весеннее техническое видео
Академия CSDN:«Семейный блок видеоуроков Spring Boot»