Анализ исходного кода процесса запуска Spring MVC

Spring MVC контейнер Tomcat
Анализ исходного кода процесса запуска Spring MVC

Сегодня я попытаюсь проанализировать процесс инициализации 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 предназначен дляdispactherServletservice, а этот новый контейнер создается на основе корневого контейнера 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-компоненты и т. д.)

Спасибо за чтение, я с нетерпением жду прогресса вместе с вами, и добро пожаловать в комментарии ниже~~~