Сегодня я попытаюсь проанализировать процесс инициализации Spring MVC на уровне исходного кода и раскрыть истинную завесу Spring MVC.Может быть, мы все научились использовать Spring MVC, или принцип Spring MVC был прочитан в теории. Прежде чем начать, вам может потребоваться освоить некоторые базовые знания Java EE.Например, мы должны сначала изучить техническую спецификацию сервлета Java EE, потому что реализована структура Spring mvc, а нижний уровень соответствует спецификации сервлета.
Перед тем, как приступить к анализу исходного кода, нам может понадобиться простой кейс-проект, не паникуйте, его устроил редактор:
Адрес загрузки примера проекта:GitHub.com/smallercode…
Итак, приступим!
1. Предварительные знания
Как мы все знаем, когда мы используем Spring MVC, мы обычноweb.xml
Выполните следующую настройку в файле:
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<!-- 上下文参数,在监听器中被使用 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:applicationContext.xml
</param-value>
</context-param>
<!-- 监听器配置 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- 前端控制器配置 -->
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
Вышеупомянутую конфигурацию можно свести к нескольким пунктам, а именно:
- 1. Настройте прослушиватель контекста Spring Web, который также является точкой входа для запуска Spring mvc.Что касается причин, об этом будет рассказано во втором разделе.
- 2. Интерфейсный контроллер
DispatcherServlet
, контроллер является входом и процессором для Spring mvc для обработки различных запросов.
Когда мы развертываем приложение spring mvc на tomcat, когда вы не настраиваете никакихcontext-param
иlistener
параметр, настройте только одинDispatcherServlet
, тогда tomcat не будет инициализировать веб-контекст Spring при запуске Другими словами, tomcat не будет инициализировать Spring Framework, потому что вы не сказали им, где находятся файлы конфигурации Spring и как их загрузить. такlistener
В этом нам помог слушатель, так почему же мы можем указывать tomcat, как загружаться после настройки слушателя? так какlistener
Это компонент слушателя, который реализует техническую спецификацию сервлета, и tomcat загружает его первым при запуске.web.xml
Существуют ли какие-либо прослушиватели сервлетов в , и запустите их.ContextLoaderListener
Это инкапсуляция прослушивателя сервлета с помощью среды Spring, и, по сути, это прослушиватель сервлета, поэтому он будет выполняться, но из-заContextLoaderListener
Исходный код основан наcontextConfigLocation
иcontextClass
Два параметра конфигурации для загрузки соответствующей конфигурации, поэтому у нас есть конфигурацияcontext-param
параметры,servlet
То же самое относится и к параметрам инициализации в теге, которые сообщают веб-серверу установить весенний веб-контекст (WebApplicationContext
) также инициализируется.
Общий процесс загрузки весенних mvc-приложений в tomcat описан выше, далее разберем принцип запуска из исходного кода.
Во-вторых, анализ исходного кода запуска веб-контекста Spring MVC.
Предположим, теперь мы поместим вышеуказанноеweb.xml
в файле<load-on-startup>1</load-on-startup>
Если его убрать, то по умолчанию при запуске tomcat будет инициализирован только весенний веб-контекст, то есть он будет загружаться только вapplicationContext.xml
этот файл дляapplicationContext-mvc.xml
Этот файл конфигурации не может быть загружен.<load-on-startup>1</load-on-startup>
значит позволитьDispatcherServlet
Задержка до времени использования (也就是处理请求的时候
), а затем инициализировать.
Мы уже знаем, что Spring Web основан наservlet
Стандартно инкапсулировать, тогда очевидно, как инициализировать сервлет,WebApplicationContext
Как должен быть инициализирован веб-контекст. давайте сначала посмотримContextLoaderListener
Что такое исходный код .
public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
// 初始化方法
@Override
public void contextInitialized(ServletContextEvent event) {
initWebApplicationContext(event.getServletContext());
}
// 销毁方法
@Override
public void contextDestroyed(ServletContextEvent event) {
closeWebApplicationContext(event.getServletContext());
ContextCleanupListener.cleanupAttributes(event.getServletContext());
}
}
ContextLoaderListener
класс реализуетServletContextListener
, по сути, прослушиватель сервлета, tomcat сначала загрузит компонент прослушивателя сервлета и вызоветcontextInitialized
метод, вcontextInitialized
вызов методаinitWebApplicationContext
Метод инициализирует веб-контекст Spring.Увидев это, я понимаю, что вход в Spring mvc уже здесь, ха-ха~~~ поторопитесь и продолжайтеinitWebApplicationContext
Проверьте метод!
initWebApplicationContext()
метод:
// 创建web上下文,默认是XmlWebApplicationContext
if (this.context == null) {
this.context = createWebApplicationContext(servletContext);
}
if (this.context instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
// 如果该容器还没有刷新过
if (!cwac.isActive()) {
if (cwac.getParent() == null) {
ApplicationContext parent = loadParentContext(servletContext);
cwac.setParent(parent);
}
// 配置并刷新容器
configureAndRefreshWebApplicationContext(cwac, servletContext);
}
}
Метод выше делает только две вещи:
- 1. Если веб-контейнер Spring не был создан, создайте новый веб-контейнер Spring, а контейнер является корневым корневым контейнером.В этом корневом контейнере создается Spring Web-контейнер сервлета, упомянутый в третьем разделе ниже.
- 2. Настройте и обновите контейнер
В приведенном выше комментарии к коду говорится, что созданный по умолчанию контейнер контекстаXmlWebApplicationContext
, а почему не другие веб-контексты? Почему не любой из следующих контекстов?
мы можем следоватьcreateWebApplicationContext
Затем вы можете обнаружить, что по умолчанию используется вызываемыйContextLoader.properties
Файл загружен и настроен, и содержимое файла:
org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext
Конкретная реализация:
protected Class<?> determineContextClass(ServletContext servletContext) {
// 自定义上下文,否则就默认创建XmlWebApplicationContext
String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
if (contextClassName != null) {
try {
return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
}
catch (ClassNotFoundException ex) {
throw new ApplicationContextException(
"Failed to load custom context class [" + contextClassName + "]", ex);
}
}
else {
// 从属性文件中加载类名,也就是org.springframework.web.context.support.XmlWebApplicationContext
contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
try {
return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
}
catch (ClassNotFoundException ex) {
throw new ApplicationContextException(
"Failed to load default context class [" + contextClassName + "]", ex);
}
}
}
Из вышеизложенного видно, что мы также можем настроить контекст Spring Web, так как же указать наш собственный контекст? Ответ черезweb.xml
указано вcontextClass
параметры, поэтому в конце первого резюме говоритсяcontextClass
параметры иcontextConfigLocation
очень важно~~contextConfigLocation
параметры, мы следимconfigureAndRefreshWebApplicationContext
Вы можете видеть, как показано ниже:
Суммировать:
Процесс запуска Spring MVC примерно из вызоваContextLoaderListener
Первоначально это прослушиватель сервлета, который может быть обнаружен и загружен веб-контейнером, инициализируя прослушиватель.ContextLoaderListener
После этого, в соответствии с конфигурацией, такой какcontextConfigLocation
иcontextClass
Создайте веб-контейнер, если вы не укажетеcontextClass
значение параметра, тип веб-контейнера Spring, созданный по умолчанию, —XmlWebApplicationContext
, последний шаг в соответствии с вашей конфигурациейcontextConfigLocation
путь к файлу для настройки и обновления контейнера.
три,DispatcherServlet
Инициализация контроллера
Ну, над которым мы кратко проанализировали исходный код Spring MVC контейнер инициализации контейнера, мы никогда не забудем, что тип контейнера, который мы создаем по умолчанию, этоXmlWebApplicationContext
, конечно не забудем, вweb.xml
, у нас также есть важная конфигурация, то естьDispatcherServlet
. Ниже мы проанализируемDispatcherServlet
процесс инициализации.
DispatcherServlet
, это сервлет, сервлет используемый для обработки запросов запросов, он является ядром spring mvc, через него проходят все запросы, и он указывает, как выполнять последующие операции, на первый взгляд как дверь, поэтому меня это волнует Он называется "шлюз". Прежде чем мы продолжим, мы все должны придерживаться здравого смысла, что ------- и слушатели, и сервлеты являются компонентами спецификации сервлета, и веб-серверы могут обнаруживать и загружать их.
Давайте взглянемDispatcherServlet
Наследственные отношения:
Видя это, видим ли мы это с первого взгляда?DispatcherServlet
наследоватьHttpServlet
этот класс,HttpServlet
Это сервлет, специально используемый для обработки HTTP-запросов в технической спецификации сервлета, что нетрудно объяснить, почему Spring MVC будетDispatcherServlet
Как единая запись запроса.
Поскольку жизненный цикл сервлетаinit()
->service()
->destory()
,ТакDispatcherServlet
Как его инициализировать? Глядя на диаграмму наследования выше, мы переходим кHttpServletBean
Иди проверь.
как и предсказывалось,HttpServletBean
класс имеетinit()
метод,HttpServletBean
это абстрактный класс,init()
Методы, как показано ниже:
Видно, что метод используетfinal
модификация, потому чтоfinal
Модифицированные методы не могут наследоваться подклассами, то есть подклассы не имеют одинаковыхinit()
метод, этоinit
Путь этоDispatcherServlet
запись инициализации.
Затем мы следуемFrameworkServlet
изinitServletBean()
метод:
В методе будет инициализирован веб-контейнер, отличный от первого раздела, имейте в виду, что этот новый веб-контейнер Spring предназначен дляdispactherServlet
service, а этот новый контейнер создается на основе корневого контейнера ROOT в первом разделе, мы находимся в<servlet>
Параметры инициализации, настроенные в теге, добавляются в новый контейнер.
Уже,DispatcherSevlet
Инициализация завершена, что звучит немного обманчиво, но на самом деле это одно и то же.Приведенный выше анализ вращается только вокруг одного метода, который называетсяinit()
, который будет вызываться при всех инициализациях сервлета.
Суммировать:
dispactherServlet
Инициализация делает две вещи, первая основана на корневом веб-контейнере, который мы создали в первом разделе.XmlWebApplicationContext
, а затем создайте специальныйdispactherServlet
Веб-контейнер сервиса, второе — поставить свойdispactherServlet
Соответствующая конфигурация загружается в новый контейнер.
3. Через какой процесс проходит каждый запрос вызова запроса
На самом деле этоdispatcherServlet
Суть контроллера в том, что веб-фреймворк — это не что иное, как прием запроса, обработка запроса и последующий ответ на запрос. Конечно, еслиdispactherServlet
Он слишком слаб, чтобы просто принимать обработку и отвечать на запросы, поэтому разработчики Spring добавили множество новых функций, таких как перехватчики, преобразователи сообщений, преобразователи обработки запросов и различныеResolver
, поэтому Spring MVC очень мощный.
dispatcherServlet
Класс не выполняет соответствующий анализ исходного кода, потому что это фиксированный шаг выполнения, что это значит? Когда приходит запрос, он примерно проходит следующий процесс:
Принимать запросы -----> Существуют ли различные обработчикиHandler
-------> Есть ли конвертер сообщенийHandlerAdapter
--------> Ответ на запрос
Если на каждом из вышеперечисленных шагов есть соответствующий компонент, конечно, предполагается, что вы выполнили соответствующую настройку в проекте, настроенный вами компонент будет выполнен, и, наконец, на запрос будет дан ответ. Итак, после понимания общего процесса, если вы хотите отладить запрос, вы можетеdispatcherServlet
КатегорияdoDispatch
Сделайте точку останова в методе, и после того, как вы закончите код, вы обнаружите, что общий процесс почти такой же.
4. Последствия
Проект в этой статье основан на традиционном веб-проекте загрузки web.xml.Конечно, в Spring MVC мы также можем настроить его полностью на основе аннотаций.Мы можем достичьWebApplicationInitializer
Чтобы создать свой собственный веб-лаунчер, вы также можете наследоватьAbstractAnnotationConfigDispatcherServletInitializer
Чтобы создать соответствующий веб-контейнер Spring (включая упомянутый выше корневой контейнер и веб-контейнер сервлета) и, наконец, передать наследованиеWebMvcConfigurationSupport
Еще один шаг для пользовательской конфигурации (соответствующие перехватчики, bean-компоненты и т. д.)
Спасибо за чтение, я с нетерпением жду прогресса вместе с вами, и добро пожаловать в комментарии ниже~~~