SpringMVC源码情操陶冶-ResourcesBeanDefinitionParser静态资源解析器

解析mvc:resources节点,控制对静态资源的映射访问

查看官方注释

/**
 * {@link org.springframework.beans.factory.xml.BeanDefinitionParser} that parses a
 * {@code resources} element to register a {@link ResourceHttpRequestHandler} and
 * register a {@link SimpleUrlHandlerMapping} for mapping resource requests,
 * and a {@link HttpRequestHandlerAdapter}. Will also create a resource handling
 * chain with {@link ResourceResolver}s and {@link ResourceTransformer}s.
 *
 * @author Keith Donald
 * @author Jeremy Grelle
 * @author Brian Clozel
 * @since 3.0.4
 */

根据注释我们得知该解析器的作用是将mvc:resources节点解析为

  1. ResourceHttpRequestHandler/SimpleUrlHandlerMapping-匹配mapping属性对应的访问请求
  2. HttpRequestHandlerAdapter-http请求适配器
  3. ResourceResolver/ResourceTransformer-访问的资源查询处理器,针对于location属性

ResourcesBeanDefinitionParser#parse()-解析逻辑

解析涉及的内容偏多,得多花点小心思,源码如下

	@Override
	public BeanDefinition parse(Element element, ParserContext parserContext) {
		Object source = parserContext.extractSource(element);
		//注册ResourceUrlProvider对象,主要是设置对每个请求都设置上RESOURCE_URL_PROVIDER_ATTR属性,供获取此对象
		//在ResourceResolver中会使用
		registerUrlProvider(parserContext, source);
		
		//解析location属性,注册为ResourceHttpRequestHandler对象
		String resourceHandlerName = registerResourceHandler(parserContext, element, source);
		if (resourceHandlerName == null) {
			return null;
		}
		
		//解析mapping属性,注册SimpleUrlHandlerMapping对象
		Map<String, String> urlMap = new ManagedMap<String, String>();
		String resourceRequestPath = element.getAttribute("mapping");
		if (!StringUtils.hasText(resourceRequestPath)) {
			parserContext.getReaderContext().error("The 'mapping' attribute is required.", parserContext.extractSource(element));
			return null;
		}
		//mapping对应的值与ResourceHttpRequestHandler匹配
		urlMap.put(resourceRequestPath, resourceHandlerName);

		RuntimeBeanReference pathMatcherRef = MvcNamespaceUtils.registerPathMatcher(null, parserContext, source);
		RuntimeBeanReference pathHelperRef = MvcNamespaceUtils.registerUrlPathHelper(null, parserContext, source);

		RootBeanDefinition handlerMappingDef = new RootBeanDefinition(SimpleUrlHandlerMapping.class);
		handlerMappingDef.setSource(source);
		handlerMappingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
		handlerMappingDef.getPropertyValues().add("urlMap", urlMap);
		handlerMappingDef.getPropertyValues().add("pathMatcher", pathMatcherRef).add("urlPathHelper", pathHelperRef);
		//order属性-执行顺序,越大优先级越低
		String order = element.getAttribute("order");
		// Use a default of near-lowest precedence, still allowing for even lower precedence in other mappings
		handlerMappingDef.getPropertyValues().add("order", StringUtils.hasText(order) ? order : Ordered.LOWEST_PRECEDENCE - 1);
		
		//SimpleUrlHandlerMapping添加corsConfigurations属性
		RuntimeBeanReference corsConfigurationsRef = MvcNamespaceUtils.registerCorsConfigurations(null, parserContext, source);
		handlerMappingDef.getPropertyValues().add("corsConfigurations", corsConfigurationsRef);
		
		//注册SimpleUrlHandlerMapping到spring bean工厂中
		String beanName = parserContext.getReaderContext().generateBeanName(handlerMappingDef);
		parserContext.getRegistry().registerBeanDefinition(beanName, handlerMappingDef);
		parserContext.registerComponent(new BeanComponentDefinition(handlerMappingDef, beanName));

		//注册BeanNameUrlHandlerMapping/HttpRequestHandlerAdapter/SimpleControllerHandlerAdapter到bean工厂
		MvcNamespaceUtils.registerDefaultComponents(parserContext, source);

		return null;
	}

以上主要涉及locationmapping属性的解析以及注册默认的bean,下面将从这三块来进行主要的分析

ResourcesBeanDefinitionParser#registerResourceHandler()-解析location属性

简要分析下源码

	private String registerResourceHandler(ParserContext parserContext, Element element, Object source) {
		//获取location属性,此属性不可为空
		String locationAttr = element.getAttribute("location");
		if (!StringUtils.hasText(locationAttr)) {
			parserContext.getReaderContext().error("The 'location' attribute is required.", parserContext.extractSource(element));
			return null;
		}
		//支持location多路径和classpath前缀,其中以,分隔
		ManagedList<String> locations = new ManagedList<String>();
		locations.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(locationAttr)));
		
		//创建ResourceHttpRequestHandler bean
		RootBeanDefinition resourceHandlerDef = new RootBeanDefinition(ResourceHttpRequestHandler.class);
		resourceHandlerDef.setSource(source);
		resourceHandlerDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
		
		//添加locations属性
		MutablePropertyValues values = resourceHandlerDef.getPropertyValues();
		values.add("locations", locations);
		
		//添加cacheSeconds属性-cache有效时间
		String cacheSeconds = element.getAttribute("cache-period");
		if (StringUtils.hasText(cacheSeconds)) {
			values.add("cacheSeconds", cacheSeconds);
		}
		//解析子节点mvc:cache-control  cache控制器
		Element cacheControlElement = DomUtils.getChildElementByTagName(element, "cache-control");
		if (cacheControlElement != null) {
			CacheControl cacheControl = parseCacheControl(cacheControlElement);
			values.add("cacheControl", cacheControl);
		}
		
		//解析mvc:resource-chain 包含mvc:resolver/mvc:transformers
		//对应ResourceHttpRequestHandler#resourceResolvers/resourceTransformers属性
		Element resourceChainElement = DomUtils.getChildElementByTagName(element, "resource-chain");
		if (resourceChainElement != null) {
			parseResourceChain(resourceHandlerDef, parserContext, resourceChainElement, source);
		}

		//处理请求中的media type
		Object manager = MvcNamespaceUtils.getContentNegotiationManager(parserContext);
		if (manager != null) {
			values.add("contentNegotiationManager", manager);
		}

		//注册ResourceHttpRequestHandler
		String beanName = parserContext.getReaderContext().generateBeanName(resourceHandlerDef);
		parserContext.getRegistry().registerBeanDefinition(beanName, resourceHandlerDef);
		parserContext.registerComponent(new BeanComponentDefinition(resourceHandlerDef, beanName));
		return beanName;
	}

注册ResourceHttpRequestHandler,作用是对静态资源的location路径进行保存

  1. 其会被HttpRequestHandlerAdapter通过handle()方法调用

  2. 支持location多路径和classpath前缀,其中以,分隔

  3. 默认的resourceResolvers集合只有PathResourceResolver,可通过mvc:resolver指定,用于静态资源的获取

  4. 默认resourceTransformers集合为空,可通过mvc:transformer指定

mapping属性绑定ResourceHttpRequestHandler资源获取类

		Map<String, String> urlMap = new ManagedMap<String, String>();
		String resourceRequestPath = element.getAttribute("mapping");
		if (!StringUtils.hasText(resourceRequestPath)) {
			parserContext.getReaderContext().error("The 'mapping' attribute is required.", parserContext.extractSource(element));
			return null;
		}
		urlMap.put(resourceRequestPath, resourceHandlerName);

源码中只是保存在urlMap集合中,此为SimpleUrlHandlerMapping的一个内部属性,所以SimpleUrlHandlerMapping是最终保存mvc:resource信息的处理逻辑类

MvcNamespaceUtils.registerDefaultComponents()-默认的组件注册

具体的代码就不展开了,有兴趣的自行去查阅

	public static void registerDefaultComponents(ParserContext parserContext, Object source) {
		registerBeanNameUrlHandlerMapping(parserContext, source);
		registerHttpRequestHandlerAdapter(parserContext, source);
		registerSimpleControllerHandlerAdapter(parserContext, source);
	}

小结

  1. 注册ResourceHttpRequestHandler,作用为处理location对应的服务端资源。其中location支持多路径配置,"/"相对于工程根目录,也支持classpath:前缀

  2. 注册SimpleUrlHandlerMapping处理类,其为AbstractHandlerMapping的实现类,关注getHandler()方法,主要供springmvc响应请求调用

  3. SimpleUrlHandlerMapping的内部属性urlMap,用于关联mapping配置与location配置处理器ResourceHttpRequestHandler

  4. 默认会注册BeanNameUrlHandlerMappingHttpRequestHandlerAdapterSimpleControllerHandlerAdapter对象,供springmvc调用

  5. mvc:resources最终会注册为SimpleUrlHandlerMapping对象,其处理逻辑是根据请求的路径是否匹配mapping属性指定的ant-style路径,是则通过ResourceHttpRequestHandler解析对应的location获取相应的服务器资源直接响应给客户端

posted @ 2017-06-26 17:52  南柯问天  阅读(513)  评论(0编辑  收藏  举报