SpringMvc之構造對映關係
springmvc正是通過構造請求的模式和其對應的method,然後通過反射機制執行方法,並將結果返回。
本篇就分析下對映關係的初始化過程
一 關鍵類講解
HandlerMethod
public class HandlerMethod { /** Logger that is available to subclasses */ protected final Log logger = LogFactory.getLog(getClass()); private final Object bean; private final BeanFactory beanFactory;private final Class<?> beanType; private final Method method;//實際的method private final Method bridgedMethod; private final MethodParameter[] parameters;//入參
它有一個非常重要的子類,ServletInvocableHandlerMethod
ServletInvocableHandlerMethod這個類在HandlerAdapter對每個請求處理過程中,都會例項化一個出來(上面提到的屬性由HandlerAdapter進行設定),分別對請求和返回進行處理。 (RequestMappingHandlerAdapter原始碼,例項化ServletInvocableHandlerMethod的時候分別set了上面提到的重要屬性)
MethodParameter
public class MethodParameter { private final Method method;//方法名 private final Constructor<?> constructor; private final int parameterIndex;//該引數的下標,表示第幾個入參 private int nestingLevel = 1; /** Map from Integer level to Integer type index */ Map<Integer, Integer> typeIndexesPerLevel;private volatile Class<?> containingClass; private volatile Class<?> parameterType;//型別 private volatile Type genericParameterType; private volatile Annotation[] parameterAnnotations; private volatile ParameterNameDiscoverer parameterNameDiscoverer; private volatile String parameterName;//引數名 private volatile MethodParameter nestedMethodParameter;
RequestMappingInfo 是一個封裝了各種請求對映條件並實現了RequestCondition介面的類。
有各種RequestCondition實現類屬性,patternsCondition,methodsCondition,paramsCondition,headersCondition,consumesCondition以及producesCondition,這個請求條件看屬性名也瞭解,分別代表http請求的路徑模式、方法、引數、頭部等資訊。
public final class RequestMappingInfo implements RequestCondition<RequestMappingInfo> { private final String name; private final PatternsRequestCondition patternsCondition;//URL模式 private final RequestMethodsRequestCondition methodsCondition;//GET 還是POST private final ParamsRequestCondition paramsCondition; private final HeadersRequestCondition headersCondition; private final ConsumesRequestCondition consumesCondition; private final ProducesRequestCondition producesCondition;
RequestMappingHandlerMapping
處理請求與HandlerMethod對映關係的一個類
二 初始化過程
我們先從名稱空間開始說起
當要使用mvc都是這麼開始的<mvc:annotation-driven content-negotiation-manager="contentNegotiationManager"/>
熟悉spring的都知道名稱空間是怎麼回事,直接到jar包的META-INF裡去找
http\://www.springframework.org/schema/mvc=org.springframework.web.servlet.config.MvcNamespaceHandler
public class MvcNamespaceHandler extends NamespaceHandlerSupport { @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("velocity-configurer", new VelocityConfigurerBeanDefinitionParser()); registerBeanDefinitionParser("groovy-configurer", new GroovyMarkupConfigurerBeanDefinitionParser()); registerBeanDefinitionParser("script-template-configurer", new ScriptTemplateConfigurerBeanDefinitionParser()); registerBeanDefinitionParser("cors", new CorsBeanDefinitionParser()); }
class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser { public static final String HANDLER_MAPPING_BEAN_NAME = RequestMappingHandlerMapping.class.getName();//預設的bean public static final String HANDLER_ADAPTER_BEAN_NAME = RequestMappingHandlerAdapter.class.getName();//預設的bean
@Override public BeanDefinition parse(Element element, ParserContext parserContext) { Object source = parserContext.extractSource(element); XmlReaderContext readerContext = parserContext.getReaderContext(); CompositeComponentDefinition compDefinition = new CompositeComponentDefinition(element.getTagName(), source); parserContext.pushContainingComponent(compDefinition); RuntimeBeanReference contentNegotiationManager = getContentNegotiationManager(element, source, parserContext); RootBeanDefinition handlerMappingDef = new RootBeanDefinition(RequestMappingHandlerMapping.class); handlerMappingDef.setSource(source); handlerMappingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); handlerMappingDef.getPropertyValues().add("order", 0); handlerMappingDef.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager); if (element.hasAttribute("enable-matrix-variables")) { Boolean enableMatrixVariables = Boolean.valueOf(element.getAttribute("enable-matrix-variables")); handlerMappingDef.getPropertyValues().add("removeSemicolonContent", !enableMatrixVariables); } else if (element.hasAttribute("enableMatrixVariables")) { Boolean enableMatrixVariables = Boolean.valueOf(element.getAttribute("enableMatrixVariables")); handlerMappingDef.getPropertyValues().add("removeSemicolonContent", !enableMatrixVariables); } configurePathMatchingProperties(handlerMappingDef, element, parserContext); readerContext.getRegistry().registerBeanDefinition(HANDLER_MAPPING_BEAN_NAME , handlerMappingDef);
上面的程式碼說明了,在解析<mvc:annotation-driven content-negotiation-manager="contentNegotiationManager"/>
系統自動給容器中註冊了兩個BeanDefinition,分別是RequestMappingHandlerMapping ,RequestMappingHandlerAdapter
接下來我們就分析RequestMappingHandlerMapping 的初始化過程
三 RequestMappingHandlerMapping 初始化
初始化的工作在AbstractHandlerMethodMapping
@Override public void afterPropertiesSet() { initHandlerMethods(); }
protected void initHandlerMethods() { if (logger.isDebugEnabled()) { logger.debug("Looking for request mappings in application context: " + getApplicationContext()); } String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ? BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) : getApplicationContext().getBeanNamesForType(Object.class)); for (String beanName : beanNames) { if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) { Class<?> beanType = null; try { beanType = getApplicationContext().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)) {//拿到當前容器的所有的bean,判斷是不是controller detectHandlerMethods(beanName); } } } handlerMethodsInitialized(getHandlerMethods()); }
protected void detectHandlerMethods(final Object handler) {//handler就是實際的controller實現類 Class<?> handlerType = (handler instanceof String ? getApplicationContext().getType((String) handler) : handler.getClass()); final Class<?> userType = ClassUtils.getUserClass(handlerType); Map<Method, T> methods = MethodIntrospector.selectMethods(userType, //這裡的methods的結構是key是Method,而Value是一個RequestMappingInfo new MethodIntrospector.MetadataLookup<T>() { @Override public T inspect(Method 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); } for (Map.Entry<Method, T> entry : methods.entrySet()) { Method invocableMethod = AopUtils.selectInvocableMethod(entry.getKey(), userType); T mapping = entry.getValue(); registerHandlerMethod(handler, invocableMethod, mapping); } }
我們看一下RequestMappingHandlerMapping的
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; }
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); }
入參 AnnotatedElement 是public com.suning.rdrs.admin.controller.ResponseResult com.suning.rdrs.admin.controller.audit.RdrsAuditCallbackController.manualAudit(java.lang.String)
requestMapping的返回值是@org.springframework.web.bind.annotation.RequestMapping(name=, value=[/alarm/summary], path=[/alarm/summary], method=[GET], params=[], headers=[], produces=[], consumes=[])
我這裡的condition是null,因為我沒有配特殊模式
然後就是構造RequestMappingInfo
protected RequestMappingInfo createRequestMappingInfo( RequestMapping requestMapping, RequestCondition<?> customCondition) { return RequestMappingInfo .paths(resolveEmbeddedValuesInPatterns(requestMapping.path())) .methods(requestMapping.method()) .params(requestMapping.params()) .headers(requestMapping.headers()) .consumes(requestMapping.consumes()) .produces(requestMapping.produces()) .mappingName(requestMapping.name()) .customCondition(customCondition) .options(this.config) .build(); }
整個過程最後我們得到Map<Method, T> methods key是Method,Value是RequestMappingInfo
讓我們回到AbstractHandlerMethodMapping.detectHandlerMethods看看剩下的部分
for (Map.Entry<Method, T> entry : methods.entrySet()) { Method invocableMethod = AopUtils.selectInvocableMethod(entry.getKey(), userType); T mapping = entry.getValue(); registerHandlerMethod(handler, invocableMethod, mapping); }
AbstractHandlerMethodMapping中的方法
protected void registerHandlerMethod(Object handler, Method method, T mapping) { this.mappingRegistry.register(mapping, handler, method); }
public void register(T mapping, Object handler, Method method) { this.readWriteLock.writeLock().lock(); try { HandlerMethod handlerMethod = createHandlerMethod(handler, method); assertUniqueMethodMapping(handlerMethod, mapping); if (logger.isInfoEnabled()) { logger.info("Mapped \"" + mapping + "\" onto " + handlerMethod); } this.mappingLookup.put(mapping, handlerMethod); //key是RequestMappingInfo物件,Value是實際的方法 List<String> directUrls = getDirectUrls(mapping); for (String url : directUrls) { this.urlLookup.add(url, mapping); //儲存url和mapping關係注意 mapping是一個RequestMappingInfo物件 } String name = null; if (getNamingStrategy() != null) { name = getNamingStrategy().getName(handlerMethod, mapping); addMappingName(name, handlerMethod); } CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping); if (corsConfig != null) { this.corsLookup.put(handlerMethod, corsConfig); } this.registry.put(mapping, new MappingRegistration<T>(mapping, handlerMethod, directUrls, name)); } finally { this.readWriteLock.writeLock().unlock(); } }
以上就是分析怎麼把請求url和對應的處理方法關聯起來的,這裡面有很多細節比如RequestMappingInfo在構建中,有些使用者會寫很複雜的url模式,比如帶萬用字元的,萬用字元還有很多鐘模式等等。本文要討論的是原理,哪些字串解析就不管他了。