众所周知,每个网站基本上都有自己的静态资源,而我们在 SpringMVC 中的静态资源是通过绝对路径来访问。而在 SpringBoot 中则是只要把静态资源放在类路径下就可直接访问。
/static
or/public
or/resources
or/META-INF/resources
访问方法:
当前项目根路径 / + 静态资源名。
例如:把一张图片放在 /static
的路径下,然后运行访问该静态资源。
访问:
从上图看则成功访问,剩下的几个路径我们就不再一一的去尝试了。因为最后的效果都是一样的,有兴趣的小伙伴可以自己去尝试尝试哦~~~
通过上述大家有没有想过一个问题——如果我们 Controller 中也有和静态资源重名的访问,则浏览器会是显示谁呢???
OK,带着这个问题我们来实验一下。首先创建 Controller 层中的请求,然后通过浏览器再次进行访问!!!
创建控制器:
浏览器访问:
我们能看见没有了静态资源,取而代之的却是 Controller 中的请求返回值!!!(所有浏览器都知道我帅嘿嘿嘿)
原理: 静态映射/**。
请求进来,先去找Controller看能不能处理。不能处理的所有请求又都交给静态资源处理器。静态资源也找不到则响应404页面
在上述我们访问静态资源时一般是默认无 前缀 的,而当在做一个很大的项目的时候一般都会通过一些 静态前缀
来访问一些静态资源。这样做的好处不仅仅是清晰明了,它也更能够减少 BUG 的出现。接下来我们就来讲解怎么添加前缀!
我们可以通过 application.yaml
文件中配置:
当前项目加 + static - path - pattern + 静态资源名 == 静态资源文件夹下找。
spring:
mvc:
static-path-pattern: /res/**
上述配置中则表示在其中多加了一个 res
路径。
按照上述配置文件配置后,我们再来运行程序。当我们还是通过最开始的无前缀访问则会发生什么呢?
无前缀:
没有意外的出现了最烦人的 404 。
有前缀:
能看到上图成功访问!!!
WebJars是将客户端(浏览器)资源(JavaScript,Css等)打成jar包文件,以对资源进行统一依赖管理。WebJars的jar包部署在Maven中央仓库上。
我们在开发Java web项目的时候会使用像Maven,Gradle等构建工具以实现对jar包版本依赖管理,以及项目的自动化管理,但是对于JavaScript,Css等前端资源包,我们只能采用拷贝到webapp目录下的手工方式,这样做就无法对这些资源进行依赖管理。而且容易导致文件混乱、版本不一致等问题。那么 WebJars就提供给我们这些前端资源的jar包形式 ,我们就可以进行 依赖管理。
<dependency>
<groupId>org.webjarsgroupId>
<artifactId>jqueryartifactId>
<version>3.5.1version>
dependency>
访问地址:http://localhost:8080/webjars/jquery/3.5.1/jquery.js 后面地址要按照依赖里面的包路径
因为目前的项目都是前后端分离了,所以在这个地方我们只需要做一些了解就可以!
通过 SpringBoot 的官方文档我们能知道如果有一个 index.html
放在静态资源文件下,则项目启动时会把这个html自动当成 欢迎页 。
我们来写一个 index.html
文件放在静态资源目录下面试一试:
注: 如果没有访问成功则需要记住自己yaml
中的配置是否影响到路径访问!!!
访问:
首先配置 yaml
中的文件:
spring.mvc.favicon.enable=false
然后在静态资源文件目录下面放入一张 ico
格式的图片,清理缓存(这个一点要做,不然他还是按照你的默认来的!!!)
例如:
运行结果:
我们在启动 SpringBoot 时一般会默认的加载 xxxAutoConfiguration
类(自动配置类)
所以我们探索静态配置原理的时候我们就需要去 SpringMVC 中查看源码:
SpringBoot-autoconfigure—>web文件夹—>servlet—> WebMvcAutoConfiguration
@Configuration(
proxyBeanMethods = false
)
@ConditionalOnWebApplication(
type = Type.SERVLET
)
@ConditionalOnClass({Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class})
@ConditionalOnMissingBean({WebMvcConfigurationSupport.class})//容器中没该组件的时候下面的代码才会生效。
@AutoConfigureOrder(-2147483638)
@AutoConfigureAfter({DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class, ValidationAutoConfiguration.class})
生效后的代码(给容器中配了什么):
@Configuration(
proxyBeanMethods = false
)
@Import({WebMvcAutoConfiguration.EnableWebMvcConfiguration.class})
@EnableConfigurationProperties({WebMvcProperties.class, WebProperties.class})
@Order(0)
public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer, ServletContextAware {
配置文件和 xxx 进行相关的绑定:
WebMvcProperties.class
中)WebProperties.class
中)通过对 WebMvcAutoConfigurationAdapter
的查看发现只有一个有参构造器
//有参构造器所有参数的值都会从容器中找
//WebProperties webProperties:相当于和spring.web绑定的所有值的对象。
//WebMvcProperties mvcProperties:相当于和spring.mvc绑定的所有值的对象。
//ListableBeanFactory beanFactory: Spring的beanFactory
//HttpMessageConverters:找到系统中所有的HttpMessageConverters
//ResourceHandlerRegistrationCustomizer:找到 资源处理器的自定义器
//DispatcherServletPath :DispatcherServlet能处理的路径
//ServletRegistrationBean 给应用注册Servlet、Filter....
public WebMvcAutoConfigurationAdapter(WebProperties webProperties, WebMvcProperties mvcProperties, ListableBeanFactory beanFactory, ObjectProvider<HttpMessageConverters> messageConvertersProvider, ObjectProvider<WebMvcAutoConfiguration.ResourceHandlerRegistrationCustomizer> resourceHandlerRegistrationCustomizerProvider, ObjectProvider<DispatcherServletPath> dispatcherServletPath, ObjectProvider<ServletRegistrationBean<?>> servletRegistrations) {
this.resourceProperties = webProperties.getResources();
this.mvcProperties = mvcProperties;
this.beanFactory = beanFactory;
this.messageConvertersProvider = messageConvertersProvider;
this.resourceHandlerRegistrationCustomizer = (WebMvcAutoConfiguration.ResourceHandlerRegistrationCustomizer)resourceHandlerRegistrationCustomizerProvider.getIfAvailable();
this.dispatcherServletPath = dispatcherServletPath;
this.servletRegistrations = servletRegistrations;
this.mvcProperties.checkConfiguration();
}
资源处理的默认规则(重点)
因为这个地方 SpringBoot
的版本和雷神视频中的 SpringBoot
版本不一样,所以就直接自己需要自己debug自己的代码:
public void addResourceHandlers(ResourceHandlerRegistry registry) {
//this.resourceProperties是获取的资源webProperties.getResources();
//且isAddMapping的源码是:
// public boolean isAddMappings() {
//return this.addMappings;
// } 补充默认:this.addMappings = true;
//则本if判断的意思就是如果addMapping为True的时候就会执行logger.debug这个操作。默认值的时候也就是没有禁用静态资源
if (!this.resourceProperties.isAddMappings()) {
logger.debug("Default resource handling disabled");
} else {
this.addResourceHandler(registry, "/webjars/**", "classpath:/META-INF/resources/webjars/");
this.addResourceHandler(registry, this.mvcProperties.getStaticPathPattern(), (registration) -> {
registration.addResourceLocations(this.resourceProperties.getStaticLocations());
if (this.servletContext != null) {
ServletContextResource resource = new ServletContextResource(this.servletContext, "/");
registration.addResourceLocations(new Resource[]{resource});
}
});
}
}
如果是webjars包下的内容,会自动的找到/META-INF/resources/webjars/路径。
如果是一些网页、图片之类的会先拿到WebMvcProperties类中的staticPathPattern值,也就是前缀!
然后再去获取WebProperties类中的CLASSPATH_RESOURCE_LOCATIONS路径。最后一起添加注册处理器。
禁用静态资源:
spring:
resources:
add-mappings: false 禁用所有静态资源规则
@ConfigurationProperties(prefix = "spring.resources", ignoreUnknownFields = false)
public class ResourceProperties {
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;