分析版本Spring5.0.9.release,Springboot2.0.3.releasehtml
spring-webmvc的META-INFO/spring.handles文件中,有MvcNamespaceHandler,這是用來解析標籤的,來看下MvcNamespaceHandler的init(),以下List-1,咱們暫時只關注AnnotationDrivenBeanDefinitionParser。java
List-1git
@Override public void init() { registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser()); registerBeanDefinitionParser("default-servlet-handler", new DefaultServletHandlerBeanDefinitionParser()); registerBeanDefinitionParser("interceptors", new InterceptorsBeanDefinitionParser()); registerBeanDefinitionParser("resources", new ResourcesBeanDefinitionParser()); registerBeanDefinitionParser("view-controller", new ViewControllerBeanDefinitionParser()); registerBeanDefinitionParser("redirect-view-controller", new ViewControllerBeanDefinitionParser()); registerBeanDefinitionParser("status-controller", new ViewControllerBeanDefinitionParser()); registerBeanDefinitionParser("view-resolvers", new ViewResolversBeanDefinitionParser()); registerBeanDefinitionParser("tiles-configurer", new TilesConfigurerBeanDefinitionParser()); registerBeanDefinitionParser("freemarker-configurer", new FreeMarkerConfigurerBeanDefinitionParser()); registerBeanDefinitionParser("groovy-configurer", new GroovyMarkupConfigurerBeanDefinitionParser()); registerBeanDefinitionParser("script-template-configurer", new ScriptTemplateConfigurerBeanDefinitionParser()); registerBeanDefinitionParser("cors", new CorsBeanDefinitionParser()); }
AnnotationDrivenBeanDefinitionParser的parse方法裏面有不少代碼,可是咱們只關注倆個,以下List-2:github
List-2web
@Override @Nullable public BeanDefinition parse(Element element, ParserContext context) { ... RootBeanDefinition handlerMappingDef = new RootBeanDefinition(RequestMappingHandlerMapping.class); ... // Ensure BeanNameUrlHandlerMapping (SPR-8289) and default HandlerAdapters are not "turned off" MvcNamespaceUtils.registerDefaultComponents(context, source); return null; }
RequestMappingHandlerMapping的繼承圖以下圖1所示:spring
圖1mvc
圖1中,AbstractHandlerMethodMapping實現了接口InitializingBean,實現了afterPropertiesSet(),若是瞭解SpringIOC,應該知道這個方法意味着什麼,Spring在建立Bean的時候會調用這個方法。app
List-3cors
@Override public void afterPropertiesSet() { initHandlerMethods(); }
由List-3知道afterPropertiesSet調用了initHandlerMethods(),以下List-4,首先從ApplicationContext或者全部的beanName,以後循環它們。isHandler方法由子類RequestMappingHandlerMapping實現,判斷類上是否有Controller或者RequestMapping註解,因此即便不加上@Controller,只是用@RequestMapping註解也能夠。List-4中的handlerMethodsInitialized是空方法,不用管。ide
List-4
protected void initHandlerMethods() { if (logger.isDebugEnabled()) { logger.debug("Looking for request mappings in application context: " + getApplicationContext()); } String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ? BeanFactoryUtils.beanNamesForTypeIncludingAncestors(obtainApplicationContext(), Object.class) : obtainApplicationContext().getBeanNamesForType(Object.class)); for (String beanName : beanNames) { if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) { 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.isDebugEnabled()) { logger.debug("Could not resolve target class for bean with name '" + beanName + "'", ex); } } if (beanType != null && isHandler(beanType)) { detectHandlerMethods(beanName); } } } handlerMethodsInitialized(getHandlerMethods()); }
來看List-4中的方法detectHandlerMethods,傳入的是String類型的bean name,以下List-5,首先獲取bean name對應的Class,getMappingForMethod在RequestMappingHandlerMapping中實現,如List-6所示,判斷方法上是否有RequestMapping註解,若是有,則獲取參數、path、header等信息,用builder模式構造出RequestMappingInfo。MethodIntrospector.selectMethods會遍歷類上的方法,因此總體上對類的全部方法進行是否有RequestMapping的檢查。須要注意的是,相似GetMapping等是組合註解,它們仍是基於RequestMapping,因此方法上有GetMapping/PostMapping等都會會掃描並建立出對應的RequestMappingInfo。
List-5
protected void detectHandlerMethods(Object handler) { Class<?> handlerType = (handler instanceof String ? obtainApplicationContext().getType((String) handler) : handler.getClass()); if (handlerType != null) { Class<?> userType = ClassUtils.getUserClass(handlerType); 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.isDebugEnabled()) { logger.debug(methods.size() + " request handler methods found on " + userType + ": " + methods); } methods.forEach((method, mapping) -> { Method invocableMethod = AopUtils.selectInvocableMethod(method, userType); registerHandlerMethod(handler, invocableMethod, mapping); }); } }
List-6
@Override @Nullable protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) { RequestMappingInfo info = createRequestMappingInfo(method); if (info != null) { RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType); if (typeInfo != null) { info = typeInfo.combine(info); } } return info; } @Nullable private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) { RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class); RequestCondition<?> condition = (element instanceof Class ? getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element)); return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null); } protected RequestMappingInfo createRequestMappingInfo( RequestMapping requestMapping, @Nullable RequestCondition<?> customCondition) { RequestMappingInfo.Builder builder = RequestMappingInfo .paths(resolveEmbeddedValuesInPatterns(requestMapping.path())) .methods(requestMapping.method()) .params(requestMapping.params()) .headers(requestMapping.headers()) .consumes(requestMapping.consumes()) .produces(requestMapping.produces()) .mappingName(requestMapping.name()); if (customCondition != null) { builder.customCondition(customCondition); } return builder.options(this.config).build(); }
List-5中的registerHandlerMethod,會將獲得的Mapper信息註冊到mappingRegistry到,這個mappingRegistry後面在DispatcherServlet中使用到。注意傳入到registerHandlerMethod方法的handler是String類型的bean name。
Springboot中,RequestMappingHandlerMapping是經過@Configuration方式注入的,以下List-7
List-7
@Bean public RequestMappingHandlerMapping requestMappingHandlerMapping() { RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping(); ...