@Configuration @ConditionalOnWebApplication(type = Type.SERVLET) @ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class }) // WebMvcConfigurationSupport這個Bean不存在纔會啓動 這個類的做用是若是@EnableWebMvc註解的自動裝配 兩個是互斥的 @ConditionalOnMissingBean(WebMvcConfigurationSupport.class) //優先級 @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10) //這些自動配置加載以後加載 @AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class, ValidationAutoConfiguration.class }) public class WebMvcAutoConfiguration { }
功能是java
handlerMapping 處理請求和controller直接的關係
handlerAdapter 處理請求和各類controller的適配問題. 好比適配servlet 普通controller 或者註解controller.
ViewResolver 視圖解析器
HandlerInterceptor 攔截器spring
這個類由一個handler和若干的HandlerInterceptor構成。那麼這個類的做用就顯而易見了,就是將攔截器和handle組合起來執行。就是對handle進行了包裝。安全
public class HandlerExecutionChain { 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]; // 直到遇到一個false的 if (!interceptor.preHandle(request, response, this.handler)) { // false 就執行triggerAfterCompletion triggerAfterCompletion(request, response, null); return false; } // 記錄false的位置 this.interceptorIndex = i; } } return true; } /** * Apply postHandle methods of registered interceptors. */ void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv) throws Exception { HandlerInterceptor[] interceptors = getInterceptors(); if (!ObjectUtils.isEmpty(interceptors)) { for (int i = interceptors.length - 1; i >= 0; i--) { HandlerInterceptor interceptor = interceptors[i]; interceptor.postHandle(request, response, this.handler, mv); } } } /** * Trigger afterCompletion callbacks on the mapped HandlerInterceptors. * Will just invoke afterCompletion for all interceptors whose preHandle invocation * has successfully completed and returned true. */ void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) throws Exception { HandlerInterceptor[] interceptors = getInterceptors(); if (!ObjectUtils.isEmpty(interceptors)) { // 從false的位置開始. 向前遍歷順序執行. for (int i = this.interceptorIndex; i >= 0; i--) { HandlerInterceptor interceptor = interceptors[i]; try { interceptor.afterCompletion(request, response, this.handler, ex); } catch (Throwable ex2) { logger.error("HandlerInterceptor.afterCompletion threw exception", ex2); } } } } }
public final class MappedInterceptor implements HandlerInterceptor { @Nullable private final String[] includePatterns; @Nullable private final String[] excludePatterns; private final HandlerInterceptor interceptor; @Nullable private PathMatcher pathMatcher; public boolean matches(String lookupPath, PathMatcher pathMatcher) { PathMatcher pathMatcherToUse = (this.pathMatcher != null ? this.pathMatcher : pathMatcher); if (!ObjectUtils.isEmpty(this.excludePatterns)) { for (String pattern : this.excludePatterns) { if (pathMatcherToUse.match(pattern, lookupPath)) { return false; } } } if (ObjectUtils.isEmpty(this.includePatterns)) { return true; } for (String pattern : this.includePatterns) { if (pathMatcherToUse.match(pattern, lookupPath)) { return true; } } return false; } }
true 攔截 false 不攔截併發
若是路徑在excludePatterns中,則不攔截。若是不在,那麼若includePatterns爲空,則攔截,不然在includePatterns中才攔截。mvc
比較關鍵的兩個地方是:app
HandlerMapping是用來處理請求與handler對象的對應關係的。cors
HandlerMapping主要包含getHandler這個方法。ide
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
DispatcherServlet就是經過調用HandlerMapping的getHandler來進行找到request對應的handler的。post
這個方法是在項目啓動建立Bean的時候調用的. 由於他實現了InitializingBean接口.
AbstractHandlerMethodMapping#afterPropertiesSet()->initHandlerMethods()ui
protected void initHandlerMethods() { for (String beanName : getCandidateBeanNames()) { if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) { processCandidateBean(beanName); } } handlerMethodsInitialized(getHandlerMethods()); } protected String[] getCandidateBeanNames() { return (this.detectHandlerMethodsInAncestorContexts ? BeanFactoryUtils.beanNamesForTypeIncludingAncestors(obtainApplicationContext(), Object.class) : obtainApplicationContext().getBeanNamesForType(Object.class)); } protected void processCandidateBean(String beanName) { Class<?> beanType = null; try { beanType = obtainApplicationContext().getType(beanName); } catch (Throwable ex) { // An unresolvable bean type, probably from a lazy bean - let's ignore it. if (logger.isTraceEnabled()) { logger.trace("Could not resolve type for bean '" + beanName + "'", ex); } } //判斷是否Handler 標註了@Controller或者@RequestMapping if (beanType != null && isHandler(beanType)) { //解析controller的方法並註冊 detectHandlerMethods(beanName); } } @Override protected boolean isHandler(Class<?> beanType) { return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) || AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class)); } protected void detectHandlerMethods(Object handler) { //獲取到controller的class Class<?> handlerType = (handler instanceof String ? obtainApplicationContext().getType((String) handler) : handler.getClass()); if (handlerType != null) { Class<?> userType = ClassUtils.getUserClass(handlerType); // 完成了controller中方法和RequestMappingInfo的綁定. 例如TestController類中的 tet方法 請求路徑信息的綁定 //RequestMappingInfo 表明了請求的一些信息 如:路徑信息(/test/get) method(GET) Map<Method, T> methods = MethodIntrospector.selectMethods(userType, (MethodIntrospector.MetadataLookup<T>) method -> { try { return getMappingForMethod(method, userType); } catch (Throwable ex) { throw new IllegalStateException("Invalid mapping on handler class [" + userType.getName() + "]: " + method, ex); } }); if (logger.isTraceEnabled()) { logger.trace(formatMappings(userType, methods)); } methods.forEach((method, mapping) -> { Method invocableMethod = AopUtils.selectInvocableMethod(method, userType); //經過MappingRegistry#registry()方法註冊. registerHandlerMethod(handler, invocableMethod, mapping); }); } }
obtainApplicationContext().getBeanNamesForType(Object.class); 這個方法. 調用鏈是
AbstractApplicationContext#getBeanNamesForType(Object.class) -> DefaultListableBeanFactory#getBeanNamesForType(type, true(包含非單例的), true(包含提前加載的)); ->
DefaultListableBeanFactory#doGetBeanNamesForType() 從DefaultListableBeanFactory中定義的註冊表中獲取 type爲Object的類名字. 固然包含Controller.
通過這些全部的controller就都解析完成了.
protected void registerHandlerMethod(Object handler, Method method, T mapping) { this.mappingRegistry.register(mapping, handler, method); }
handler 值是Controller的名字. 也就是beanName. spring ioc容器中註冊的名字. 若是你沒有改過, 類的第一個字母小寫. 例如TestController. handler = testController.
invocableMethod 是能夠執行的method方法. 標記了@RequestMapping註解的
mapping RequestMappingInfo類 一個請求和handler 說使用的信息. 都在這裏. 什麼狀況路徑啊 方法啊 參數啊 header啊
handler和mapping的註冊, 內部調用了MappingRegistry類. 顧名思義. 內部持有4個註冊表和一個讀寫鎖. 這個讀寫鎖就是Controller在併發環境下 線程安全的關鍵
class MappingRegistry { private final Map<T, MappingRegistration<T>> registry = new HashMap<>(); //RequestMappingInfo和HandlerMethod的映射關係. LinkedHashMap<> 可以保存插入順序的和遍歷順序一致的HashMap private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<>(); //url(路徑的所有 包含Controller和方法上的path))和mappinginfo 映射關係 private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<>(); //類名字中提取大寫的字母及其執行方法的名字 例如TestController和要執行的方法名稱爲tet. 是方法名不是url 那麼這個key=TC#tet. private final Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<>(); private final Map<HandlerMethod, CorsConfiguration> corsLookup = new ConcurrentHashMap<>(); //讀寫鎖 提供讀寫效率並能保證線程安全 private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock(); public void register(T mapping, Object handler, Method method) { this.readWriteLock.writeLock().lock(); try { //HandlerMethod類 就是將 beanName和BeanFactory以及要執行的method封裝成了不可變對象. 用於數據傳輸和記錄信息 HandlerMethod handlerMethod = createHandlerMethod(handler, method); //驗證RequestMappingInfo(不可變類)是否已經註冊過了而且仍是惟一的. assertUniqueMethodMapping(handlerMethod, mapping); // 註冊RequestMappingInfo和HandlerMethod的映射 this.mappingLookup.put(mapping, handlerMethod); // 註冊URL和mapping 信息 List<String> directUrls = getDirectUrls(mapping); for (String url : directUrls) { this.urlLookup.add(url, mapping); } String name = null; if (getNamingStrategy() != null) { name = getNamingStrategy().getName(handlerMethod, mapping); //nameLookup註冊 addMappingName(name, handlerMethod); } // 處理CrossOrigin註解 CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping); if (corsConfig != null) { this.corsLookup.put(handlerMethod, corsConfig); } // MappingRegistration不可變類, 將RequestMappingInfo HandlerMethod directUrls(/test/get) name(TC#tet)封裝. this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name)); } finally { this.readWriteLock.writeLock().unlock(); } } }
Controller的寫是有讀寫鎖保護的.
DispatcherServlet#doDispatch 扎到了這樣的代碼
// Determine handler for the current request. HandlerExecutionChain mappedHandler = getHandler(processedRequest); if (mappedHandler == null) { noHandlerFound(processedRequest, response); return; } @Nullable 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; } //AbstractHandlerMapping public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { Object handler = getHandlerInternal(request); if (handler == null) { handler = getDefaultHandler(); } if (handler == null) { return null; } // Bean name or resolved handler? if (handler instanceof String) { String handlerName = (String) handler; handler = obtainApplicationContext().getBean(handlerName); } HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request); if (logger.isTraceEnabled()) { logger.trace("Mapped to " + handler); } else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) { logger.debug("Mapped to " + executionChain.getHandler()); } if (CorsUtils.isCorsRequest(request)) { CorsConfiguration globalConfig = this.corsConfigurationSource.getCorsConfiguration(request); CorsConfiguration handlerConfig = getCorsConfiguration(handler, request); CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig); executionChain = getCorsHandlerExecutionChain(request, executionChain, config); } return executionChain; } @Override protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception { String lookupPath = getUrlPathHelper().getLookupPathForRequest(request); this.mappingRegistry.acquireReadLock(); try { HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request); return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null); } finally { this.mappingRegistry.releaseReadLock(); } }
handlerMappings這是一個集合. 裏面存放着全部HeadlerMapping. 問題是這些HM什麼時候初始化的呢. 咱們都知道DispatcherServlet是本質是Servlet. 那麼Servlet聲明週期分 init(初始化)) service(執行邏輯) destroy(銷燬)
handlerMappings屬性的初始化必定和這個有關. DispatcherServlet類的初始化是經過Spring自動裝載完成的. init方法會去加載 在經過mappingRegistry讀取Handler的時候. 也是會加讀鎖的.
在springMVC的執行流行流程中,當執行完handlerMapping獲取到request對應的HandlerExecutionChain以後,下一步就是調用HandlerAdapter執行對應的Handler。
public interface HandlerAdapter { boolean supports(Object handler); @Nullable ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception; long getLastModified(HttpServletRequest request, Object handler); }
supports()是用來判斷一個handler是否屬於該HandlerAdapter的,一個典型的實現方式是判斷該handler的類型,一般來講一個HandlerAdapter只支持一種類型的handler。
handle()的做用是使用給定的handler去處理請求。
getLastModified()的做用和HttpServlet中的getLastModified一致,如果handler不支持getLastModified則直接返回-1。