@@ -407,6 +407,73 @@ initFlashMapManager方法会向容器注册SessionFlashMapManager对象,类图
407407
408408此接口和FlashMap搭配使用,用于在** 请求重定向时保存/传递参数** 。
409409
410+ ## HandlerMapping初始化
411+
412+ 此接口用以根据请求的URL寻找合适的处理器。从前面配置解析一节可以看出,我们的容器中有三个HandlerMapping实现,下面进行分别说明。
413+
414+ ### RequestMappingHandlerMapping
415+
416+ 此实现根据@Controller 和@RequestMapping 注解完成解析。类图(忽略部分接口):
417+
418+ ![ RequestMappingHandlerMapping类图] ( images/RequestMappingHandlerMapping.jpg )
419+
420+ 初始化的入口位于AbstractHandlerMethodMapping的afterPropertiesSet方法,afterPropertiesSet调用了initHandlerMethods:
421+
422+ ``` java
423+ protected void initHandlerMethods() {
424+ // 获取容器中所有的bean
425+ String [] beanNames = (this . detectHandlerMethodsInAncestorContexts ?
426+ BeanFactoryUtils . beanNamesForTypeIncludingAncestors(getApplicationContext(), Object . class) : getApplicationContext(). getBeanNamesForType(Object . class));
427+ for (String beanName : beanNames) {
428+ if (! beanName. startsWith(SCOPED_TARGET_NAME_PREFIX )) {
429+ Class<?> beanType = null ;
430+ beanType = getApplicationContext(). getType(beanName);
431+ // isHandler方法的原理:
432+ // 判断类上有没有@Controller注解或者是@RequestMapping注解
433+ if (beanType != null && isHandler(beanType)) {
434+ detectHandlerMethods(beanName);
435+ }
436+ }
437+ }
438+ handlerMethodsInitialized(getHandlerMethods());
439+ }
440+ ```
441+
442+ detectHandlerMethods方法将反射遍历类中所有的public方法,如果方法上含有@RequestMapping 注解,那么将方法上的路径与类上的基础路径(如果有)进行合并,之后将映射(匹配关系)注册到MappingRegistry中。
443+
444+ 注意,** 类上的@RequestMapping 注解只能作为基路径存在,也就是说,如果类里面没有任何的方法级@RequestMapping 注解,那么类上的注解是没有意义的** 。这一点可以从实验和源码上得到证实。
445+
446+ 下面我们关注一下映射关系是如何保存(注册)的。
447+
448+ 内部类AbstractHandlerMethodMapping.MappingRegistry是映射的载体,类图:
449+
450+ ![ MappingRegistry类图] ( images/MappingRegistry.jpg )
451+
452+ 其register方法简略版源码:
453+
454+ ``` java
455+ public void register(T mapping, Object handler, Method method) {
456+ HandlerMethod handlerMethod = createHandlerMethod(handler, method);
457+ this . mappingLookup. put(mapping, handlerMethod);
458+ List<String > directUrls = getDirectUrls(mapping);
459+ for (String url : directUrls) {
460+ this . urlLookup. add(url, mapping);
461+ }
462+ String name = null ;
463+ if (getNamingStrategy() != null ) {
464+ name = getNamingStrategy(). getName(handlerMethod, mapping);
465+ addMappingName(name, handlerMethod);
466+ }
467+ CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
468+ if (corsConfig != null ) {
469+ this . corsLookup. put(handlerMethod, corsConfig);
470+ }
471+ this . registry. put(mapping, new MappingRegistration<T > (mapping, handlerMethod, directUrls, name));
472+ }
473+ ```
474+
475+
476+
410477# 请求响应
411478
412479我们先来看一下入口在哪。众所周知,Servlet标准定义了所有请求先由service方法处理,如果是get或post方法,那么再交由doGet或是doPost方法处理。
@@ -431,3 +498,14 @@ Spring要覆盖此方法的目的在于拦截PATCH请求,PATCH请求与PUT类
431498
432499FrameworkServlet同样也覆盖了doGet和doPost方法,两者只是调用processRequest方法。
433500
501+ ## 请求上下文
502+
503+ Spring MVC会在请求分发之前进行上下文的准备工作,含两部分:
504+
505+ 1 . 将地区(Locale)和请求属性以ThreadLocal的方法与当前线程进行关联,分别可以通过LocaleContextHolder和RequestContextHolder进行获取。
506+ 2 . 将WebApplicationContext、FlashMap等组件放入到Request属性中。
507+
508+ ## 请求分发
509+
510+
511+
0 commit comments