SpringMVC源碼情操陶冶-AnnotationDrivenBeanDefinitionParser注解解析器


mvc:annotation-driven節點的解析器,是springmvc的核心解析器

官方注釋


Open Declaration   org.springframework.web.servlet.config.AnnotationDrivenBeanDefinitionParser


A BeanDefinitionParser that provides the configuration for the <annotation-driven/> MVC namespace element. 

This class registers the following <<HandlerMappings>>:
•RequestMappingHandlerMapping ordered at 0 for mapping requests to annotated controller methods. 
•BeanNameUrlHandlerMapping ordered at 2 to map URL paths to controller bean names. 

Note: Additional HandlerMappings may be registered as a result of using the <view-controller> or the <resources> MVC namespace elements. 



This class registers the following <<HandlerAdapters>>: 
•RequestMappingHandlerAdapter for processing requests with annotated controller methods. 
•HttpRequestHandlerAdapter for processing requests with HttpRequestHandlers. 
•SimpleControllerHandlerAdapter for processing requests with interface-based Controllers. 



This class registers the following <<HandlerExceptionResolvers>>: 
•ExceptionHandlerExceptionResolver for handling exceptions through @ExceptionHandler methods. 
•ResponseStatusExceptionResolver for exceptions annotated with @ResponseStatus. 
•DefaultHandlerExceptionResolver for resolving known Spring exception types 



This class registers an org.springframework.util.<<AntPathMatcher>> and a org.springframework.web.util.<<UrlPathHelper>> to be used by: 
•the RequestMappingHandlerMapping, 
•the HandlerMapping for ViewControllers 
•and the HandlerMapping for serving resources 
Note that those beans can be configured by using the path-matching MVC namespace element. 
Both the RequestMappingHandlerAdapter and the ExceptionHandlerExceptionResolver are configured with instances of the following by default: 
•A ContentNegotiationManager 
•A DefaultFormattingConversionService 
•A org.springframework.validation.beanvalidation.LocalValidatorFactoryBean if a JSR-303 implementation is available on the classpath 
•A range of HttpMessageConverters depending on what 3rd party libraries are available on the classpath. 

主要注冊HandlerMappings-路徑匹配器HandlerAdapters-路徑匹配適配器HandlerExceptionResolvers-異常解析器AntPathMatcher-路徑解析器UrlPathHelper-請求路徑獲取幫助類等beans,提前為mvc做好基礎的准備

AnnotationDrivenBeanDefinitionParser#parse

從此方法中,提煉實現的最主要代碼片段,代碼如下


		parserContext.registerComponent(new BeanComponentDefinition(handlerMappingDef, HANDLER_MAPPING_BEAN_NAME));
		parserContext.registerComponent(new BeanComponentDefinition(handlerAdapterDef, HANDLER_ADAPTER_BEAN_NAME));
		parserContext.registerComponent(new BeanComponentDefinition(uriCompContribDef, uriCompContribName));
		parserContext.registerComponent(new BeanComponentDefinition(exceptionHandlerExceptionResolver, methodExceptionResolverName));
		parserContext.registerComponent(new BeanComponentDefinition(responseStatusExceptionResolver, responseStatusExceptionResolverName));
		parserContext.registerComponent(new BeanComponentDefinition(defaultExceptionResolver, defaultExceptionResolverName));
		//格式轉換處理攔截類,比如時間、數字等
		parserContext.registerComponent(new BeanComponentDefinition(mappedCsInterceptorDef, mappedInterceptorName));

		// Ensure BeanNameUrlHandlerMapping (SPR-8289) and default HandlerAdapters are not "turned off"
		MvcNamespaceUtils.registerDefaultComponents(parserContext, source);

跟官方注釋提及的一樣,主要目的是注冊與mvc處理有關的相關beans以及默認的mvc組件,下面我們將從以上代碼的不同組件生成進行逐個分析

HandlerMappings組件注冊

對應parse()方法的代碼片段如下

		//生成RequestMappingHandlerMapping組件對象
		RootBeanDefinition handlerMappingDef = new RootBeanDefinition(RequestMappingHandlerMapping.class);
		handlerMappingDef.setSource(source);
		handlerMappingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
		//優先級設置為最高
		handlerMappingDef.getPropertyValues().add("order", 0);
//添加contentNegotiationManager屬性,處理media type		handlerMappingDef.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager);
		
		//查看mvc:annotation-driven有無enable-matrix-variables/enableMatrixVariables,表示是否開啟多變量映射比如/cars;a=1;b=1
		//具體使用可查閱相關文檔,默認removeSemicolonContent為false
		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);
//將RequestMappingHandlerMapping注冊為bean對象放置bean工廠中		readerContext.getRegistry().registerBeanDefinition(HANDLER_MAPPING_BEAN_NAME , handlerMappingDef);

RequestMappingHandlerMapping主要是處理@Controller@RequestMapping注解的,另外再看下configurePathMatchingProperties()配置路徑解析器方法

AnnotationDrivenBeanDefinitionParser#configurePathMatchingProperties-路徑匹配解析器配置

代碼如下

	private void configurePathMatchingProperties(RootBeanDefinition handlerMappingDef, Element element,
			ParserContext parserContext) {
		//獲取mvc:annotation-driven下子節點mvc:path-matching
		Element pathMatchingElement = DomUtils.getChildElementByTagName(element, "path-matching");
		
		if (pathMatchingElement != null) {
			Object source = parserContext.extractSource(element);
			//是否采用suffix-pattern,即.*,比如/user也匹配/user.*。默認為true
			if (pathMatchingElement.hasAttribute("suffix-pattern")) {
				Boolean useSuffixPatternMatch = Boolean.valueOf(pathMatchingElement.getAttribute("suffix-pattern"));
				handlerMappingDef.getPropertyValues().add("useSuffixPatternMatch", useSuffixPatternMatch);
			}
			//是否采用分隔符,特指/,比如/user也匹配/user/。默認為true
			if (pathMatchingElement.hasAttribute("trailing-slash")) {
				Boolean useTrailingSlashMatch = Boolean.valueOf(pathMatchingElement.getAttribute("trailing-slash"));
				handlerMappingDef.getPropertyValues().add("useTrailingSlashMatch", useTrailingSlashMatch);
			}
			//是否采用contentNegotiationManager中的格式,比如*.json/*.xml。默認為false
			if (pathMatchingElement.hasAttribute("registered-suffixes-only")) {
				Boolean useRegisteredSuffixPatternMatch = Boolean.valueOf(pathMatchingElement.getAttribute("registered-suffixes-only"));
				handlerMappingDef.getPropertyValues().add("useRegisteredSuffixPatternMatch", useRegisteredSuffixPatternMatch);
			}
			RuntimeBeanReference pathHelperRef = null;
			//路徑解析幫助類,可指定,默認為UrlPathHelper
			if (pathMatchingElement.hasAttribute("path-helper")) {
				pathHelperRef = new RuntimeBeanReference(pathMatchingElement.getAttribute("path-helper"));
			}
			pathHelperRef = MvcNamespaceUtils.registerUrlPathHelper(pathHelperRef, parserContext, source);
			handlerMappingDef.getPropertyValues().add("urlPathHelper", pathHelperRef);

			RuntimeBeanReference pathMatcherRef = null;
			//路徑解析器,默認為AntPathMatcher解析器
			if (pathMatchingElement.hasAttribute("path-matcher")) {
				pathMatcherRef = new RuntimeBeanReference(pathMatchingElement.getAttribute("path-matcher"));
			}
			pathMatcherRef = MvcNamespaceUtils.registerPathMatcher(pathMatcherRef, parserContext, source);
			handlerMappingDef.getPropertyValues().add("pathMatcher", pathMatcherRef);
		}
	}

看代碼邏輯,主要是通過mvc:annotation-driven下的mvc:path-matching節點來配置

  1. suffix-pattern-是否啟用后綴匹配,默認為true,即對指定的url會再新增.*,比如/user實際匹配/user.*->/user匹配/user.html,/user.jsp
  2. trailing-slash-是否啟動尾部斜線匹配,默認為true,即對指定的url會新增/,比如/user也會匹配/user/
  3. registered-suffixes-only-是否啟用media type類型的匹配,即對指定的url新增*.json/*.xml等匹配,默認為false
  4. path-helper-路徑獲取幫助類,默認為UrlPathHelper類
  5. path-matcher-路徑匹配解析器,默認為AntPathMather

HandlerAdapters組件注冊

相應的代碼片段如下

		//獲取conversion-service屬性,默認為FormattingConversionServiceFactoryBean
		//處理一些基本類的格式與轉換,比如時間、數字等
		RuntimeBeanReference conversionService = getConversionService(element, source, parserContext);
		//獲取validator,默認為OptionalValidatorFactoryBean,基於java自帶的Validator接口,表示校驗器,可用於javabean的參數校驗等
		RuntimeBeanReference validator = getValidator(element, source, parserContext);
		//獲取message-codes-resolver屬性,默認為null。
		RuntimeBeanReference messageCodesResolver = getMessageCodesResolver(element);
		//設置ConfigurableWebBindingInitializer類 用於數據綁定
		RootBeanDefinition bindingDef = new RootBeanDefinition(ConfigurableWebBindingInitializer.class);
		bindingDef.setSource(source);
		bindingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
		bindingDef.getPropertyValues().add("conversionService", conversionService);
		bindingDef.getPropertyValues().add("validator", validator);
		bindingDef.getPropertyValues().add("messageCodesResolver", messageCodesResolver);

		//獲取message-converters子節點的配置,消息轉換器,可用於向前端發送數據再次自定義組裝
		//比如MappingJackson2HttpMessageConverter json轉字符串
		ManagedList<?> messageConverters = getMessageConverters(element, source, parserContext);
		//argument-resolvers子節點配置,參數解析
		ManagedList<?> argumentResolvers = getArgumentResolvers(element, parserContext);
		//return-value-handlers子節點解析
		ManagedList<?> returnValueHandlers = getReturnValueHandlers(element, parserContext);
		//async-support子節點解析,獲取其中的default-timeout屬性,作為異步處理超時時間,默認null
		String asyncTimeout = getAsyncTimeout(element);
		//async-support子節點解析,獲取其中的task-executor屬性。異步任務線程池
		RuntimeBeanReference asyncExecutor = getAsyncExecutor(element);
		//async-support子節點解析,獲取其中的callable-interceptors節點。異步處理callable類型攔截器
		ManagedList<?> callableInterceptors = getCallableInterceptors(element, source, parserContext);
		ManagedList<?> deferredResultInterceptors = getDeferredResultInterceptors(element, source, parserContext);

		//將上述的屬性添加到RequestMappingHandlerAdapter中
		RootBeanDefinition handlerAdapterDef = new RootBeanDefinition(RequestMappingHandlerAdapter.class);
		handlerAdapterDef.setSource(source);
		handlerAdapterDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
		handlerAdapterDef.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager);
		handlerAdapterDef.getPropertyValues().add("webBindingInitializer", bindingDef);
		handlerAdapterDef.getPropertyValues().add("messageConverters", messageConverters);
		addRequestBodyAdvice(handlerAdapterDef);
		addResponseBodyAdvice(handlerAdapterDef);

		//ignoreDefaultModelOnRedirect屬性配置
		if (element.hasAttribute("ignore-default-model-on-redirect")) {
			Boolean ignoreDefaultModel = Boolean.valueOf(element.getAttribute("ignore-default-model-on-redirect"));
			handlerAdapterDef.getPropertyValues().add("ignoreDefaultModelOnRedirect", ignoreDefaultModel);
		}
		else if (element.hasAttribute("ignoreDefaultModelOnRedirect")) {
			// "ignoreDefaultModelOnRedirect" spelling is deprecated
			Boolean ignoreDefaultModel = Boolean.valueOf(element.getAttribute("ignoreDefaultModelOnRedirect"));
			handlerAdapterDef.getPropertyValues().add("ignoreDefaultModelOnRedirect", ignoreDefaultModel);
		}

		if (argumentResolvers != null) {
			handlerAdapterDef.getPropertyValues().add("customArgumentResolvers", argumentResolvers);
		}
		if (returnValueHandlers != null) {
			handlerAdapterDef.getPropertyValues().add("customReturnValueHandlers", returnValueHandlers);
		}
		if (asyncTimeout != null) {
			handlerAdapterDef.getPropertyValues().add("asyncRequestTimeout", asyncTimeout);
		}
		if (asyncExecutor != null) {
			handlerAdapterDef.getPropertyValues().add("taskExecutor", asyncExecutor);
		}

		handlerAdapterDef.getPropertyValues().add("callableInterceptors", callableInterceptors);
		handlerAdapterDef.getPropertyValues().add("deferredResultInterceptors", deferredResultInterceptors);
		readerContext.getRegistry().registerBeanDefinition(HANDLER_ADAPTER_BEAN_NAME , handlerAdapterDef);

配置的主要是RequestMappingHandlerAdapter對象,其中的屬性較多,讀者可查看>>>SpringMVC源碼情操陶冶-RequestMappingHandlerAdapter適配器。其主要支持HandlerMethod類,而這些類都是由RequestMappingHandlerMapping來生成的,而這又是通過AbstractHandlerMethodMapping#initHandlerMethods來實現的,有興趣的可以自行去查閱

ExceptionResolver組件注冊

默認采用ExceptionHandlerExceptionResolverResponseStatusExceptionResolverDefaultHandlerExceptionResolver作為異常處理類

		RootBeanDefinition exceptionHandlerExceptionResolver = new RootBeanDefinition(ExceptionHandlerExceptionResolver.class);
		exceptionHandlerExceptionResolver.setSource(source);
		exceptionHandlerExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
		exceptionHandlerExceptionResolver.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager);
		exceptionHandlerExceptionResolver.getPropertyValues().add("messageConverters", messageConverters);
		exceptionHandlerExceptionResolver.getPropertyValues().add("order", 0);
		addResponseBodyAdvice(exceptionHandlerExceptionResolver);

		String methodExceptionResolverName = readerContext.registerWithGeneratedName(exceptionHandlerExceptionResolver);

		RootBeanDefinition responseStatusExceptionResolver = new RootBeanDefinition(ResponseStatusExceptionResolver.class);
		responseStatusExceptionResolver.setSource(source);
		responseStatusExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
		responseStatusExceptionResolver.getPropertyValues().add("order", 1);
		String responseStatusExceptionResolverName =
				readerContext.registerWithGeneratedName(responseStatusExceptionResolver);

		RootBeanDefinition defaultExceptionResolver = new RootBeanDefinition(DefaultHandlerExceptionResolver.class);
		defaultExceptionResolver.setSource(source);
		defaultExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
		defaultExceptionResolver.getPropertyValues().add("order", 2);
		String defaultExceptionResolverName =
				readerContext.registerWithGeneratedName(defaultExceptionResolver);
  • ExceptionHandlerExceptionResolver-處理@ExceptionHandler方法注解

  • ResponseStatusExceptionResolver-處理@ResponseStatus類型、方法注解

  • DefaultHandlerExceptionResolver-處理普通的spring異常

更准確的可查看官方注釋說明

小結

mvc:annotation-driven一句話便囊括了springmvc的基本處理組件注冊成bean到springmvc上下文,結合此處再去理解springmvc的邏輯源碼就簡單了


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM