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

查看官方注释

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

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

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

ResourcesBeanDefinitionParser#parse()-解析逻辑

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

  1. @Override
  2. public BeanDefinition parse(Element element, ParserContext parserContext) {
  3. Object source = parserContext.extractSource(element);
  4. //注册ResourceUrlProvider对象,主要是设置对每个请求都设置上RESOURCE_URL_PROVIDER_ATTR属性,供获取此对象
  5. //在ResourceResolver中会使用
  6. registerUrlProvider(parserContext, source);
  7. //解析location属性,注册为ResourceHttpRequestHandler对象
  8. String resourceHandlerName = registerResourceHandler(parserContext, element, source);
  9. if (resourceHandlerName == null) {
  10. return null;
  11. }
  12. //解析mapping属性,注册SimpleUrlHandlerMapping对象
  13. Map<String, String> urlMap = new ManagedMap<String, String>();
  14. String resourceRequestPath = element.getAttribute("mapping");
  15. if (!StringUtils.hasText(resourceRequestPath)) {
  16. parserContext.getReaderContext().error("The 'mapping' attribute is required.", parserContext.extractSource(element));
  17. return null;
  18. }
  19. //mapping对应的值与ResourceHttpRequestHandler匹配
  20. urlMap.put(resourceRequestPath, resourceHandlerName);
  21. RuntimeBeanReference pathMatcherRef = MvcNamespaceUtils.registerPathMatcher(null, parserContext, source);
  22. RuntimeBeanReference pathHelperRef = MvcNamespaceUtils.registerUrlPathHelper(null, parserContext, source);
  23. RootBeanDefinition handlerMappingDef = new RootBeanDefinition(SimpleUrlHandlerMapping.class);
  24. handlerMappingDef.setSource(source);
  25. handlerMappingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
  26. handlerMappingDef.getPropertyValues().add("urlMap", urlMap);
  27. handlerMappingDef.getPropertyValues().add("pathMatcher", pathMatcherRef).add("urlPathHelper", pathHelperRef);
  28. //order属性-执行顺序,越大优先级越低
  29. String order = element.getAttribute("order");
  30. // Use a default of near-lowest precedence, still allowing for even lower precedence in other mappings
  31. handlerMappingDef.getPropertyValues().add("order", StringUtils.hasText(order) ? order : Ordered.LOWEST_PRECEDENCE - 1);
  32. //SimpleUrlHandlerMapping添加corsConfigurations属性
  33. RuntimeBeanReference corsConfigurationsRef = MvcNamespaceUtils.registerCorsConfigurations(null, parserContext, source);
  34. handlerMappingDef.getPropertyValues().add("corsConfigurations", corsConfigurationsRef);
  35. //注册SimpleUrlHandlerMapping到spring bean工厂中
  36. String beanName = parserContext.getReaderContext().generateBeanName(handlerMappingDef);
  37. parserContext.getRegistry().registerBeanDefinition(beanName, handlerMappingDef);
  38. parserContext.registerComponent(new BeanComponentDefinition(handlerMappingDef, beanName));
  39. //注册BeanNameUrlHandlerMapping/HttpRequestHandlerAdapter/SimpleControllerHandlerAdapter到bean工厂
  40. MvcNamespaceUtils.registerDefaultComponents(parserContext, source);
  41. return null;
  42. }

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

ResourcesBeanDefinitionParser#registerResourceHandler()-解析location属性

简要分析下源码

  1. private String registerResourceHandler(ParserContext parserContext, Element element, Object source) {
  2. //获取location属性,此属性不可为空
  3. String locationAttr = element.getAttribute("location");
  4. if (!StringUtils.hasText(locationAttr)) {
  5. parserContext.getReaderContext().error("The 'location' attribute is required.", parserContext.extractSource(element));
  6. return null;
  7. }
  8. //支持location多路径和classpath前缀,其中以,分隔
  9. ManagedList<String> locations = new ManagedList<String>();
  10. locations.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(locationAttr)));
  11. //创建ResourceHttpRequestHandler bean
  12. RootBeanDefinition resourceHandlerDef = new RootBeanDefinition(ResourceHttpRequestHandler.class);
  13. resourceHandlerDef.setSource(source);
  14. resourceHandlerDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
  15. //添加locations属性
  16. MutablePropertyValues values = resourceHandlerDef.getPropertyValues();
  17. values.add("locations", locations);
  18. //添加cacheSeconds属性-cache有效时间
  19. String cacheSeconds = element.getAttribute("cache-period");
  20. if (StringUtils.hasText(cacheSeconds)) {
  21. values.add("cacheSeconds", cacheSeconds);
  22. }
  23. //解析子节点mvc:cache-control cache控制器
  24. Element cacheControlElement = DomUtils.getChildElementByTagName(element, "cache-control");
  25. if (cacheControlElement != null) {
  26. CacheControl cacheControl = parseCacheControl(cacheControlElement);
  27. values.add("cacheControl", cacheControl);
  28. }
  29. //解析mvc:resource-chain 包含mvc:resolver/mvc:transformers
  30. //对应ResourceHttpRequestHandler#resourceResolvers/resourceTransformers属性
  31. Element resourceChainElement = DomUtils.getChildElementByTagName(element, "resource-chain");
  32. if (resourceChainElement != null) {
  33. parseResourceChain(resourceHandlerDef, parserContext, resourceChainElement, source);
  34. }
  35. //处理请求中的media type
  36. Object manager = MvcNamespaceUtils.getContentNegotiationManager(parserContext);
  37. if (manager != null) {
  38. values.add("contentNegotiationManager", manager);
  39. }
  40. //注册ResourceHttpRequestHandler
  41. String beanName = parserContext.getReaderContext().generateBeanName(resourceHandlerDef);
  42. parserContext.getRegistry().registerBeanDefinition(beanName, resourceHandlerDef);
  43. parserContext.registerComponent(new BeanComponentDefinition(resourceHandlerDef, beanName));
  44. return beanName;
  45. }

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

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

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

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

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

mapping属性绑定ResourceHttpRequestHandler资源获取类

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

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

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

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

  1. public static void registerDefaultComponents(ParserContext parserContext, Object source) {
  2. registerBeanNameUrlHandlerMapping(parserContext, source);
  3. registerHttpRequestHandlerAdapter(parserContext, source);
  4. registerSimpleControllerHandlerAdapter(parserContext, source);
  5. }

小结

  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. BZOJ 1207: [HNOI2004]打鼹鼠【妥妥的n^2爆搜,dp】

    1207: [HNOI2004]打鼹鼠 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 3259  Solved: 1564[Submit][Statu ...

  2. [bzoj1999]树网的核

    从下午坑到网上..noip的数据太弱,若干的地方写挂结果还随便过= = 最坑的就是网上有些题解没考虑周全... 第一步是找直径,用两次bfs(或者dfs,Linux下系统栈挺大的..)解决.找出其中一 ...

  3. poj_2528Mayor's posters(线段树)

    poj_2528Mayor's posters(线段树) 标签: 线段树 题目连接 Mayor's posters Time Limit: 1000MS Memory Limit: 65536K To ...

  4. Monthly update for Dynamics 365 for Operation

    日期 标题, 类别 版本 描述 2017/8/22 Dyn 365 Fin and Ops, Ent ed July 2017 Plat Update 10 Category: Download   ...

  5. [学习OpenCV攻略][009][从摄像机读入数据]

    cvCreateCameraCapture(设备ID) 创建一个摄像机视频,返回值是CvCapture*类型.设备ID表示设备的编号,如果有多个摄像机设备,-1表示随机选择一个设备. #include ...

  6. putty怎么用?如何使用Putty远程管理Linux主机

    Putty是一个免费的Windows 32平台下用于telnet.rlogin和ssh客户端的远程客户端工具,可以通过PUTTY快速的实现SSH连接linux等主机,下面小编就给大家演示一下如何使用P ...

  7. 2.移植3.4内核-使内核支持烧写yaffs2

    在上章-制作文件系统,并使内核成功启动jffs2文件系统了 本章便开始使内核支持烧写yaffs2文件系统 1.首先获取yaffs2源码(参考git命令使用详解) cd /work/nfs_root g ...

  8. ios开发 第三天

    1.复合 对象可以引用其它对象,可以利用其它对象提供的特性. 通过包含作为实例变量的对象指针实现的. 2.OC是单一继承 3.继承-重构 4.类实例化对象时,self指向了对象的首地址. 类对象isa ...

  9. jstl 的判断使用

    JSTL  是JSP的标准标记库 1.必须引入的头部标签 <%@ taglib uri="http://java.sun.com/jstl/core_rt"prefix=&q ...

  10. 关于JAVA字符编码:Unicode,ISO-8859-1,GBK,UTF-8编码及相互转换

    我们最初学习计算机的时候,都学过ASCII编码. 但是为了表示各种各样的语言,在计算机技术的发展过程中,逐渐出现了很多不同标准的编码格式, 重要的有Unicode.UTF.ISO-8859-1和中国人 ...