博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Spring Boot 静态资源访问原理解析
阅读量:6248 次
发布时间:2019-06-22

本文共 8171 字,大约阅读时间需要 27 分钟。

一、前言

  springboot配置静态资源方式是多种多样,接下来我会介绍其中几种方式,并解析一下其中的原理。

二、使用properties属性进行配置

  应该说 spring.mvc.static-path-pattern 和 spring.resources.static-locations这两属性是成对使用的,如果不明白其中的原理,总会出现资源404的情况。首先收一下spring.mvc.static-path-pattern代表的是一个Ant Path路径,例如resources/**,表示当你的路径中存在resources/**的时候才会处理请求。比如我们访问“”时,很显然,springboot逻辑中会根据模式匹配对url进行匹配,匹配命中后,是如何再定位到具体的资源的呢?这时候spring.resources.static-locations的配置就起作用了。

  忘记说了,在springboot中spring.mvc.static-path-pattern的默认值是/**,spring.resources.static-locations的默认值是classpath:/static,classpath:/public,classpath:/resources,classpath:/META-INF/resources,servlet context:/,springboot中相关的ResourceHttpRequestHandler就会去spring.resources.static-locations配置的所有路径中寻找资源文件。

  所以我之前才说spring.mvc.static-path-pattern 和 spring.resources.static-locations这两属性是成对使用的。

三、springboot中默认对静态资源的处理

  调试过程中,通过查看 org.springframework.web.servlet.DispatcherServlet中的handlerMappings变量,我们发现有一个很显眼的 resourceHandlerMapping ,这个是springboot为我们提供的一个默认的静态资源handler,通过全文搜索发现出现在org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport这个类中,也就是这个类包含了@EnableWebMvc注解中的大多数功能,更多的扩展功能请参考org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration。

  resourceHandlerMapping 的定义如下。

/** * Return a handler mapping ordered at Integer.MAX_VALUE-1 with mapped * resource handlers. To configure resource handling, override * {
@link #addResourceHandlers}. */@Beanpublic HandlerMapping resourceHandlerMapping() { ResourceHandlerRegistry registry = new ResourceHandlerRegistry(this.applicationContext, this.servletContext, mvcContentNegotiationManager()); addResourceHandlers(registry); AbstractHandlerMapping handlerMapping = registry.getHandlerMapping(); if (handlerMapping != null) { handlerMapping.setPathMatcher(mvcPathMatcher()); handlerMapping.setUrlPathHelper(mvcUrlPathHelper()); handlerMapping.setInterceptors(new ResourceUrlProviderExposingInterceptor(mvcResourceUrlProvider())); handlerMapping.setCorsConfigurations(getCorsConfigurations()); } else { handlerMapping = new EmptyHandlerMapping(); } return handlerMapping;}

  请大家先记住ResourceHandlerRegistry这个类。

    首先看一下addResourceHandlers(registry);这个方法,父类DelegatingWebMvcConfiguration做了实现,如下。

 
private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();
@Overrideprotected void addResourceHandlers(ResourceHandlerRegistry registry) {    this.configurers.addResourceHandlers(registry);}

  其中WebMvcConfigurerComposite是操作了WebMvcConfigurer类型的对象的集合。在org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration这个springmvc的自动配置类中,有一个WebMvcConfigurer的实现类,如下。

// Defined as a nested config to ensure WebMvcConfigurerAdapter is not read when not// on the classpath@Configuration@Import(EnableWebMvcConfiguration.class)@EnableConfigurationProperties({ WebMvcProperties.class, ResourceProperties.class })public static class WebMvcAutoConfigurationAdapter extends WebMvcConfigurerAdapter {    ...        @Override    public void addResourceHandlers(ResourceHandlerRegistry registry) {        if (!this.resourceProperties.isAddMappings()) {            logger.debug("Default resource handling disabled");            return;        }        Integer cachePeriod = this.resourceProperties.getCachePeriod();        if (!registry.hasMappingForPattern("/webjars/**")) {            customizeResourceHandlerRegistration(                    registry.addResourceHandler("/webjars/**")                            .addResourceLocations(                                    "classpath:/META-INF/resources/webjars/")                    .setCachePeriod(cachePeriod));        }        String staticPathPattern = this.mvcProperties.getStaticPathPattern();        if (!registry.hasMappingForPattern(staticPathPattern)) {            customizeResourceHandlerRegistration(                    registry.addResourceHandler(staticPathPattern)                            .addResourceLocations(                                    this.resourceProperties.getStaticLocations())                    .setCachePeriod(cachePeriod));        }    }        ...}

  上面的addResourceHandlers方法中,增加了默认的mapping pattern = /webjars/** ,默认的resource location是classpath:/META-INF/resources/webjars/。正是这里的配置,我们在集成swagger的时候,就可以正常访问到swagger webjars中的js文件了。其中红色的代码部分就是用户可以自定义的默认静态资源访问方式,并通过ResourceHandlerRegistry对象进行注册。接着看一下mvcProperties和resourceProperties对应的类吧。

@ConfigurationProperties("spring.mvc")public class WebMvcProperties {    ...    /**     * Path pattern used for static resources.     */    private String staticPathPattern = "/**";    ...}

  WebMvcProperties类中的staticPathPattern field 对应了spring.mvc.static-path-pattern这个属性,可以看到默认值是 "/**"。

@ConfigurationProperties(prefix = "spring.resources", ignoreUnknownFields = false)public class ResourceProperties implements ResourceLoaderAware {    .....    private static final String[] SERVLET_RESOURCE_LOCATIONS = { "/" };    private static final String[] CLASSPATH_RESOURCE_LOCATIONS = {            "classpath:/META-INF/resources/", "classpath:/resources/",            "classpath:/static/", "classpath:/public/" };    private static final String[] RESOURCE_LOCATIONS;    static {        RESOURCE_LOCATIONS = new String[CLASSPATH_RESOURCE_LOCATIONS.length                + SERVLET_RESOURCE_LOCATIONS.length];        System.arraycopy(SERVLET_RESOURCE_LOCATIONS, 0, RESOURCE_LOCATIONS, 0,                SERVLET_RESOURCE_LOCATIONS.length);        System.arraycopy(CLASSPATH_RESOURCE_LOCATIONS, 0, RESOURCE_LOCATIONS,                SERVLET_RESOURCE_LOCATIONS.length, CLASSPATH_RESOURCE_LOCATIONS.length);    }    private String[] staticLocations = RESOURCE_LOCATIONS;    ......}

  ResourceProperties中staticLocations field 对应了 spring.resources.static-locations 这个属性。可以看到默认值是classpath:[/META-INF/resources/, /resources/, /static/, /public/], servlet context:/

四、静态资源的Bean配置

  在了解了springboot默认资源的配置的原理(即 spring.mvc.static-path-pattern 和 spring.resources.static-locations),我们可以增加一个WebMvcConfigurer类型的bean,来添加静态资源的访问方式,还记得上面说的“请记住ResourceHandlerRegistry这个类“,下面就用到了哦。

@Configurationpublic class ResourceWebMvcConfigurer extends WebMvcConfigurerAdapter {    @Override    public void addResourceHandlers(ResourceHandlerRegistry registry) {        registry.addResourceHandler("/resources/**")                .addResourceLocations("classpath:/public-resources/")                .setCacheControl(CacheControl.maxAge(1, TimeUnit.HOURS).cachePublic());    }}

  那么当访问路径中包含"resources/**"的时候,resource handler就会去classpath:/public-resources目录下寻找了。

五、静态资源的查找

  参考 org.springframework.web.servlet.resource.ResourceHttpRequestHandler,ResourceHttpRequestHandler中通过org.springframework.web.servlet.resource.PathResourceResolver进行查找。

  举个例子,下图是springboot打包之后的目录结构,现在想要通过url访问application.properties文件,springboot默认的静态文件配置可以吗?当然需要用事实来说话了。

   

   我们已经知道,默认的resource locations中有个 servlet-context:/,访问你的url是工程名/application.properties,调试一下PathResourceResolver,结果如下。

  

  

  发现servlet-context的根路径如上图所示,查看一下这个路径对应的目录,发现什么都没有,所以很显然无法找到我们要找的文件了。毕竟一般使用springboot都是jar项目,servlet-context path下没有用户自定义的资源。

 六、其他方式

  在Servlet3协议规范中,包含在JAR文件/META-INFO/resources/路径下的资源可以直接访问了。如果将springboot项目打包成war包,可以配置一个默认的servlet。在WebMvcConfigurationSupport中已经定义好了,不过默认是一个EmptyHandlerMapping。

/** * Return a handler mapping ordered at Integer.MAX_VALUE with a mapped * default servlet handler. To configure "default" Servlet handling, * override {
@link #configureDefaultServletHandling}. */@Beanpublic HandlerMapping defaultServletHandlerMapping() { DefaultServletHandlerConfigurer configurer = new DefaultServletHandlerConfigurer(servletContext); configureDefaultServletHandling(configurer); AbstractHandlerMapping handlerMapping = configurer.getHandlerMapping(); handlerMapping = handlerMapping != null ? handlerMapping : new EmptyHandlerMapping(); return handlerMapping;}

  可以通过自定义一个WebMvcConfigurer类型的bean,改写configureDefaultServletHandling 方法,如下。

@Configurationpublic class MyWebConfigurer extends WebMvcConfigurerAdapter {    @Override    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {        configurer.enable();    }}

  这样就设置了一个默认的servlet,在加载静态资源的时候就会按照servelt方式去加载了。

 

  就先分享这么多了,更多分享请关注我们的技术公众号吧!!!

转载地址:http://bmria.baihongyu.com/

你可能感兴趣的文章
类似gitbook的wiki选择
查看>>
java -----------------LeetCode——电话号码的字母组合
查看>>
Java 学习(12)--接口
查看>>
特斯拉又熬过破产危机后,马斯克说自动驾驶芯片已经准备好了
查看>>
单机单网卡最大tcp长连接数真的是65535吗?
查看>>
ubutu系统 安装eclipse
查看>>
JavaScript正则表达式元字符总结
查看>>
Awesome Python
查看>>
ubuntu14.04安装opencv2.4.10
查看>>
Windows 7 延长支持服务价格曝光:一台电脑最低25美元
查看>>
64 位设备上抛弃 32 位安卓应用,谷歌公布了具体日期
查看>>
你还在为20倍的连麦成本发愁吗?
查看>>
【SQLServer】【恢复挂起的解决方案】附加文件时候的提示“无法重新生成日志,原因是数据库关闭时存在打开的事务/用户,该数据库没有检查点或者该数据库是只读的。 ”【数据库恢复】...
查看>>
开源大数据周刊-第91期
查看>>
云计算---openstack实例共享80、443端口
查看>>
福布斯传媒选择Oracle营销云提升广告收入
查看>>
pandas入门指南
查看>>
数值的整数次方
查看>>
[20180105]oracle临时表补充.txt
查看>>
单体架构风格
查看>>