SpringMVC源碼(三)-組件初始化

SpringMVC的好用離不開其功能強大的各個組件,相互獨立易於擴展,默認實現就已經能支持絕大多數狀況了。但在組件可使用前,都須要進行初始化。在DispatcherServlet初始化最後,觸發了全部必要組件的策略化加載,支持動態地對全部組件進行配置。另外,在各個組件自身的初始化時,也完成了各自運行環境的準備。java

1.組件加載策略化

上一章最後,在servlet子上下文完成建立,調用了模板擴展方法OnRefresh,它在FrameworkServlet中僅僅只是個空方法,但在其子類DispatcherServlet中則相當重要,它是一切組件的起源。web

DispatcherServlet.java

protected void onRefresh(ApplicationContext context) {
	initStrategies(context);
}

初始化全部策略,實際上是指各個組件能夠經過策略動態地進行配置。spring

protected void initStrategies(ApplicationContext context) {
	// 文件上傳解析器
	initMultipartResolver(context);
	// 本地化解析器
	initLocaleResolver(context);
	// 主題解析器
	initThemeResolver(context);
	// 處理器映射器(url和Controller方法的映射)
	initHandlerMappings(context);
	// 處理器適配器(實際執行Controller方法)
	initHandlerAdapters(context);
	// 處理器異常解析器
	initHandlerExceptionResolvers(context);
	// RequestToViewName解析器
	initRequestToViewNameTranslator(context);
	// 視圖解析器(視圖的匹配和渲染)
	initViewResolvers(context);
	// FlashMap管理者
	initFlashMapManager(context);
}

以上基本將SpringMVC中的主要組件都羅列出來了,其中最重要的當是HandlerMapping,HandlerAdapter和ViewResolver。因爲各組件的初始化策略方式類似,咱們以HandlerMapping來介紹。mvc

private void initHandlerMappings(ApplicationContext context) {
	this.handlerMappings = null;

	// 是否查找全部HandlerMapping標識
	if (this.detectAllHandlerMappings) {
		// 從上下文(包含全部父上下文)中查找HandlerMapping類型的Bean
		Map<String, HandlerMapping> matchingBeans =
				BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
		if (!matchingBeans.isEmpty()) {
			this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values());
			// We keep HandlerMappings in sorted order.
			AnnotationAwareOrderComparator.sort(this.handlerMappings);
		}
	}
	else {
		try {
			// 根據指定名稱獲取HandlerMapping對象
			HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
			this.handlerMappings = Collections.singletonList(hm);
		}
		catch (NoSuchBeanDefinitionException ex) {
			// Ignore, we'll add a default HandlerMapping later.
		}
	}

	// 確保至少有一個HandlerMapping,若是沒能找到,註冊一個默認的
	if (this.handlerMappings == null) {
		this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
		if (logger.isDebugEnabled()) {
			logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");
		}
	}
}

策略的邏輯很簡單:有一個標識,是否查找全部HandlerMapping(默認爲true)。若是爲是,則從上下文(包括全部父上下文)中查詢類型爲HandlerMapping的Bean,並進行排序。若是爲否,則從上下文中按指定名稱去尋找。若是都沒有找到,提供一個默認的實現。這個默認實現從DispatcherServlet同級目錄的DispatcherServlet.properties中加載獲得。app

org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver

org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver

org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
	org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping

org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
	org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
	org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter

org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver,\
	org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
	org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver

org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator

org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver

org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager

能夠看到SpringMVC爲每一個組件都提供了默認的實現,一般狀況下都可以知足需求。若是你想對某個組件進行定製,能夠經過spring的xml文件或者@Configuration類中的@Bean來實現。好比常配置的視圖解析器:cors

xml方式jsp

<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
	<property name="prefix" value="/WEB-INF/"></property>
	<property name="suffix" value=".jsp"></property>
</bean>

編碼方式ide

[@Bean](https://my.oschina.net/bean)
public ViewResolver viewResolver(){
	return new InternalResourceViewResolver("/WEB-INF/", ".jsp");
}

因爲其餘組件的初始化方式徹底一致,這裏就不贅述了。須要關注的一點是,當匹配到合適的組件時,都會經過Spring的方式實例化組件。而有些組件在實例化時也會對自身運行環境進行初始化。ui

2.URL-Controller方法映射初始化

在使用SpringMVC時,須要在xml文件中添加一行註解this

<mvc:annotation-driven />

或者在配置類上增長註解@EnableWebMvc,才能使SpringMVC的功能徹底開啓。咱們以xml的配置爲例,看看它都作了什麼。根據spring對namespace的解析策略找到MvcNamespaceHandler類,在其init方法中

registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());

直接看AnnotationDrivenBeanDefinitionParser的parse方法,上下有一百多行,瀏覽一遍,主要就是註冊各類組件和內部須要的解析器和轉換器。找到HandlerMapping組件的實現

RootBeanDefinition handlerMappingDef = new RootBeanDefinition(RequestMappingHandlerMapping.class);

於是實際用的HandlerMapping實現即爲RequestMappingHandlerMapping。其抽象基類AbstractHandlerMethodMapping實現了InitializingBean接口。

public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean

當RequestMappingHandlerMapping對象初始化時,會調用InitializingBean接口的afterPropertiesSet方法。在此方法中完成了Controller方法同請求url的映射表。

public void afterPropertiesSet() {
	initHandlerMethods();
}

protected void initHandlerMethods() {
	if (logger.isDebugEnabled()) {
		logger.debug("Looking for request mappings in application context: " + getApplicationContext());
	}
	// 默認只從當前上下文中查詢全部beanName
	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);
				}
			}
			// 若是類上有@Controller或@RequestMapping註解,則進行解析
			if (beanType != null && isHandler(beanType)) {
				detectHandlerMethods(beanName);
			}
		}
	}
	handlerMethodsInitialized(getHandlerMethods());
}

對於類上有@Controller或@RequestMapping註解,都進行了detect。

protected void detectHandlerMethods(final Object handler) {
	Class<?> handlerType = (handler instanceof String ?
			getApplicationContext().getType((String) handler) : handler.getClass());
	final Class<?> userType = ClassUtils.getUserClass(handlerType);

	// 方法內省器,用於發現@RequestMapping註解的方法
	Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
			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);
	}

	// 遍歷全部有效方法,封裝方法爲可執行的Method,註冊到URL-Controller方法映射表
	for (Map.Entry<Method, T> entry : methods.entrySet()) {
		Method invocableMethod = AopUtils.selectInvocableMethod(entry.getKey(), userType);
		T mapping = entry.getValue();
		registerHandlerMethod(handler, invocableMethod, mapping);
	}
}

方法內省器中的內部類調用的getMappingForMethod方法爲抽象方法,實如今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);
}

解析方法上的@RequestMapping註解返回RequestMappingInfo,其實就是請求映射信息。而在上面的detect方法最後,註冊URL-Controller方法映射表由registerHandlerMethod方法完成。

protected void registerHandlerMethod(Object handler, Method method, T mapping) {
	this.mappingRegistry.register(mapping, handler, method);
}

mappingRegistry是AbstractHandlerMethodMapping的核心構成,主要做用就是維護HandlerMethod的映射關係,以及提供映射的查詢方法。其register方法的實現以下:

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);
		}
		// 添加mapping->HandlerMethod的映射
		this.mappingLookup.put(mapping, handlerMethod);

		List<String> directUrls = getDirectUrls(mapping);
		for (String url : directUrls) {
			// 添加url->mapping的映射
			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);
		}

		// 添加mapping->MappingRegistration的映射
		this.registry.put(mapping, new MappingRegistration<T>(mapping, handlerMethod, directUrls, name));
	}
	finally {
		this.readWriteLock.writeLock().unlock();
	}
}

註冊過程增長了三個個映射關係,一個是mapping->HandlerMethod的映射,一個是url->mapping的映射,一個是mapping->MappingRegistration的映射。經過前兩個映射,能夠將請求定位到肯定的Controller的方法上,最後一個映射保留全部Mapping註冊信息,用於unregister。而方法加鎖則是保證全部映射的一致性。

至此,請求URL和Controller方法之間的關係就初始化完成了。

3.參數解析器和返回值解析器的初始化

在使用SpringMVC時,對Controller中方法的參數和返回值的處理都很是的方便。咱們知道,經常使用類型的參數不須要任何額外配置,SpringMVC便可完美轉換,而返回值既能夠是ModelAndView, 也能夠是String,或者是HashMap,或者是ResponseEntity,多種方式簡單配置,完美兼容。它是怎麼作到的呢,經過一系列的轉換器來完成的。而這些轉換器也是須要初始化到運行環境中的, 誰的運行環境, HandlerAdapter的。

一樣SpringMVC提供了一個默認的強大實現,RequestMappingHandlerAdapter,一樣在<mvc:annotation-driven />中定義。它也實現了InitializingBean接口。

public void afterPropertiesSet() {
	// Do this first, it may add ResponseBody advice beans
	// 初始化Controller通用切面
	initControllerAdviceCache();

	// 初始化參數解析器
	if (this.argumentResolvers == null) {
		List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
		this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
	}
	// 初始化InitBinder解析器
	if (this.initBinderArgumentResolvers == null) {
		List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
		this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
	}
	// 初始化返回值處理器
	if (this.returnValueHandlers == null) {
		List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
		this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
	}
}

每一個組件都經過內置默認的實現,咱們主要來看參數解析器和返回值處理器兩個。

參數解析器

private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
	List<HandlerMethodArgumentResolver> resolvers = new ArrayList<HandlerMethodArgumentResolver>();

	// Annotation-based argument resolution
	resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));
	resolvers.add(new RequestParamMapMethodArgumentResolver());
	resolvers.add(new PathVariableMethodArgumentResolver());
	resolvers.add(new PathVariableMapMethodArgumentResolver());
	resolvers.add(new MatrixVariableMethodArgumentResolver());
	resolvers.add(new MatrixVariableMapMethodArgumentResolver());
	resolvers.add(new ServletModelAttributeMethodProcessor(false));
	resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
	resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters(), this.requestResponseBodyAdvice));
	resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory()));
	resolvers.add(new RequestHeaderMapMethodArgumentResolver());
	resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory()));
	resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));
	resolvers.add(new SessionAttributeMethodArgumentResolver());
	resolvers.add(new RequestAttributeMethodArgumentResolver());

	// Type-based argument resolution
	resolvers.add(new ServletRequestMethodArgumentResolver());
	resolvers.add(new ServletResponseMethodArgumentResolver());
	resolvers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
	resolvers.add(new RedirectAttributesMethodArgumentResolver());
	resolvers.add(new ModelMethodProcessor());
	resolvers.add(new MapMethodProcessor());
	resolvers.add(new ErrorsMethodArgumentResolver());
	resolvers.add(new SessionStatusMethodArgumentResolver());
	resolvers.add(new UriComponentsBuilderMethodArgumentResolver());

	// Custom arguments
	if (getCustomArgumentResolvers() != null) {
		resolvers.addAll(getCustomArgumentResolvers());
	}

	// Catch-all
	resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));
	resolvers.add(new ServletModelAttributeMethodProcessor(true));

	return resolvers;
}

你們根據解析器名稱大概能夠推測出其做用,好比@RequestParam解析器,@PathVariable解析器,及@RequestBody和@ResponseBody解析器等等。SpringMVC強大的參數解析能力其實來源於豐富的內置解析器。

另外一個返回值處理器的初始化

private List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers() {
	List<HandlerMethodReturnValueHandler> handlers = new ArrayList<HandlerMethodReturnValueHandler>();

	// Single-purpose return value types
	handlers.add(new ModelAndViewMethodReturnValueHandler());
	handlers.add(new ModelMethodProcessor());
	handlers.add(new ViewMethodReturnValueHandler());
	handlers.add(new ResponseBodyEmitterReturnValueHandler(getMessageConverters()));
	handlers.add(new StreamingResponseBodyReturnValueHandler());
	handlers.add(new HttpEntityMethodProcessor(getMessageConverters(),
			this.contentNegotiationManager, this.requestResponseBodyAdvice));
	handlers.add(new HttpHeadersReturnValueHandler());
	handlers.add(new CallableMethodReturnValueHandler());
	handlers.add(new DeferredResultMethodReturnValueHandler());
	handlers.add(new AsyncTaskMethodReturnValueHandler(this.beanFactory));

	// Annotation-based return value types
	handlers.add(new ModelAttributeMethodProcessor(false));
	handlers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(),
			this.contentNegotiationManager, this.requestResponseBodyAdvice));

	// Multi-purpose return value types
	handlers.add(new ViewNameMethodReturnValueHandler());
	handlers.add(new MapMethodProcessor());

	// Custom return value types
	if (getCustomReturnValueHandlers() != null) {
		handlers.addAll(getCustomReturnValueHandlers());
	}

	// Catch-all
	if (!CollectionUtils.isEmpty(getModelAndViewResolvers())) {
		handlers.add(new ModelAndViewResolverMethodReturnValueHandler(getModelAndViewResolvers()));
	}
	else {
		handlers.add(new ModelAttributeMethodProcessor(true));
	}

	return handlers;
}

一樣內置了對多種返回類型,返回方式的處理器,才支撐起豐富便捷的使用。

本章主要介紹了SpringMVC的web組件的初始化,支持外部配置的策略化方式。另外就HandlerMapping和HandlerAdapter兩個重要組件的運行環境的初始化簡要了解,知曉請求url同Controller方法的映射關係的創建過程,以及請求處理器Handler運行時初始化的各類解析器,以支持其強大的處理過程。下一章咱們去探究一個請求如何在SpringMVC各組件中進行流轉。

相關文章
相關標籤/搜索