Инвентаризация SpringMVC: основной процесс MVC

Java задняя часть
Инвентаризация SpringMVC: основной процесс MVC

Прежде всего, поделитесь всеми предыдущими статьями, ставьте лайки, добавляйте в избранное и пересылайте три раза подряд. >>>>😜😜😜
Сборник статей:🎁nuggets.capable/post/694164…
Github :👉github.com/black-ant
Резервное копирование CASE:👉git ee.com/ant black/wipe…

Введение

Цель статьи:

  • Разберите соответствующую логику в основном процессе SpringMVC
  • Определить соответствующие параметры процесса

Детали, не вошедшие в эту статью:

  • Конфигурация MVC
  • Инициализация контейнера
  • загрузка контейнера
  • Ждать....

2. Анализ процесса

Этот основной процесс включает в себя следующие части:

  • Сканирование обработки аннотаций
  • запрос на перехват
  • запрошенная конверсия
  • окончательная обработка заявки

image.png

2.1 Сканирование обработки аннотаций

Сканирование аннотаций в основном находится в DispatcherServlet.initHandlerMappings, а класс его логической обработки находится вAbstractHandlerMethodMapping.getHandlerInternalсередина.

2.1.1 Сканирование аннотаций

Отправной точкой сканирования аннотаций в основном является RequestMappingHandlerMapping , который выполняет следующие действия:

  • Обработка всех методов в AbstractHandlerMethodMapping с помощью AfterPropertiesSet InitializingBean.
    • Основная обработка вDetectHandlerMethods
  • Отсканировано RequestMapping и сгенерировано RequestMappingInfo

C31- RequestMappingHandlerMapping : 扫描的起点 , 因为类实现了 InitializingBean 接口 , 其核心基于 afterPropertiesSet
    M- afterPropertiesSet
        ?- 这个方法中主要对  RequestMappingInfo.BuilderConfiguration 进行配置 ,最后调用父类 afterPropertiesSet 方法
    
C32- AbstractHandlerMethodMapping
    M32_01- afterPropertiesSet
        - initHandlerMethods() : 核心方法 , 对 Handler Method 进行处理 -> M32_02
    M32_02- initHandlerMethods 
        ?- 扫描ApplicationContext中的bean,检测和注册处理程序方法
        - 获取所有的 BeanName , 调用 processCandidateBean 执行 -> M32_03
    M32_03- processCandidateBean
        - obtainApplicationContext().getType 获取具体的 Class 类
        - 通过 isHandler 判断是否包含 Controller 或者 RequestMapping 
            ?- RestController 继承了 Controller
            - 如果需要处理 , 调用 detectHandlerMethods 处理 -> M32_04
    M32_04- detectHandlerMethods
        - 同样的 , 通过传入的 handle (类的别名) 获取对应的 Class<?>
        - 获取所有的标注了注解的集合 Map<Method, T> -> ps:M32_04_01
            - getMappingForMethod 获取注解对应的 Condition -> ps:M32_04_01 详细流程
            - selectMethods
        FOR- 循环所有的 methods , 首先搜索可用的代理 , 注册对应的 Bean
            ?- 代理是 Spring 中很重要的一环 , 后面开单章详细说
    M32_05- registerHandlerMethod
        - 注册到 MappingRegistry
                
            
// ps:M32_04_01 流程详细分析
Map<Method, T> methods = MethodIntrospector.selectMethods(userType,(MethodIntrospector.MetadataLookup<T>) method -> {
    return getMappingForMethod(method, userType);
});

// 可以看到 , 其中核心的方法有2个 getMappingForMethod / selectMethods
getMappingForMethod 会返回Mapping 对应的 Condition , 例如 patternsCondition / methodCondition
    

// ps:M32_04_01
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
    // 此处对 RequestMapping 进行了扫描操作
    // 代码比较简单 , 就是 AnnotatedElementUtils.findMergedAnnotation 后构建了一个 RequestMappingInfo bean
    RequestMappingInfo info = createRequestMappingInfo(method);
    if (info != null) {
        // 此处是获取 Class 类上面的 RequestMapping
        RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
        if (typeInfo != null) {
             // 对2者进行组合 , 生成新的 Bean 对象
             // 注意是类 组合 方法
             // 主要是覆盖和合并 , url 比较特殊 , 变化为 /get -> /cookie/get
            info = typeInfo.combine(info);
        }
        
        // 前缀 path 的处理 , 该属性在配置 PathMatchConfigurer 时处理 , 可以通过 addPathPrefix 添加
        String prefix = getPathPrefix(handlerType);
        if (prefix != null) {
            info = RequestMappingInfo.paths(prefix).options(this.config).build().combine(info);
        }
    }
    return info;
}       

2.1.2 Регистрация компонента

Вышеупомянутая ссылка завершила сканирование метода сопоставления, и регистрация выполняется по этой ссылке.Регистрация в основном использует внутренние классы.MappingRegistry, внутри него есть несколько карт, которые используются для сохранения отношения сопоставления адресов.

(url-MappingRegistration/HandlerMethod/CorsConfiguration и т. д.)

            
C- AbstractHandlerMethodMapping
    VC33- MappingRegistry : 内部类 
        F33_01- Map<T, MappingRegistration<T>> registry = new HashMap<>();
        F33_02- Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<>();
        F33_03- MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<>();
        F33_04- Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<>();
        F33_05- Map<HandlerMethod, CorsConfiguration> corsLookup = new ConcurrentHashMap<>();
        M33_01- register(T mapping, Object handler, Method method)  -> PS:M33_01
            ?- 此处主要是为了构建 Map 集合 , 用于映射时匹配
            1- 通过 handler 和 Method 构建了使用时的主要单元Bean HandlerMethod
            2- 以 RequestMappingInfo(mapping) 为key 将 HandlerMethod 放入 Map (F33_02) 集合 
            3- 以 URL 字符串为 key , 将 以 RequestMappingInfo(mapping) 放入 Map 集合 (F33_03)
            4- 以 Name (策略类构建特殊的 mappingName)为 key , 将 handlerMethod 放入 Map (F33_04) -> PS:M33_02
            5- 以 HandlerMethod 为 key , 将 CorsConfiguration 放入集合 Map (F33_05)
            6- 以 RequestMappingInfo(mapping) 为key , 生成一个 MappingRegistration 放入集合 registry (F33_01)
            
        
// PS:M33_01 register 方法的关键和亮点
1- 使用 ReentrantReadWriteLock  保证多线程下访问的唯一性 , 在 finally 中解锁

// PS:M33_02 特殊的命名方式 RequestMappingInfoHandlerMethodMappingNamingStrategy
这里主要通过 RequestMappingInfoHandlerMethodMappingNamingStrategy 类生成对应的 Name
    
    
// M33_01 伪代码
public void register(T mapping, Object handler, Method method) {
    this.readWriteLock.writeLock().lock();
    try {
        // 1- 通过 handler 和 Method 构建了使用时的主要单元Bean HandlerMethod
        HandlerMethod handlerMethod = createHandlerMethod(handler, method);
        validateMethodMapping(handlerMethod, mapping);
        
        // 2- 以 RequestMappingInfo(mapping) 为key 将 HandlerMethod 放入 Map (F33_02) 集合 
        this.mappingLookup.put(mapping, handlerMethod);

        List<String> directUrls = getDirectUrls(mapping);
        for (String url : directUrls) {
            // 3- 以 URL 字符串为 key , 将 以 RequestMappingInfo(mapping) 放入 Map 集合 (F33_03)
            this.urlLookup.add(url, mapping);
        }

        String name = null;
        if (getNamingStrategy() != null) {
            name = getNamingStrategy().getName(handlerMethod, mapping);
            // 4- 以 Name (策略类构建特殊的 mappingName)为 key , 将 handlerMethod 放入 Map (F33_04) 
            addMappingName(name, handlerMethod);
        }
        
        // 构建跨域配置
        CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
        if (corsConfig != null) {
            5- 以 HandlerMethod 为 key , 将 CorsConfiguration 放入集合 Map (F33_05)
            this.corsLookup.put(handlerMethod, corsConfig);
        }
        // 6- 以 RequestMappingInfo(mapping) 为key , 生成一个 MappingRegistration 放入集合 registry
        this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));
    }finally {
        this.readWriteLock.writeLock().unlock();
    }
}
    

// 自此 , Mapper 的前期准备就完成了 

MvcUrl001.jpg

HandlerMappingCondition.jpg

2.2 Приход запроса

Ввод запроса по-прежнему выполняется через сервлет, давайте посмотрим на систему сервлетов PS:51_01.

2.2.1 Отправная точка: инициализация сервлета

MVC — это архитектура на основе сервлетов, начальной отправной точкой которой является initServlet через StandardWrapper. Путь потока следующий:

StandardWrapper -> GenericServlet (init) -> HttpServletBean (init) -> FrameworkServlet (initServletBean)

Глядя на очень старую картинку, у вас может быть предварительное представление о DispatcherServlet:

mvc-context-hierarchy.png

2.2.2 Ядро: инициализация DispatchServlet

C51- DispatcherServlet 
    M51_10- initHandlerMappings(ApplicationContext context)
        ?- 这个方法的核心操作就是初始化 handlerMappings -> PS:M51_10_01
        - 通过 BeanFactoryUtils 构建所有的 Mapping 类型结合  
        - 为 Map 进行排序
            

Логика обработки фильтра. Фильтр разделен на 3 этапа:инициализировать, перехватить, уничтожить, здесь в основном операция инициализации >>

Фильтр не является исключительным объектом Spring, он принадлежит собственной системе Servlet и в основном имеет следующие два процесса.


Подпроцесс 1: Контейнер основной обработки: StandardContext, добавить процесс фильтра

  • ServletWebServerApplicationContext # selfInitialize: получить все классы фильтров, которые необходимо обработать.
  • ServletContextInitializerBeans # ServletContextInitializerBeans : Получить определенные классы из Factory
  • RegistrationBean # onStartup : запустить и зарегистрировать FilterBean
  • StandardContext # addFilterDef : Добавить FilterDef , т.е. определение фильтра
                  
C- ServletWebServerApplicationContext
    M- selfInitialize
        - getServletContextInitializerBeans 其中会获取所有的 Beans  , 分别调用 beans.onStartup 启动
    M- getServletContextInitializerBeans
        - 核心处理逻辑在 ServletContextInitializerBeans 中
            
C- ServletContextInitializerBeans
    M- ServletContextInitializerBeans(ListableBeanFactory beanFactory,Class<? extends ServletContextInitializer>... initializerTypes)
        ?- 此方法中会获取 Servlet 和 Filter 的初始化类 --> TODO : 后续单独分析 Serlvet 时看


Подпроцесс 2: вызов процесса инициализации фильтра

Это асинхронная операция: обрабатывается асинхронно через процесс вызова, конкретный процесс находится в org.apache.catalina.core.

  • StandardContext # filterStart : Получить все фильтры в наборе filterDefs.entrySet
  • ApplicationFilterConfig: создайте ApplicationFilterConfig и вызовите в нем initFilter() для инициализации.
            
// Filter 的拦截调用
Filter 的核心思路就是 FilterChain 的链表调用 . 其起点为  ApplicationFilterChain 
C- ApplicationFilterChain
    F- private ApplicationFilterConfig[] filters = new ApplicationFilterConfig[0];
        - 选择一个 ApplicationFilterConfig , 调用 doFilter , 并且把当前对象传入
             ?- 后续就是调用链的逻辑
            
            
            
            
// HandlerInterceptor 的处理流程
- 与 Filter 不同的是 , 这里的 HandlerInterceptor 是 Spring 体系中的对象
- 拦截器主要在 WebMvcConfigurer 通过 InterceptorRegistry 手动注入             

@Override
public void addInterceptors(InterceptorRegistry registry) {
    logger.info("------> this is addInterceptors  <-------");
    registry.addInterceptor(new ConfigInterceptor());
}

// Interceptor 的初始化
Interceptor 的主要起点是 WebMvcConfigurationSupport
C- WebMvcConfigurationSupport
    M- requestMappingHandlerMapping
        - 其中会为当前 Mapping 添加 Interceptor

    
// Interceptor 的调用
Interceptor 的主调流程位于 HandlerExecutionChain
C- HandlerExecutionChain
    M- applyPreHandle
        - getInterceptors() 获取 HandlerInterceptor->LV001 数组
        FOR- 循环 HandlerInterceptor:LV001 调用 preHandle
            

// DispatcherServlet 衔接到 RequestMapping 映射关联
- ApplicationFilterChain 处理完成 Filter 
//  这里会进行一次继承类的循环调用
C- DispatcherServlet
    M- FrameworkServlet
        M- HttpServlet
            
- HttpServlet 执行 service(ServletRequest req, ServletResponse res) 方法
- FrameworkServlet 执行 service(HttpServletRequest request, HttpServletResponse response) 方法        
- HttpServlet 执行 service(HttpServletRequest request, HttpServletResponse response) 方法
- FrameworkServlet 执行 doGet 处理请求
- FrameworkServlet 执行 processRequest 开始完整处理
- 来到具体实现类 DispatcherServlet 

PS:51_01 Система семейства сервлетов ServletModule.png

PS: M51_10_01 Семейная система HandlerMapping HandlerMappingModule.png

2.3 Разбор запроса

Начальным методом сервлета является HttpServlet, а основным методом части Spring является реализация класса FrameworkServlet.

Разбор операций предварительной обработки

  • После преобразования Reuqest и Response в HttpServletRequest и HttpServletResponse
  • Вызовите метод службы (HttpServletRequest req, HttpServletResponse соответственно) в HttpServlet, чтобы вызвать метод doGet FrameworkServlet.
  • Наконец, введите базовый класс DispatcherServlet для обработки Http-запроса.

2.3.1 DispatcherServlet получает обработчик

Start -> HttpServlet
// TODO : Dispatch 具体流程以后单独分析 , 这里先简单过一下
C51- DispatcherServlet 
    M51_01- doService(HttpServletRequest request, HttpServletResponse response)
        ?- 执行 Service 处理请求
        - 调用 M51_02 执行具体的流程
    M51_02- doDispatch(HttpServletRequest request, HttpServletResponse response)
        1- WebAsyncUtils.getAsyncManager(request) : 准备异步处理管理器 WebAsyncManager -> PS:M51_02_01
        2- checkMultipart(request) : 尝试解析 Multipart Request -> PS:M51_02_02
        3- getHandler(processedRequest) : 获取具体需要执行的 Handler -> M51_03
            ?- 未找到对应的 Handler 会直接 return
        4- getHandlerAdapter(mappedHandler.getHandler()) : 确定当前请求的处理程序适配器 -> PS:M51_02_04
        5- 处理 Get-Head 请求 -> PS:M51_02_05
        - mappedHandler.applyPreHandle : 前置处理
        6- 调用 HandlerAdapter 处理 请求
        7- applyDefaultViewName : view 名称的解析 , 此环节不分析
        - mappedHandler.applyPostHandle : 后置处理 -> PS:M51_02_09
    M51_03- getHandler
    M51_04- getHandlerAdapter    

// M51_03 源代码
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    if (this.handlerMappings != null) {
        for (HandlerMapping mapping : this.handlerMappings) {
            HandlerExecutionChain handler = mapping.getHandler(request);
            if (handler != null) {
                return handler;
            }
        }
    }
    return null;
}


//  M51_04 getHandlerAdapter 源代码
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
    if (this.handlerAdapters != null) {
        for (HandlerAdapter adapter : this.handlerAdapters) {
            // 太常见的处理方式 , 循环处理中通过 support 判断
            if (adapter.supports(handler)) {
                return adapter;
            }
        }
    }
    throw new ServletException("....");
}

PS: M51_02_01 введение в WebAsyncManager

  • эффект: Центральный класс для управления асинхронной обработкой запросов, в основном используется как SPI, обычно не используется напрямую классами приложений.
  • основной: Обработка запросов и результатов через несколько разных потоков.
  • обработать
    1. Асинхронные сценарии начинаются с обычной обработки запросов в потоке (T1).
    2. Параллельную обработку запроса можно инициировать, вызвав startCallableProcessing или startDeferredResultProcessing, оба из которых производят результат в отдельном потоке (T2).
    3. Сохраните результат и отправьте запрос в контейнер для продолжения обработки сохраненного результата в третьем потоке (T3).
    4. В отправленном потоке (T3) доступ к сохраненному результату можно получить с помощью getConcurrentResult(), или его существование можно обнаружить с помощью hasConcurrentResult().

PS:M51_02_02 : Цель проверкиMultipart

Зачем нам здесь проверять checkMultipart?Нижний слой в основном выполняет следующие функции:

  • Определите, существует ли multipartResolver, и используйте преобразователь, чтобы определить, является ли он Multipart.
  • Если вышеуказанные условия соблюдены, multipartResolver.resolveMultipart(request) анализирует и возвращает новый запрос.
  • В противном случае верните исходный запрос

Грубо говоря, это обработка составного запроса, но опять возникает относительная проблема >>> Что такое составной запрос?

Функция:Составной HTTP-запрос — это HTTP-запрос, созданный HTTP-клиентом для отправки файлов и данных на HTTP-сервер.
Сцены:Он обычно используется браузерами и HTTP-клиентами для загрузки файлов на сервер.


PS: разница между M51_02_04 getHandler и getHandlerAdapter?

getHandler -> HandlerExecutionChain
getHandlerAdapter -> HandlerAdapter

// HandlerExecutionChain 的作用 :
- HandlerExecutionChain 是 Handler 的处理器链 , 其中包含了 HandlerInterceptor 的数组和对应的 Handler 

// HandlerAdapter 的作用 :
- 首先 , 看名字就知道 , 这是个适配器 , 适配器能在原有功能上扩展新的功能
- MVC框架SPI,允许参数化核心MVC工作流 , 该接口用于允许DispatcherServlet无限扩展。
- DispatcherServlet通过这个接口访问所有已安装的处理程序




HandlerAdapter_system.png

PS: M51_02_05 детали запроса GET/HEAD

Это место старое и интересное.Когда я первый раз соприкоснулся с кодом, тут была бы проблема.Понадобилось много времени, чтобы понять, что есть такая штука

Справочная документация @ блог woo woo woo.cn on.com/Long Yongzhe…

Функции :Метод HEAD аналогичен методу GET, за исключением того, что сервер не возвращает тело сообщения при ответе. В ответе на запрос HEAD метаинформация, содержащаяся в заголовке HTTP, СЛЕДУЕТ совпадать с ответным сообщением для запроса GET. Этот метод можно использовать для получения неявной метаинформации в запросе без передачи самого объекта. Также часто используется для проверки правильности гиперссылок, удобства использования и последних версий.
Функция:

  1. Запрашивать только заголовок ресурса;
  2. Проверить правильность гиперссылок;
  3. Проверьте, не была ли изменена веб-страница;
  4. Он в основном используется для автоматических поисковых роботов для получения информации о логотипах веб-страниц, получения информации о начальных значениях rss или передачи информации о сертификации безопасности и т. д.

PS: M51_02_09 С чем в основном связаны препроцессинг и постпроцессинг?

- mappedHandler.applyPreHandle : 前置处理
- mappedHandler.applyPostHandle : 后置处理 

// 这2个方法主要是对拦截器的处理 , 可以看到其中直接调用拦截器的方法
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
    HandlerInterceptor[] interceptors = getInterceptors();
    if (!ObjectUtils.isEmpty(interceptors)) {
        for (int i = 0; i < interceptors.length; i++) {
            HandlerInterceptor interceptor = interceptors[i];
            if (!interceptor.preHandle(request, response, this.handler)) {
                triggerAfterCompletion(request, response, null);
                return false;
            }
            this.interceptorIndex = i;
        }
    }
    return true;
}


M51_02 : DispatcherServlet # исходный код doDispatch


protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request;
        HandlerExecutionChain mappedHandler = null;
        boolean multipartRequestParsed = false;

        // 准备异步处理管理器 WebAsyncManager
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

        try {
            ModelAndView mv = null;
            Exception dispatchException = null;

            try {
                // 尝试解析 Multipart Request
                processedRequest = checkMultipart(request);
                multipartRequestParsed = (processedRequest != request);

                // 获取具体需要执行的 Handler
                mappedHandler = getHandler(processedRequest);
                if (mappedHandler == null) {
                    noHandlerFound(processedRequest, response);
                    return;
                }

                // 确定当前请求的处理程序适配器
                HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

                // 处理 Get-Head 请求
                String method = request.getMethod();
                boolean isGet = "GET".equals(method);
                if (isGet || "HEAD".equals(method)) {
                    long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                    if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
                        return;
                    }
                }

                // 拦截器前置处理
                if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                    return;
                }

                // 调用 HandlerAdapter 处理 请求
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

                if (asyncManager.isConcurrentHandlingStarted()) {
                    return;
                }

                applyDefaultViewName(processedRequest, mv);
                // 拦截器后置处理
                mappedHandler.applyPostHandle(processedRequest, response, mv);
            }
            catch (Exception ex) {
                dispatchException = ex;
            }
            catch (Throwable err) {
                // As of 4.3, we're processing Errors thrown from handler methods as well,
                // making them available for @ExceptionHandler methods and other scenarios.
                dispatchException = new NestedServletException("Handler dispatch failed", err);
            }
            processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
        }
        catch (Exception ex) {
            triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
        }
        catch (Throwable err) {
            triggerAfterCompletion(processedRequest, response, mappedHandler,
                    new NestedServletException("Handler processing failed", err));
        }
        finally {
            if (asyncManager.isConcurrentHandlingStarted()) {
                // Instead of postHandle and afterCompletion
                if (mappedHandler != null) {
                    mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
                }
            }
            else {
                // Clean up any resources used by a multipart request.
                if (multipartRequestParsed) {
                    cleanupMultipart(processedRequest);
                }
            }
        }
}

2.3.2 HandlerMapping разрешается в методы

Адаптер был получен выше, а ниже приведена подробная обработка адаптера.

Процесс обработки

Процесс обработки разделен на две части, которые просто:

  • getHandler получает используемый MethodHandler
  • getHandlerAdapter Получить обработчик адаптера
  • Обработка MethodHandler путем адаптации обработчика

Один: получить обработчик для обработки (getHandler)

DispatcherServlet # M51_03- getHandlerКак видно из , объект Handler получается через HandlerExecutionChain # getHandler

C76- AbstractHandlerMapping 
    M76_01- HandlerExecutionChain getHandler(HttpServletRequest request) : 构建处理链
        - getHandlerInternal(request) 获取处理的 Handler -> M32_01
        - 通过 Handler 构建 HandlerExecutionChain TODO 
        - 跨域配置的处理 CorsConfiguration
    M76_02- getHandlerInternal
        - 调用 父类 -> M32_01

C32- AbstractHandlerMethodMapping
   M32_01- getHandlerInternal(HttpServletRequest request)
        - 先配置了一个读锁
        - 调用 lookupHandlerMethod -> M32_02
   M32_02- lookupHandlerMethod : 对请求做真正的处理 , 获取 HandlerMethod
        1- 构建 List<Match> 用于存放匹配结果
        - MappingRegistry 通过 lookupPath 获取对象 List<RequestMappingInfo>
               ?- 底层实际上是调用上文初始化使用的 urlLookup.get(urlPath)
        - 调用 addMatchingMappings  , 将匹配的 Match放入 1 步构建的 List 中
        - 返回处理的 Method
        


M32_02 обработка запроса lookupHandlerMethod

protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
        List<Match> matches = new ArrayList<>();
        // 从 MappingRegistry 中获取 Mapping List
        List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
        if (directPathMatches != null) {
            addMatchingMappings(directPathMatches, matches, request);
        }
        if (matches.isEmpty()) {
            // No choice but to go through all mappings...
            addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
        }
        // 如果匹配项不为空 
        if (!matches.isEmpty()) {
             // {POST /test/getBody}
            Match bestMatch = matches.get(0);
            // 进行匹配 , 获取对应的 HandlerMethod
            if (matches.size() > 1) {
                Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
                matches.sort(comparator);
                bestMatch = matches.get(0);
                if (CorsUtils.isPreFlightRequest(request)) {
                    return PREFLIGHT_AMBIGUOUS_MATCH;
                }
                Match secondBestMatch = matches.get(1);
                if (comparator.compare(bestMatch, secondBestMatch) == 0) {
                    Method m1 = bestMatch.handlerMethod.getMethod();
                    Method m2 = secondBestMatch.handlerMethod.getMethod();
                    String uri = request.getRequestURI();
                    throw new IllegalStateException(
                            "Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
                }
            }
            
            // request.setAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, lookupPath) -> PS:M32_02_01
            request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
             
            handleMatch(bestMatch.mapping, lookupPath, request);
            return bestMatch.handlerMethod;
        }
        else {
            return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
        }
    }

// PS:M32_02_01
String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE = HandlerMapping.class.getName() + ".pathWithinHandlerMapping";

Шаг 2: Используйте полученный обработчик (HandlerAdapter.handle)

На шаге 1 мы получили HandleMethod , а дальнейшие действия связаны с логикой обработки

  • DispatcherServlet # doDispatch : запросить ввод
  • AbstractHandlerMethodAdapter # handle : Родительская запись RequestMappingAdapter
  • RequestMappingHandlerAdapter # handleInternal :Основной процесс обработки запроса
  • RequestMappingHandlerAdapter # invokeHandlerMethod
  • ServletInvocableHandlerMethod # invokeAndHandle
  • InvocableHandlerMethod # invokeForRequest
  • InvocableHandlerMethod # doInvoke
  • Метод #invoke: отражение Java в метод

Шаг 2-1: RequestMappingHandlerAdapter # обработка внутренней обработки

В этом методе, если требуется разрешение представления, вызовите метод обработчика RequestMapping, который подготавливает ModelAndView.

Основной :Подготовка ModelAndView

protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
            HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

        ServletWebRequest webRequest = new ServletWebRequest(request, response);
        try {
            WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
            ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
            
            // 扩展InvocableHandlerMethod,通过注册的HandlerMethodReturnValueHandler处理返回值
            ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
            if (this.argumentResolvers != null) {
                invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
            }
            if (this.returnValueHandlers != null) {
                invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
            }
            invocableMethod.setDataBinderFactory(binderFactory);
            invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
            
            // ModuleAndView 容器
            ModelAndViewContainer mavContainer = new ModelAndViewContainer();
            mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
            modelFactory.initModel(webRequest, mavContainer, invocableMethod);
            mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);

            AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
            asyncWebRequest.setTimeout(this.asyncRequestTimeout);

            WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
            asyncManager.setTaskExecutor(this.taskExecutor);
            asyncManager.setAsyncWebRequest(asyncWebRequest);
            
            // 异步管理器注册回调拦截器
            asyncManager.registerCallableInterceptors(this.callableInterceptors);
            asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);

            if (asyncManager.hasConcurrentResult()) {
                Object result = asyncManager.getConcurrentResult();
                mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
                asyncManager.clearConcurrentResult();
                invocableMethod = invocableMethod.wrapConcurrentResult(result);
            }

            invocableMethod.invokeAndHandle(webRequest, mavContainer);
            if (asyncManager.isConcurrentHandlingStarted()) {
                return null;
            }

            return getModelAndView(mavContainer, modelFactory, webRequest);
        }
        finally {
            webRequest.requestCompleted();
        }
    }


Детали параметра ModelAndViewContainer:

image.png




        
// Web 模块最终步骤     
C- InvocableHandlerMethod 

        
// 代码最终步骤 : 调用 Method 的 invoke 方法 , 去完成具体的逻辑        
C39- Method 
    M39_01- invoke(Object obj, Object... args)
        ?- 通过此方法调用最后的代理类 . java 反射包中方法 ,省略

2.4 Дополнение: Преобразование атрибутов

Основной класс преобразования свойств — RequestMappingHandlerAdapter, и его основная логика вызова:

  • C90- RequestMappingHandlerAdapter # M90_1- invokeHandlerMethod
  • C91- ServletInvocableHandlerMethod # M91_1- invokeAndHandle
  • C92-InvocableHandlerMethod # M92_1- invokeForRequest: обработать запрос
  • C93-InvocableHandlerMethod#M93_1-getMethodArgumentValues: получить параметры
  • C94- HandlerMethodArgumentResolverComposite # M94_1- resolveArgument : параметры разрешения
  • C95-RequestResponseBodyMethodProcessor # M95_1-readWithMessageConverters: параметры преобразования
  • C96- AbstractMessageConverterMethodArgumentResolver # M96_1-readWithMessageConverters : логика окончательной обработки
C- RequestMappingHandlerAdapter
    M- invokeHandlerMethod(HttpServletRequest request,HttpServletResponse response, HandlerMethod handlerMethod)
        - invocableMethod.invokeAndHandle(webRequest, mavContainer)

// Step 1 : 处理的入口
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
            Object... providedArgs) throws Exception {
    
    //  将 Request 解析处理
    Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
    setResponseStatus(webRequest);

    if (returnValue == null) {
        if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
            disableContentCachingIfNecessary(webRequest);
            mavContainer.setRequestHandled(true);
            return;
        }
    } else if (StringUtils.hasText(getResponseStatusReason())) {
        mavContainer.setRequestHandled(true);
        return;
    }

    mavContainer.setRequestHandled(false);
    try {
        this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
    } catch (Exception ex) {
        throw ex;
    }
}


// PS : invokeForRequest 中主要流程
C- InvocableHandlerMethod 
    M- invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,Object... providedArgs)    
        - getMethodArgumentValues(request, mavContainer, providedArgs)
            ?- 详见下文参数获取

Шаг 2: Получение параметров

    protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
            Object... providedArgs) throws Exception {

        MethodParameter[] parameters = getMethodParameters();
        if (ObjectUtils.isEmpty(parameters)) {
            return EMPTY_ARGS;
            }
        
        // 构建一个 Object 数组用于存放 param
        Object[] args = new Object[parameters.length];
        for (int i = 0; i < parameters.length; i++) {
            MethodParameter parameter = parameters[i];
            parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
            args[i] = findProvidedArgument(parameter, providedArgs);
            if (args[i] != null) {
                continue;
            }
            if (!this.resolvers.supportsParameter(parameter)) {
                throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
            }
            try {
                  // 解析参数主体
                args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
            }
            catch (Exception ex) {
                throw ex;
            }
        }
        return args;
}



public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
            NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {

    HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
    if (resolver == null) {
        throw new IllegalArgumentException("Unsupported parameter type [" +
                    parameter.getParameterType().getName() + "]. supportsParameter should be called first.");
    }
    return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
}
  

Шаг 3: RequestResponseBodyMethodProcessor анализирует RequestBody

 
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
            NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {

    parameter = parameter.nestedIfOptional();
    // 读取并且转换为相关对象
    Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());
    String name = Conventions.getVariableNameForParameter(parameter);

    if (binderFactory != null) {
        WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);
        if (arg != null) {
            validateIfApplicable(binder, parameter);
            if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
                throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());
            }
        }
        if (mavContainer != null) {
            mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());
        }
    }
    return adaptArgumentIfNecessary(arg, parameter);
}


// Step  : 转换操作入口
protected <T> Object readWithMessageConverters(NativeWebRequest webRequest, MethodParameter parameter,
            Type paramType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {

    HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
    ServletServerHttpRequest inputMessage = new ServletServerHttpRequest(servletRequest);

    Object arg = readWithMessageConverters(inputMessage, parameter, paramType);
    if (arg == null && checkRequired(parameter)) {
        throw new HttpMessageNotReadableException("Required request body is missing: " +
                    parameter.getExecutable().toGenericString(), inputMessage);
    }
    return arg;
}

Окончание этапа: окончательная обработка

При окончательной обработке параметры атрибута окончательно сопоставляются.

PS: система наследования M96_1_01

  • C- EmptyBodyCheckingHttpInputMessage
  • C- HttpInputMessage
  • C- HttpMessage

PS: M96_01_02, все преобразователи сообщений

  • ByteArrayHttpMessageConverter
  • StringHttpMessageConverter
  • ResourceHttpMessageConverter
  • ResourceRegionHttpMessageConverter
  • SourceHttpMessageConverter
  • AllEncompassingFormHttpMessageConverter
  • Jaxb2RootElementHttpMessageConverter
  • MappingJackson2HttpMessageConverter
protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter,
            Type targetType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {

    MediaType contentType;
    boolean noContentType = false;
    try {
         // 内容类型 : application/json;charset=UTF-8
        contentType = inputMessage.getHeaders().getContentType();
    } catch (InvalidMediaTypeException ex) {
        throw new HttpMediaTypeNotSupportedException(ex.getMessage());
    }
    if (contentType == null) {
        noContentType = true;
        contentType = MediaType.APPLICATION_OCTET_STREAM;
    }
    
    // Controller 类
    Class<?> contextClass = parameter.getContainingClass();
    // 实体类 class
    Class<T> targetClass = (targetType instanceof Class ? (Class<T>) targetType : null);
    if (targetClass == null) {
        ResolvableType resolvableType = ResolvableType.forMethodParameter(parameter);
        targetClass = (Class<T>) resolvableType.resolve();
    }
    
    // 获取请求类型
    HttpMethod httpMethod = (inputMessage instanceof HttpRequest ? ((HttpRequest) inputMessage).getMethod() : null);
    Object body = NO_VALUE;

    EmptyBodyCheckingHttpInputMessage message;
    try {
        // PS: M96_1_01
        message = new EmptyBodyCheckingHttpInputMessage(inputMessage);
    
     // 获取所有的 HttpMessageConverter -> PS:M96_01_02
    for (HttpMessageConverter<?> converter : this.messageConverters) {
        Class<HttpMessageConverter<?>> converterType = (Class<HttpMessageConverter<?>>) converter.getClass();
        GenericHttpMessageConverter<?> genericConverter =
            (converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter<?>) converter : null);
         // 此处主要是 MappingJackson2HttpMessageConverter
        if (genericConverter != null ? genericConverter.canRead(targetType, contextClass, contentType) :
            (targetClass != null && converter.canRead(targetClass, contentType))) {
            if (message.hasBody()) {
                 // 转换核心流程 -> PS:M96_01_02
                 // 主要为 Header 
                HttpInputMessage msgToUse =
                    getAdvice().beforeBodyRead(message, parameter, targetType, converterType);
                body = (genericConverter != null ? genericConverter.read(targetType, contextClass, msgToUse) :
                    ((HttpMessageConverter<T>) converter).read(targetClass, msgToUse));
                body = getAdvice().afterBodyRead(body, msgToUse, parameter, targetType, converterType);
            } else {
                body = getAdvice().handleEmptyBody(null, message, parameter, targetType, converterType);
            }
                break;
        }
            }
    } catch (IOException ex) {
        throw new HttpMessageNotReadableException("I/O error while reading input message", ex, inputMessage);
    }

    if (body == NO_VALUE) {
        if (httpMethod == null || !SUPPORTED_METHODS.contains(httpMethod) ||(noContentType && !message.hasBody())) {
            return null;
        }
        throw new HttpMediaTypeNotSupportedException(contentType, this.allSupportedMediaTypes);
    }

    MediaType selectedContentType = contentType;
    Object theBody = body;
    LogFormatUtils.traceDebug(logger, traceOn -> {
        String formatted = LogFormatUtils.formatValue(theBody, !traceOn);
        return "Read \"" + selectedContentType + "\" to [" + formatted + "]";
    });

    return body;
}


PS: M96_01_02 Сведения о параметрах

convert_body.jpg

convert_maptoUser.jpg

2.5 Дополнение: Преобразование ответа

Преобразование атрибутов в основном осуществляется в HandlerMethodReturnValueHandlerComposite, и основная логика такова:

  • C- InvocableHandlerMethod # invokeForRequest
  • C- ServletInvocableHandlerMethod # invokeAndHandle
  • C- HandlerMethodReturnValueHandlerComposite # handleReturnValue : вызов обработчика для обработки
  • C- RequestResponseBodyMethodProcessor # handleReturnValue
// - C91- ServletInvocableHandlerMethod  # M91_1- invokeAndHandle
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {

    Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
    setResponseStatus(webRequest);

    if (returnValue == null) {
        if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
            disableContentCachingIfNecessary(webRequest);
            mavContainer.setRequestHandled(true);
            return;
        }
    } else if (StringUtils.hasText(getResponseStatusReason())) {
        mavContainer.setRequestHandled(true);
        return;
    }

    mavContainer.setRequestHandled(false);
    try {
        // 此处进行 return 处理 : HandlerMethodReturnValueHandlerComposite
        this.returnValueHandlers.handleReturnValue(
        returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
    } catch (Exception ex) {
        throw ex;
    }
}


// HandlerMethodReturnValueHandlerComposite  # handleReturnValue
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
			ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {

    HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
    if (handler == null) {
        throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
    }
    handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}

// RequestResponseBodyMethodProcessor
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
    ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
    throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {

    mavContainer.setRequestHandled(true);
    ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
    ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);

    // 此处进行返回处理 , 本次先不深入 , 以后单独看看返回的处理
    writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
}

Суммировать

Наконец-то я закончил писать MVC, изначально это было небольшое пространство, но чем больше я писал, тем сложнее было понятно объяснить.

初始化阶段中 , RequestMappingHandlerMappingперечислитьinitHandlerMethodsЗавершите сканирование всех bean-компонентов и, наконец, соберитеRequestMappingInfoпройти сноваMappingRegistryзарегистрироваться

在调用的阶段, первый звонокDispatcherServlet, он позвонитAbstractHandlerMappingВойдите в getHandlerHandlerMapping( lookupHandlerMethod вызываетMappingRegistryсоответствовать)

匹配完成后, придетAdapterосновная логика,RequestMappingHandlerAdapterв ходе выполненияModelAndViewДождаться обработки и одновременно позвонитьServletInvocableHandlerMethodСопоставление методов

在方法映射阶段, придетInvocableHandlerMethodclass, в первую очередь парсинг и сопоставление параметров, и, наконец, черезAbstractMessageConverterMethodArgumentResolverа такжеHttpMessageConverterсовместная обработка

映射完成后, будуinvokeсоответствующему методу, при этом передаваяHandlerMethodReturnValueHandlerCompositeа такжеRequestResponseBodyMethodProcessorОбработка ответа

Список изменений

V20210821 Обновить блок-схему и сводку