Прежде всего, поделитесь всеми предыдущими статьями, ставьте лайки, добавляйте в избранное и пересылайте три раза подряд. >>>>😜😜😜
Сборник статей:🎁nuggets.capable/post/694164…
Github :👉github.com/black-ant
Резервное копирование CASE:👉git ee.com/ant black/wipe…
Введение
Цель статьи:
- Разберите соответствующую логику в основном процессе SpringMVC
- Определить соответствующие параметры процесса
Детали, не вошедшие в эту статью:
- Конфигурация MVC
- Инициализация контейнера
- загрузка контейнера
- Ждать....
2. Анализ процесса
Этот основной процесс включает в себя следующие части:
- Сканирование обработки аннотаций
- запрос на перехват
- запрошенная конверсия
- окончательная обработка заявки
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 的前期准备就完成了
2.2 Приход запроса
Ввод запроса по-прежнему выполняется через сервлет, давайте посмотрим на систему сервлетов PS:51_01.
2.2.1 Отправная точка: инициализация сервлета
MVC — это архитектура на основе сервлетов, начальной отправной точкой которой является initServlet через StandardWrapper. Путь потока следующий:
StandardWrapper -> GenericServlet (init) -> HttpServletBean (init) -> FrameworkServlet (initServletBean)
Глядя на очень старую картинку, у вас может быть предварительное представление о DispatcherServlet:
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 Система семейства сервлетов
PS: M51_10_01 Семейная система HandlerMapping
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, обычно не используется напрямую классами приложений.
- основной: Обработка запросов и результатов через несколько разных потоков.
-
обработать
- Асинхронные сценарии начинаются с обычной обработки запросов в потоке (T1).
- Параллельную обработку запроса можно инициировать, вызвав startCallableProcessing или startDeferredResultProcessing, оба из которых производят результат в отдельном потоке (T2).
- Сохраните результат и отправьте запрос в контейнер для продолжения обработки сохраненного результата в третьем потоке (T3).
- В отправленном потоке (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通过这个接口访问所有已安装的处理程序
PS: M51_02_05 детали запроса GET/HEAD
Это место старое и интересное.Когда я первый раз соприкоснулся с кодом, тут была бы проблема.Понадобилось много времени, чтобы понять, что есть такая штука
Справочная документация @ блог woo woo woo.cn on.com/Long Yongzhe…
Функции :Метод HEAD аналогичен методу GET, за исключением того, что сервер не возвращает тело сообщения при ответе. В ответе на запрос HEAD метаинформация, содержащаяся в заголовке HTTP, СЛЕДУЕТ совпадать с ответным сообщением для запроса GET. Этот метод можно использовать для получения неявной метаинформации в запросе без передачи самого объекта. Также часто используется для проверки правильности гиперссылок, удобства использования и последних версий.
Функция:
- Запрашивать только заголовок ресурса;
- Проверить правильность гиперссылок;
- Проверьте, не была ли изменена веб-страница;
- Он в основном используется для автоматических поисковых роботов для получения информации о логотипах веб-страниц, получения информации о начальных значениях 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:
// 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 Сведения о параметрах
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 Обновить блок-схему и сводку