静态资源
官网地址:Servlet Web Applications :: Spring Boot
在 SpringBoot 应用中,默认情况下,静态资源是放在以下目录中的:
-
/static
-
/public
-
/resources
-
META-INF/resources
我们也可以配置自己的静态资源,配置的方式如下:
spring: mvc: static-path-pattern: "/resources/**"
以上是默认的配置地址,如果我们配置了自己指定的目录,最好是带上默认的地址,否则默认地址不可用。
欢迎页面
官网地址:Servlet Web Applications :: Spring Boot
Spring Boot supports both static and templated welcome pages. It first looks for an index.html file in the configured static content locations. If one is not found, it then looks for an index template. If either is found, it is automatically used as the welcome page of the application. Spring Boot 支持静态和模板化的欢迎页面。 它首先在配置的静态内容位置中查找 index.html 文件。如果没有找到,它会寻找一个索引模板。如果找到其中任何一个,它会自动用作应用程序的欢迎页面。
示例:
在 static 目录下,新建 index.html(必须叫这个名称)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>首页</title> </head> <body> <img src="/01.jpg" width="300"> <img src="/02.png" width="300"> </body> </html>
然后重启项目,在浏览器中输入 http://localhost:8080 即可以访问到欢迎页面。
如果欢迎页不叫 index.html
,那么就需要编写一个控制器来进行重定向到指定的欢迎页面中。假设欢迎页面叫 default.html
@Controller public class HelloController { @GetMapping("/") public String index() { return "redirect:/default.html"; } @GetMapping("/hello") @ResponseBody public String hello() { return "hello spring boot"; } }
如果没有找到静态资源目录下的 index.html,还会去找有没有叫 index 的模板页。
首先创建一个控制器:
@Controller public class IndexController { @GetMapping("/") public String index() { return "index"; } }
然后在 resources 目录下新建 templates 目录,并在这个目录下新建 index.html 文件。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>这是模板中的欢迎页面</h1> </body> </html>
最后在 pom.xml 文件中添加如下依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency>
资源处理原理
SpringBoot 在启动时会默认加载以 AutoConfiguration 结尾的自动配置类,由于 SpringBoot 封装了 SpringMVC 的功能,因此与 Web 开发相关的自动配置类 org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration 也会被自动加载。
@AutoConfiguration(after = { DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,
ValidationAutoConfiguration.class })
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@ImportRuntimeHints(WebResourcesRuntimeHints.class)
public class WebMvcAutoConfiguration {
//......省略.......
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(WebProperties.class)
public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration implements ResourceLoaderAware {
private final Resources resourceProperties;
private final WebMvcProperties mvcProperties;
private final WebProperties webProperties;
private final ListableBeanFactory beanFactory;
private final WebMvcRegistrations mvcRegistrations;
private ResourceLoader resourceLoader;
public EnableWebMvcConfiguration(WebMvcProperties mvcProperties, WebProperties webProperties,
ObjectProvider<WebMvcRegistrations> mvcRegistrationsProvider,
ObjectProvider<ResourceHandlerRegistrationCustomizer> resourceHandlerRegistrationCustomizerProvider,
ListableBeanFactory beanFactory) {
this.resourceProperties = webProperties.getResources();
this.mvcProperties = mvcProperties;
this.webProperties = webProperties;
this.mvcRegistrations = mvcRegistrationsProvider.getIfUnique();
this.beanFactory = beanFactory;
}
//......省略.......
在这个自动装配类中,很多功能我们在后续的学习中都会用上,其中有一个内部叫 EnableWebMvcConfiguration,它有一个构造器,构造器中的参数都是容器中获取。
这个构造器的参数说明:
1. WebMvcProperties mvcProperties:用过读取 spring.mvc 开头的配置 2. WebProperties webProperties:用于读取 spring.web 开头的配置 3. ObjectProvider<WebMvcRegistrations> mvcRegistrationsProvider:注册请求所有处理器 4. ObjectProvider<ResourceHandlerRegistrationCustomizer> resourceHandlerRegistrationCustomizerProvider:自定义配置的处理器 5. ListableBeanFactory beanFactory:Spring的Bean工厂
在这类中用于处理资源默认规则的方法是
@Override public void addResourceHandlers(ResourceHandlerRegistry registry) { if (!this.resourceProperties.isAddMappings()) { logger.debug("Default resource handling disabled"); return; } addResourceHandler(registry, this.mvcProperties.getWebjarsPathPattern(), "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); } }); }
进入 getStaticLocations() 这个方法:
public String[] getStaticLocations() { return this.staticLocations; }
点击 staticLocations 的进入:
public static class Resources { private static final String[] CLASSPATH_RESOURCE_LOCATIONS = { "classpath:/META-INF/resources/", "classpath:/resources/", "classpath:/static/", "classpath:/public/" }; /** * Locations of static resources. Defaults to classpath:[/META-INF/resources/, * /resources/, /static/, /public/]. */ private String[] staticLocations = CLASSPATH_RESOURCE_LOCATIONS;
从上面可以看到,SpringBoot 读取静态资源的默认目录是写死的。
而欢迎页面的处理规则是在 welcomePageHandlerMapping() 方法中:
@Bean public WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext applicationContext, FormattingConversionService mvcConversionService, ResourceUrlProvider mvcResourceUrlProvider) { return createWelcomePageHandlerMapping(applicationContext, mvcConversionService, mvcResourceUrlProvider, WelcomePageHandlerMapping::new); }
进入到WelcomePageHandlerMapping::new对象中:
final class WelcomePageHandlerMapping extends AbstractUrlHandlerMapping { private static final Log logger = LogFactory.getLog(WelcomePageHandlerMapping.class); private static final List<MediaType> MEDIA_TYPES_ALL = Collections.singletonList(MediaType.ALL); WelcomePageHandlerMapping(TemplateAvailabilityProviders templateAvailabilityProviders, ApplicationContext applicationContext, Resource indexHtmlResource, String staticPathPattern) { setOrder(2); WelcomePage welcomePage = WelcomePage.resolve(templateAvailabilityProviders, applicationContext, indexHtmlResource, staticPathPattern); if (welcomePage != WelcomePage.UNRESOLVED) { logger.info(LogMessage.of(() -> (!welcomePage.isTemplated()) ? "Adding welcome page: " + indexHtmlResource : "Adding welcome page template: index")); ParameterizableViewController controller = new ParameterizableViewController(); controller.setViewName(welcomePage.getViewName()); setRootHandler(controller); } }
点击 WelcomePage.resolve() 方法,并进入到该方法中:
static WelcomePage resolve(TemplateAvailabilityProviders templateAvailabilityProviders, ApplicationContext applicationContext, Resource indexHtmlResource, String staticPathPattern) { if (indexHtmlResource != null && "/**".equals(staticPathPattern)) { // 如果在静态资源目录下(/static、/public、/resources、/META-INF/resources)能够找到 index.html,则显示 return new WelcomePage("forward:index.html", false); } if (templateAvailabilityProviders.getProvider("index", applicationContext) != null) { // 如果能够调用到 Controller 中的 /index 模板,则显示 return new WelcomePage("index", true); } return UNRESOLVED; }