Spring MVC學習筆記二

我有一口玄黄气發表於2024-03-26

Spring MVC學習筆記二

上次我們說到了RequestMappingHandlerMapping繼承自AbstractHandlerMethodMapping的方法來完成找到被@RequestMapping註解標註的類和其中的方法,並且將註解裡面配置的url和方法一一對應起來,但是我們還沒講到拿到這個對映關係後Spring是怎麼儲存這個對映關係的。上次我們沒有把尋找方法和url對映的方法給拿出來,這裡也貼出來。

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

		if (handlerType != null) {
			Class<?> userType = ClassUtils.getUserClass(handlerType);
            // 這裡用lambda表示式來遍歷userType物件裡面所有的方法
			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));
			}
			else if (mappingsLogger.isDebugEnabled()) {
				mappingsLogger.debug(formatMappings(userType, methods));
			}
			methods.forEach((method, mapping) -> {
				Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
                // 這裡就是用一個成員變數儲存這個對映關係
                // private final MappingRegistry mappingRegistry = new MappingRegistry();
				registerHandlerMethod(handler, invocableMethod, mapping);
			});
		}
	}

// 這個方法是讓RequestMappingHandlerMapping來實現的
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
    // 這裡是取方法上的@RequestMapping註解的資訊
		RequestMappingInfo info = createRequestMappingInfo(method);
		if (info != null) {
            // 這裡是取類上面的註解資訊
			RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
			if (typeInfo != null) {
                // 拼接成完整的url
				info = typeInfo.combine(info);
			}
			String prefix = getPathPrefix(handlerType);
			if (prefix != null) {
				info = RequestMappingInfo.paths(prefix).options(this.config).build().combine(info);
			}
		}
		return info;
	}


為了忘記RequestMappingHandlerMapping的繼承結構,又把圖放在這裡

可以看到這些類繼承了ApplicationContextAware介面,當Spring啟動過程中會去掃描那些繼承了Aware介面的實現,然後執行這些實現類的某些初始化方法,比如ApplicatrionContextAware的子實現就會執行setApplicationContext()方法,這裡可以看一下ApplicationContextAware介面

public interface ApplicationContextAware extends Aware {
	void setApplicationContext(ApplicationContext applicationContext) throws BeansException;

}

而和SpringMVC有關的攔截器初始化就在上面繼承圖裡面ApplicationObjectSupport類的setApplicationContext方法裡面,然後這個方法裡面會呼叫一個叫initApplication方法,這個方法的實現在AbstractHandlerMapping類裡面

@Override
	protected void initApplicationContext() throws BeansException {
        // 空實現
		extendInterceptors(this.interceptors);
        // 解析容器中的MappedInterceptor物件
		detectMappedInterceptors(this.adaptedInterceptors);
        // 處理如果有WebRequestInterceptor的情況
		initInterceptors();
	}
	// 這個方法就是將這些能掃描到的攔截類放到一個成員變數中
// private final List<HandlerInterceptor> adaptedInterceptors = new ArrayList<>();

這裡我們就得到了配置的攔截器,然後後面就是怎麼拿到這些攔截器構成一個攔截器鏈路在請求的時候執行攔截邏輯了,這個很像Servlet裡面的Fiiter,後面我們可以一起看一下這兩者有啥不同,不過今晚就先這樣咯,後面我們介紹攔截器起作用的程式碼。

相關文章