zoukankan      html  css  js  c++  java
  • 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获取相应的服务器资源直接响应给客户端

  • 相关阅读:
    存储过程中执行动态Sql语句
    SqlServer新建视图
    DataGridView DataGridViewCheckBoxColumn编辑时实时触发事件
    oracle number 和sqlserver numeric的区别
    放下你的无效社交
    一个程序员眼中的北京和上海
    10+年程序员总结的20+条经验教训
    SQL collate
    SQL自定义函数split分隔字符串
    C# .NET开发Oracle数据库应用程序
  • 原文地址:https://www.cnblogs.com/question-sky/p/7081709.html
Copyright © 2011-2022 走看看