1. 程式人生 > 程式設計 >spring-mvc-handlerMapping 是怎麼存放我們的請求路徑的-原始碼

spring-mvc-handlerMapping 是怎麼存放我們的請求路徑的-原始碼

不要問我閱讀spring原始碼有什麼用,問就是沒有用,只是讓我自己使用spring的過程中自信點!

相關文章

spring-相關文章

相關連結

  1. spring-mvc-handlerMapping 是怎麼存放我們的請求路徑的-原始碼
  2. springmvc-執行流程
  3. spring-mvc 時如何選擇 messageConverter
  4. springMVC-Interceptor-原始碼分析

對映關係是在哪裡拿到的

首先有幾個類

  1. HandlerExecutionChain,這個是執行鏈
  2. HandlerMethod 這個就是我們常用的請求的封裝(也是本文的目標)
  3. HandlerAdapter 介面卡(處理請求的就是從它開始)
  4. HandlerExecutionChain.handler 屬性 就是 HandlerMethod
  5. HandlerAdapter 中最後執行的也是 ServletInvocableHandlerMethod 也是從 HandlerMethod 封裝的

說明: 本文只對 HandlerMethod 的獲取進行分析,不對mvc的執行流程分析,所有的一起原始碼都是針對常見的請求(@Controller 和 @RequestMapping)

HandlerMethod的獲取

上面已經知道了最後執行的是HandlerExecutionChain.handler,那就看看這個屬性是如何被賦值的

方法入口為

DispatcherServlet.doDispatch.getHandler()

protected void doDispatch(HttpServletRequest request,HttpServletResponse response) throws Exception {
    HttpServletRequest processedRequest = request;
    HandlerExecutionChain mappedHandler = null;
    boolean multipartRequestParsed = false;
    
    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
    
    try
{ ModelAndView mv = null; Exception dispatchException = null; try { processedRequest = checkMultipart(request); multipartRequestParsed = (processedRequest != request); // Determine handler for the current request. //就是在這個地方獲取的 handlerExecutionChain,也是在這裡獲取的 handlerMethod mappedHandler = getHandler(processedRequest); if (mappedHandler == null) { noHandlerFound(processedRequest,response); return; } // Determine handler adapter for the current request. //獲取介面卡 HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); // Process last-modified header,if supported by the handler. 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; } // Actually invoke the handler. //執行請求 mv = ha.handle(processedRequest,response,mappedHandler.getHandler()); ..................................... } 複製程式碼

上述程式碼 刪除無關程式碼,繼續往下:


@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
	if (this.handlerMappings != null) {
		for (HandlerMapping mapping : this.handlerMappings) {
		       //文章開頭說了 handlerMethod 是在 handlerExceutionChain 中,所以在這個程式碼裡
			HandlerExecutionChain handler = mapping.getHandler(request);
			if (handler != null) {
				return handler;
			}
		}
	}
	return null;
}
複製程式碼
@Override
@Nullable
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
             //就是在這裡獲取的  handlerMethod  也就是請求的對映關係
	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);
	}
             //獲取 執行鏈 把 handlerMethod 放入 HandlerExecutionChain
	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 {
        //根據request 獲取 請求路徑 裡面東西還挺多,不多也就是獲取路徑的
	String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
	this.mappingRegistry.acquireReadLock();
	try {
	       //獲取 handlerMethod
		HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath,request);
		return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
	}
	finally {
		this.mappingRegistry.releaseReadLock();
	}
}

複製程式碼
@Nullable
protected HandlerMethod lookupHandlerMethod(String lookupPath,HttpServletRequest request) throws Exception {
    List<Match> matches = new ArrayList<>();
    //這裡根據請求路徑獲取requsetMappingInfo  格式為  [{GET /xml,produces [application/xml]}]
    //請求方式,請求路徑,響應型別   注意 現在這個類 是requestMappingHandlerMapping 的一個父類
    // 說明 這也資訊有可能是在 requestMappingHandlerMapping 初始化的時候放進去的.
    List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
    if (directPathMatches != null) {
        //獲取 Match 的方法 Match 中包含 HandlerMethod
    	addMatchingMappings(directPathMatches,matches,request);
    }
    if (matches.isEmpty()) {
    	// No choice but to go through all mappings...
    	addMatchingMappings(this.mappingRegistry.getMappings().keySet(),request);
    }
    
    if (!matches.isEmpty()) {
    	Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
    	matches.sort(comparator);
    	Match bestMatch = matches.get(0);
    	if (matches.size() > 1) {
    		if (logger.isTraceEnabled()) {
    			logger.trace(matches.size() + " matching mappings: " + matches);
    		}
    		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(BEST_MATCHING_HANDLER_ATTRIBUTE,bestMatch.handlerMethod);
    	handleMatch(bestMatch.mapping,lookupPath,request);
    	// 發現返回的 handlerMethod 是 Match 屬性  所以我們關注 matches 就行了
    	return bestMatch.handlerMethod;
    }
    else {
    	return handleNoMatch(this.mappingRegistry.getMappings().keySet(),request);
    }
}
複製程式碼
private void addMatchingMappings(Collection<T> mappings,List<Match> matches,HttpServletRequest request) {
	for (T mapping : mappings) {
	    //這裡又去獲取了一次  requestMappingInfo  我以為不知道為啥又去獲取了一次
		T match = getMatchingMapping(mapping,request);
		if (match != null) {
		    //這裡建立了Match  點進去發現傳了兩個引數  一個是requestMapping 一個是 HandlerMethod
		    //現在主要看下 this.mappingRegistry.getMappings().get(mapping))
			matches.add(new Match(match,this.mappingRegistry.getMappings().get(mapping)));
		}
	}
}
複製程式碼
public Map<T,HandlerMethod> getMappings() {
	return this.mappingLookup;
}
//到這裡可以看到了  HandlerMethod 是從mappingLookup獲取的
private final Map<T,HandlerMethod> mappingLookup = new LinkedHashMap<>();
複製程式碼

下面就要mappingLookup是什麼時候被賦值的

mappingLookup 是 MappingRegistry 的屬性 MappingRegistry 是 AbstractHandlerMethodMapping的內部類

對映關係是在哪裡被載入進去的

思路: 在MappingRegistry.put() 的地方debug,觀察執行棧

重要執行節點:requestMappingHandlerMapping初始化-> afterPropertiesSet()

  1. requestMappingHandlerMapping初始化就不看了,是spring-ioc 的東西,以後會講
  2. requestMappingHandlerMapping的父類AbstractHandlerMethodMapping實現了InitializingBean介面
  3. spring bean 初始化之後 會執行 @PostConstruct 註解的 方法 實現了InitializingBean介面 的afterPropertiesSet()方法,我們這裡用到的是InitializingBean

貼程式碼如下:

protected void invokeInitMethods(String beanName,final Object bean,@Nullable RootBeanDefinition mbd)
			throws Throwable {

	boolean isInitializingBean = (bean instanceof InitializingBean);
	if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
		if (logger.isTraceEnabled()) {
			logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
		}
		if (System.getSecurityManager() != null) {
			try {
				AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
					((InitializingBean) bean).afterPropertiesSet();
					return null;
				},getAccessControlContext());
			}
			catch (PrivilegedActionException pae) {
				throw pae.getException();
			}
		}
		else {
		    //這裡就是入口
			((InitializingBean) bean).afterPropertiesSet();
		}
	}

	if (mbd != null && bean.getClass() != NullBean.class) {
		String initMethodName = mbd.getInitMethodName();
		if (StringUtils.hasLength(initMethodName) &&
				!(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
				!mbd.isExternallyManagedInitMethod(initMethodName)) {
			invokeCustomInitMethod(beanName,bean,mbd);
		}
	}
}
複製程式碼
@Override
public void afterPropertiesSet() {
    //這堆配置 具體是啥我也不知道
	this.config = new RequestMappingInfo.BuilderConfiguration();
	this.config.setUrlPathHelper(getUrlPathHelper());
	this.config.setPathMatcher(getPathMatcher());
	this.config.setSuffixPatternMatch(this.useSuffixPatternMatch);
	this.config.setTrailingSlashMatch(this.useTrailingSlashMatch);
	this.config.setRegisteredSuffixPatternMatch(this.useRegisteredSuffixPatternMatch);
	this.config.setContentNegotiationManager(getContentNegotiationManager());
          //看這裡,呼叫了父類的 afterPropertiesSet();
	super.afterPropertiesSet();();
}
複製程式碼
@Override
public void afterPropertiesSet() {
        //這裡
	initHandlerMethods();
}

//這裡
protected void initHandlerMethods() {
        //getCandidateBeanNames()  應該是獲取了所有的beanName
	for (String beanName : getCandidateBeanNames()) {
		if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
		    //就是這裡
			processCandidateBean(beanName);
		}
	}
	handlerMethodsInitialized(getHandlerMethods());
}
//根絕beanName處理的
protected void processCandidateBean(String beanName) {
	Class<?> beanType = null;
	try {
	       //獲取當前bean的class 型別
		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);
		}
	}
	//這裡 重點 isHandler() 判斷是否需要處理
	if (beanType != null && isHandler(beanType)) {
	        //假如是的話,繼續 (重點)
		detectHandlerMethods(beanName);
	}
}

// 看到了,條件就是 當前類是否存在 @Controller  @RequestMapping 註解
protected boolean isHandler(Class<?> beanType) {
	return (AnnotatedElementUtils.hasAnnotation(beanType,Controller.class) ||
			AnnotatedElementUtils.hasAnnotation(beanType,RequestMapping.class));
}

複製程式碼

下面的程式碼就是最後了,注意看! 下面的程式碼比較繞,用的是java8的Lambda 表示式,假如對java8比較熟,看這段程式碼是無壓力的 先把這幾個方法的程式碼都貼出來,如下:

protected void detectHandlerMethods(Object handler) {
	Class<?> handlerType = (handler instanceof String ?
			obtainApplicationContext().getType((String) handler) : handler.getClass());

	if (handlerType != null) {
	    //獲取當前contorller 型別
		Class<?> userType = ClassUtils.getUserClass(handlerType);
		// 第一步  就是這裡   執行selectMethods   這個方法 傳遞了一個行為(lambda表示式)
		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);
			registerHandlerMethod(handler,invocableMethod,mapping);
		});
	}
}

public static <T> Map<Method,T> selectMethods(Class<?> targetType,final MetadataLookup<T> metadataLookup) {
	final Map<Method,T> methodMap = new LinkedHashMap<>();
	Set<Class<?>> handlerTypes = new LinkedHashSet<>();
	Class<?> specificHandlerType = null;
    //判斷是否存在代理
	if (!Proxy.isProxyClass(targetType)) {
		specificHandlerType = ClassUtils.getUserClass(targetType);
		handlerTypes.add(specificHandlerType);
	}
	handlerTypes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetType));
    //迴圈 第二步 目標 controller  正常情況就一個元素
	for (Class<?> currentHandlerType : handlerTypes) {
		final Class<?> targetClass = (specificHandlerType != null ? specificHandlerType : currentHandlerType);
        // 呼叫 doWithMethods  第三步
		ReflectionUtils.doWithMethods(currentHandlerType,method -> {
		    //第五步  獲取到了 method
			Method specificMethod = ClassUtils.getMostSpecificMethod(method,targetClass);
			T result = metadataLookup.inspect(specificMethod);
			if (result != null) {
				Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
				if (bridgedMethod == specificMethod || metadataLookup.inspect(bridgedMethod) == null) {
					methodMap.put(specificMethod,result);
				}
			}
		},ReflectionUtils.USER_DECLARED_METHODS);
	}

	return methodMap;
}
public static void doWithMethods(Class<?> clazz,MethodCallback mc,@Nullable MethodFilter mf) {
	// Keep backing up the inheritance hierarchy.
	//獲取所有的方法
	Method[] methods = getDeclaredMethods(clazz);
	for (Method method : methods) {
		if (mf != null && !mf.matches(method)) {
			continue;
		}
		try {
		    //執行 selectMethods 方法中的 MethodFilter mf  第四步
			mc.doWith(method);
		}
		catch (IllegalAccessException ex) {
			throw new IllegalStateException("Not allowed to access method '" + method.getName() + "': " + ex);
		}
	}
	if (clazz.getSuperclass() != null) {
		doWithMethods(clazz.getSuperclass(),mc,mf);
	}
	else if (clazz.isInterface()) {
		for (Class<?> superIfc : clazz.getInterfaces()) {
			doWithMethods(superIfc,mf);
		}
	}
}

//儲存對應關係入口
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);
		//儲存到  mappingLookup
		this.mappingLookup.put(mapping,handlerMethod);

		List<String> directUrls = getDirectUrls(mapping);
		for (String url : directUrls) {
		    //儲存路徑到urlLookup
			this.urlLookup.add(url,mapping);
		}
		// 

		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<>(mapping,handlerMethod,directUrls,name));
	}
	finally {
		this.readWriteLock.writeLock().unlock();
	}
}
複製程式碼

總結:

  1. spring-ioc 初始化 requestMappingHandlerMapping 的時候 把對應關係 存放在mappingLookup
  2. 執行的時候 根絕請求路徑 查詢urlLookup,再根據 urlLookup 的val 當做key 去 mappingLookup 中查詢