欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

SpringBoot - 静态资源映射处理

程序员文章站 2022-03-18 18:05:51
...

上一篇博文说明了SpringBoot对WebJars的支持,那么项目中还有其他静态资源文件,如自定义CSS、JS、image等,SpringBoot同样提供了映射支持。


【1】静态资源文件映射规则

同样查看WebMVCAutoConfiguration源码如下:

        @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));
            }
        }

具体支持代码如下:

    String staticPathPattern = this.mvcProperties.getStaticPathPattern();
            if (!registry.hasMappingForPattern(staticPathPattern)) {
                customizeResourceHandlerRegistration(
                        registry.addResourceHandler(staticPathPattern)
                                .addResourceLocations(
                                        this.resourceProperties.getStaticLocations())
                        .setCachePeriod(cachePeriod));
            }

第一步拿到staticPathPattern;

    /**
     * Path pattern used for static resources.
     */
    private String staticPathPattern = "/**";

第二步如果资源请求没有对应映射,就添加资源处理器及资源找寻位置并设置缓存

if (!registry.hasMappingForPattern(staticPathPattern)) {
                customizeResourceHandlerRegistration(
                        registry.addResourceHandler(staticPathPattern)
                                .addResourceLocations(
                                        this.resourceProperties.getStaticLocations())
                        .setCachePeriod(cachePeriod));
            }

其中 staticLocations在ResourceProperties类中,源码如下:

/**
 * Properties used to configure resource handling.
 *
 * @author Phillip Webb
 * @author Brian Clozel
 * @author Dave Syer
 * @author Venil Noronha
 * @since 1.1.0
 */
@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);
    }

    /**
     * Locations of static resources. Defaults to classpath:[/META-INF/resources/,
     * /resources/, /static/, /public/] plus context:/ (the root of the servlet context).
     */
    private String[] staticLocations = RESOURCE_LOCATIONS;

即:"/**"访问当前项目的任何资源,只要没有匹配的处理映射,则都去静态资源的文件夹找映射

"classpath:/META‐INF/resources/",
"classpath:/resources/",
"classpath:/static/",
"classpath:/public/"
"/":当前项目的根路径

SpringBoot - 静态资源映射处理


如下图,访问static/asserts/img/bootstrap-solid.svg:

SpringBoot - 静态资源映射处理

URL : http://localhost:8083/asserts/img/bootstrap-solid.svg

这里注意URL上面不要添加静态资源文件夹(路径)的名字,如static、public等等。


【2】默认欢迎页的映射

代码如下:

        @Bean
        public WelcomePageHandlerMapping welcomePageHandlerMapping(
                ResourceProperties resourceProperties) {
            return new WelcomePageHandlerMapping(resourceProperties.getWelcomePage(),
                    this.mvcProperties.getStaticPathPattern());
        }

注册了WelcomePageHandlerMappingBean来处理项目的默认欢迎页面,构造器有两个参数,第一个是项目中存在的欢迎页资源,第二个是静态资源路径映射规则。


① 获取欢迎页资源

其中跟踪resourceProperties.getWelcomePage()源码:

    public Resource getWelcomePage() {
        for (String location : getStaticWelcomePageLocations()) {
            Resource resource = this.resourceLoader.getResource(location);
            try {
                if (resource.exists()) {
                    resource.getURL();
                    return resource;
                }
            }
            catch (Exception ex) {
                // Ignore
            }
        }
        return null;
    }

即找到存在的欢迎页的资源,继续跟getStaticWelcomePageLocations()源码:

private String[] getStaticWelcomePageLocations() {
        String[] result = new String[this.staticLocations.length];
        for (int i = 0; i < result.length; i++) {
            String location = this.staticLocations[i];
            if (!location.endsWith("/")) {
                location = location + "/";
            }
            result[i] = location + "index.html";
        }
        return result;
    }

即 欢迎页的位置为第【1】部分中静态资源文件路径下的index.html。

如下所示:

"classpath:/META‐INF/resources/index.html",
"classpath:/resources/index.html",
"classpath:/static/index.html",
"classpath:/public/index.html"
"/":/index.html

② 静态路径映射规则


    /**
     * Path pattern used for static resources.
     */
    private String staticPathPattern = "/**";

③ WelcomePageHandlerMapping类

static final class WelcomePageHandlerMapping extends AbstractUrlHandlerMapping {

        private static final Log logger = LogFactory
                .getLog(WelcomePageHandlerMapping.class);

        private WelcomePageHandlerMapping(Resource welcomePage,
                String staticPathPattern) {
            if (welcomePage != null && "/**".equals(staticPathPattern)) {
                logger.info("Adding welcome page: " + welcomePage);
                ParameterizableViewController controller = new ParameterizableViewController();
                controller.setViewName("forward:index.html");
                setRootHandler(controller);
                setOrder(0);
            }
        }

        @Override
        public Object getHandlerInternal(HttpServletRequest request) throws Exception {
            for (MediaType mediaType : getAcceptedMediaTypes(request)) {
                if (mediaType.includes(MediaType.TEXT_HTML)) {
                    return super.getHandlerInternal(request);
                }
            }
            return null;
        }

        private List<MediaType> getAcceptedMediaTypes(HttpServletRequest request) {
            String acceptHeader = request.getHeader(HttpHeaders.ACCEPT);
            return MediaType.parseMediaTypes(
                    StringUtils.hasText(acceptHeader) ? acceptHeader : "*/*");
        }

    }

总结:项目访问时,请求如http://localhost:8083/,则SpringBoot会从静态资源文件路径下找index.html,如果存在一个这样的资源,就转发请求到该页面。


【3】项目浏览器图标的映射

SpringBoot同样对项目浏览器图标请求映射做了默认配置。浏览器图片即项目访问时,浏览器页面上呈现的图标,如CSDN图标如下

SpringBoot - 静态资源映射处理


代码如下:

        @Configuration
        @ConditionalOnProperty(value = "spring.mvc.favicon.enabled", matchIfMissing = true)
        public static class FaviconConfiguration {

            private final ResourceProperties resourceProperties;

            public FaviconConfiguration(ResourceProperties resourceProperties) {
                this.resourceProperties = resourceProperties;
            }

            @Bean
            public SimpleUrlHandlerMapping faviconHandlerMapping() {
                SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
                mapping.setOrder(Ordered.HIGHEST_PRECEDENCE + 1);
                mapping.setUrlMap(Collections.singletonMap("**/favicon.ico",
                        faviconRequestHandler()));
                return mapping;
            }

            @Bean
            public ResourceHttpRequestHandler faviconRequestHandler() {
                ResourceHttpRequestHandler requestHandler = new ResourceHttpRequestHandler();
                requestHandler
                        .setLocations(this.resourceProperties.getFaviconLocations());
                return requestHandler;
            }

        }

跟踪源码this.resourceProperties.getFaviconLocations()如下:

List<Resource> getFaviconLocations() {
        List<Resource> locations = new ArrayList<Resource>(
                this.staticLocations.length + 1);
        if (this.resourceLoader != null) {
            for (String location : this.staticLocations) {
                locations.add(this.resourceLoader.getResource(location));
            }
        }
        locations.add(new ClassPathResource("/"));
        return Collections.unmodifiableList(locations);
    }

即在静态资源文件下找所有的favicon.ico !