解析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获取相应的服务器资源直接响应给客户端

SpringMVC源码情操陶冶-ResourcesBeanDefinitionParser静态资源解析器的更多相关文章

  1. Spring源码情操陶冶-AnnotationConfigBeanDefinitionParser注解配置解析器

    本文承接前文Spring源码情操陶冶-自定义节点的解析,分析spring中的context:annotation-config节点如何被解析 源码概览 对BeanDefinitionParser接口的 ...

  2. Spring源码情操陶冶-ComponentScanBeanDefinitionParser文件扫描解析器

    承接前文Spring源码情操陶冶-自定义节点的解析,本文讲述spring通过context:component-scan节点干了什么事 ComponentScanBeanDefinitionParse ...

  3. Spring源码情操陶冶-PropertyPlaceholderBeanDefinitionParser注解配置解析器

    本文针对spring配置的context:property-placeholder作下简单的分析,承接前文Spring源码情操陶冶-自定义节点的解析 spring配置文件应用 <context: ...

  4. Spring源码情操陶冶-AOP之ConfigBeanDefinitionParser解析器

    aop-Aspect Oriented Programming,面向切面编程.根据百度百科的解释,其通过预编译方式和运行期动态代理实现程序功能的一种技术.主要目的是为了程序间的解耦,常用于日志记录.事 ...

  5. SpringMVC源码情操陶冶-HandlerAdapter适配器简析

    springmvc中对业务的具体处理是通过HandlerAdapter适配器操作的 HandlerAdapter接口方法 列表如下 /** * Given a handler instance, re ...

  6. Spring源码情操陶冶-自定义节点的解析

    本文承接前文Spring源码情操陶冶-DefaultBeanDefinitionDocumentReader#parseBeanDefinitions,特开辟出一块新地来啃啃这块有意思的骨头 自定义节 ...

  7. SpringMVC源码情操陶冶-FreeMarker之web配置

    前言:本文不讲解FreeMarkerView视图的相关配置,其配置基本由FreeMarkerViewResolver实现,具体可参考>>>SpringMVC源码情操陶冶-ViewRe ...

  8. SpringMVC源码情操陶冶-DispatcherServlet父类简析

    阅读源码有助于陶冶情操,本文对springmvc作个简单的向导 springmvc-web.xml配置 <servlet> <servlet-name>dispatch< ...

  9. SpringMVC源码情操陶冶-DispatcherServlet类简析(一)

    阅读源码有利于陶冶情操,此文承接前文SpringMVC源码情操陶冶-DispatcherServlet父类简析 注意:springmvc初始化其他内容,其对应的配置文件已被加载至beanFactory ...

随机推荐

  1. hdu_1018(斯大林公式/n!的位数)

    题意:求大数n!的位数. 根据n! = (int)log(n!)+1 方法1: log(n!) = log(1*2*3*...*n) = log1+log2+...+logn 方法2: 斯大林公式: ...

  2. codeforce 375_2_b_c

    codeforce 375_2 标签: 水题 好久没有打代码,竟然一场比赛两次卡在边界条件上....跪 b.题意很简单...纯模拟就可以了,开始忘记了当字符串结束的时候也要更新两个值,所以就错了 #i ...

  3. jq.paginator分页插件稍加修改

    样式一: 样式二: 此分页功能在jq.paginator分页插件上稍加修改. 必加模板html: <div id="jqPaginator"> <div id=& ...

  4. 初识Spider_Man(爬爬虫)

    一:引子

  5. Oracle_多行函数

      Oracle_多行函数   多行函数min(),max(),count(),sum(),avg() --1.1统计公司的最高工资,最低工资和总人数 --对于null值直接略过,不做运算 --max ...

  6. mysql中的union和order by、limit

      我有一个表 CREATE TABLE `test1` (  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,  `name` varchar(20) N ...

  7. ASP.NET导出word实例

    ASP.NET导出word实例 最近遇到一个题目就是如何在asp.net中将数据导出到word中,由于数据是动态的,所以需要在后台拼出想要的的格式,翻遍了网页找出了一个比较满意的代码,感谢那位高手.代 ...

  8. MySQL 多版本并发控制(MVCC)

    可以认为MVCC是行级锁的一个变种,但是它在很多情况下避免了加锁的操作,因此开销会很低.主要实现的是非阻塞的读操作,写操作也只是锁定必要的行.MVCC的实现是通过保存数据在某个时间点的快照来实现的,也 ...

  9. 黑窗口输入确定数字弹MessageBox(VirtualProtect())

    今天有人说到这个就想的弹一下,刚开始弄了一下,发现内存访问有问题想到可能与读写保护有关,所以用了VirtualProtect函数,得到了正确结果 网上这个小东西我自己没发现,就贴一下.. void m ...

  10. laravel5.5 when()的用法

    当你在使用where语句有前提条件时,比如某值为1的时候才执行where子句,否则不执行,这个时候,laravel5.5新出了一个简便方法when($arg,fun1[,fun2]). 具体用法如下: ...