静态资源访问

静态资源访问

静态资源只要放到类路径下:/static,/public,/resources,/MATE-INF/resources目录下,我们访问静态资源的时候就不需要加路径,直接在默认的根目录就可以访问。

默认的访问策略:如果访问的路径Controller可以处理,那么就会使用Controller路径映射处理,没有的话就会使用访问路径在静态资源目录下查找有无对应的静态资源,如果依然没有,就会出现404错误。

默认情况下,资源映射在 上/**,但您可以使用该spring.mvc.static-path-pattern属性对其进行调整。

spring:
  mvc:
    static-path-pattern: /res/**

还可以使用该spring.web.resources.static-locations属性自定义静态资源位置(将默认值替换为目录位置列表)。根 Servlet 上下文路径"/"也会自动添加为位置。

spring:
  web:
    resources:
      static-locations: [classpath:/haha/]

前面提到的“标准”静态资源位置之外,我们还可以使用Webjars来导入一些静态库,比如Jquery。

将Jquery依赖导入到pom.xml文件中

<dependency>
    <groupId>org.webjars</groupId>
    <artifactId>jquery</artifactId>
    <version>3.6.0</version>
</dependency>

然后可以通过静态路径访问该Jquery文件

为什么说默认情况下我们把静态文件放在上面四个目录SpringBoot就会把这些作为静态资源处理

我们可以通过源码查看得出结论:

public void setStaticLocations(String[] staticLocations) {
    this.staticLocations = appendSlashIfNecessary(staticLocations);
    this.customized = true;
}

欢迎页设置

  • 在静态资源目录下建立一个index.html
  • 使用Controller来建立index的访问映射

自定义 Favicon

将favicon.icon放置到静态资源目录下即可

静态资源配置原理

SpringBoot启动就会默认加载很多自动配置类,比如AopAutoConfiguration,SpringMVC的自动配置类是WebMvcAutoConfiguration,我们还需要根据该类的@Conditional条件注解,查看该类是否生效

@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,
		ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {
}

当我们容器中存在Servlet,DispatcherServlet,WebMvcConfigurer类,没有WebMvcConfigurationSupport类的时候生效。然后还定义的加载顺序,通过@AutoConfigureOrder@AutoConfigureAfter来定义。通过查看该类,可以发现配置大量的组建

与静态资源有关的是一个静态内部类 WebMvcAutoConfigurationAdapter的自动配置类

@Configuration(proxyBeanMethods = false)//这是一个配置类
@Import(EnableWebMvcConfiguration.class)//导入一个类
@EnableConfigurationProperties({ WebMvcProperties.class,
                                org.springframework.boot.autoconfigure.web.ResourceProperties.class, WebProperties.class })//加载Properties属性类文件
@Order(0)
public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer, ServletContextAware {
}

WebMvcProperties与配置文件中的spring.mvc绑定

ResourceProperties与配置文件中的spring.resources绑定

WebProperties与配置文件中的spring.web绑定

这个静态内部类只有一个构造器

public WebMvcAutoConfigurationAdapter(
    //获取和spring.resources绑定的所有的值的对象
    org.springframework.boot.autoconfigure.web.ResourceProperties resourceProperties,
    //获取和spring.web绑定的所有的值的对象
    WebProperties webProperties, 
    //获取和spring.mvc绑定的所有值和对象
    WebMvcProperties mvcProperties, 
    //Spring的对象工厂
    ListableBeanFactory beanFactory,
    //找到所有的HttpMessageConverters
    ObjectProvider<HttpMessageConverters> messageConvertersProvider,
    //找到资源处理器的自定义器
    ObjectProvider<ResourceHandlerRegistrationCustomizer> resourceHandlerRegistrationCustomizerProvider,
    //dispatcherServletPath
    ObjectProvider<DispatcherServletPath> dispatcherServletPath,
    //ServletRegistrationBean给应用注册Servlet、Filter等
    ObjectProvider<ServletRegistrationBean<?>> servletRegistrations) {
    this.resourceProperties = resourceProperties.hasBeenCustomized() ? resourceProperties
        : webProperties.getResources();
    this.mvcProperties = mvcProperties;
    this.beanFactory = beanFactory;
    this.messageConvertersProvider = messageConvertersProvider;
    this.resourceHandlerRegistrationCustomizer = resourceHandlerRegistrationCustomizerProvider.getIfAvailable();
    this.dispatcherServletPath = dispatcherServletPath;
    this.servletRegistrations = servletRegistrations;
    this.mvcProperties.checkConfiguration();
}

所有的参数都会从容器中获取。

资源的默认处理规则

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
    if (!this.resourceProperties.isAddMappings()) {
        logger.debug("Default resource handling disabled");
        return;
    }
    addResourceHandler(registry, "/webjars/**", "classpath:/META-INF/resources/webjars/");
    addResourceHandler(registry, this.mvcProperties.getStaticPathPattern(), (registration) -> {
        registration.addResourceLocations(this.resourceProperties.getStaticLocations());
        if (this.servletContext != null) {
            ServletContextResource resource = new ServletContextResource(this.servletContext, SERVLET_LOCATION);
            registration.addResourceLocations(resource);
        }
    });
}

private void addResourceHandler(ResourceHandlerRegistry registry, String pattern, String... locations) {
    addResourceHandler(registry, pattern, (registration) -> registration.addResourceLocations(locations));
}

private void addResourceHandler(ResourceHandlerRegistry registry, String pattern,
                                Consumer<ResourceHandlerRegistration> customizer) {
    if (registry.hasMappingForPattern(pattern)) {
        return;
    }
    ResourceHandlerRegistration registration = registry.addResourceHandler(pattern);
    customizer.accept(registration);
    registration.setCachePeriod(getSeconds(this.resourceProperties.getCache().getPeriod()));
    registration.setCacheControl(this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl());
    registration.setUseLastModified(this.resourceProperties.getCache().isUseLastModified());
    customizeResourceHandlerRegistration(registration);
}

第一步,静态资源是否启用

if (!this.resourceProperties.isAddMappings()) {
    logger.debug("Default resource handling disabled");
    return;
}

是否启用静态资源规则,我们可以通过属性设置,在WebProperties中设置,默认是true,可以在配置文件中进行设置不启用。

spring:
  web:
    resources:
      add-mappings: false

第二步Webjars有关设置

addResourceHandler(registry, "/webjars/**", "classpath:/META-INF/resources/webjars/");

里面设置了Webjars默认的访问路径classpath:/META-INF/resources/webjars/

第三步设置默认的静态资源设置

addResourceHandler(registry, this.mvcProperties.getStaticPathPattern(), (registration) -> {
				registration.addResourceLocations(this.resourceProperties.getStaticLocations());
    if (this.servletContext != null) {
        ServletContextResource resource = new ServletContextResource(this.servletContext, SERVLET_LOCATION);
        registration.addResourceLocations(resource);
    }
});

首选加载路径,默认情况下是这四个路径:

private static final String[] CLASSPATH_RESOURCE_LOCATIONS = { "classpath:/META-INF/resources/",
				"classpath:/resources/", "classpath:/static/", "classpath:/public/" };

在2.6.7版本中,有一个变化就是对addResourceHandler进行了重载

private void addResourceHandler(ResourceHandlerRegistry registry, String pattern, String... locations) {
    addResourceHandler(registry, pattern, (registration) -> registration.addResourceLocations(locations));
}
//会进行缓存有关设置,默认单位是s
private void addResourceHandler(ResourceHandlerRegistry registry, String pattern,
                                Consumer<ResourceHandlerRegistration> customizer) {
    if (registry.hasMappingForPattern(pattern)) {
        return;
    }
    ResourceHandlerRegistration registration = registry.addResourceHandler(pattern);
    customizer.accept(registration);
    registration.setCachePeriod(getSeconds(this.resourceProperties.getCache().getPeriod()));
    registration.setCacheControl(this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl());
    registration.setUseLastModified(this.resourceProperties.getCache().isUseLastModified());
    customizeResourceHandlerRegistration(registration);
}

//缓存单位
@DurationUnit(ChronoUnit.SECONDS)
private Duration period;
spring:
  web:
    resources:
      cache:
        period: 1000

对资源进行访问的时候可以看到有关缓存的情况

欢迎页的处理规则

@Bean
public WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext applicationContext,
                                                           FormattingConversionService mvcConversionService, ResourceUrlProvider mvcResourceUrlProvider) {
    //创建一个欢迎页处理器映射
    WelcomePageHandlerMapping welcomePageHandlerMapping = new WelcomePageHandlerMapping(
        new TemplateAvailabilityProviders(applicationContext), applicationContext, getWelcomePage(),
        //默认路径是/**
        this.mvcProperties.getStaticPathPattern());
    welcomePageHandlerMapping.setInterceptors(getInterceptors(mvcConversionService, mvcResourceUrlProvider));
    welcomePageHandlerMapping.setCorsConfigurations(getCorsConfigurations());
    return welcomePageHandlerMapping;
}

WelcomePageHandlerMapping(TemplateAvailabilityProviders templateAvailabilityProviders,
                          ApplicationContext applicationContext, Resource welcomePage, String staticPathPattern) {
    //如何我们设置了欢迎页并且路径是/**,也就是我们设置了前缀会导致静态欢迎页失效
    if (welcomePage != null && "/**".equals(staticPathPattern)) {
        logger.info("Adding welcome page: " + welcomePage);
        //请求转发
        setRootViewName("forward:index.html");
    }
    //否则通过Controller处理
    else if (welcomeTemplateExists(templateAvailabilityProviders, applicationContext)) {
        logger.info("Adding welcome page template: index");
        //视图访问
        setRootViewName("index");
    }
}
posted @ 2022-04-24 23:37  无涯子wyz  阅读(611)  评论(0)    收藏  举报