springboot系列--web相关知识探索一
一、web知识探索概述
一、探索大纲
二、SpringMVC原理流程图
二、springmvc自动配置
springboot在底层自动帮我们配置好了mvc所需要的各个组件。当然,我们也可以自己定制化相关组件。
可参考官方文档:
三、静态资源探究
一、静态资源存放位置
静态资源一般是存放在当前类路径下(classpath),在 Spring Boot 中既指程序在打包前的
/java/
目录加上/resource
目录,也指程序在打包后生成的/classes/
目录。两者实际上指的是同一个目录,里面包含的文件内容一模一样。
一、只要静态资源放在类路径下:
/static
(或/public
或/resources
或/META-INF/resources
)的目录访问 : 当前项目根路径/ + 静态资源名 就能访问到对应路径下的静态资源。
二、原理:
当前项目根路径/ + 静态资源名,为什么就能访问到静态资源,springboot是怎么知道我们只要找静态资源,而不是找请求呢。
1、首相,可以准备一张叫做a.jpg的图片放置到/static路径下,然后再准备一个接口,请求路径就是当前项目根路径/ + a.jpg,然后请求当前项目根路径/ + a.jpg 这个路径会发现,这个时候返回的是a接口返回的数据。
2、原理就是,请求的时候资源默认映射到/**,也就是说请求根据当前项目根路径/,然后找这个路径下所有的接口,看看那个接口的路径是a.jpg的,看看Controller能不能处理,不能处理的话,就将所有请求又都交给静态资源处理器,静态资源处理器就会到指定路径下找一个叫 a.jpg的资源,如果静态资源也找不到则响应404页面。
二、改变默认的静态资源路径
一、静态资源访问前缀
默认情况下是无前缀的,如果需要改变,可以在yaml配置中加入:
spring:
mvc:
static-path-pattern: /res/**
二、改变资源存放位置
静态资源默认是存放在
/static
(或/public
或/resources
或/META-INF/resources
)的目录下,我们也可以改变他的位置,这个时候就只能放在a这个目录下才能访问到了,放在其他位置上一律访问不到。如果想加入更多文件夹位置,只需要在数组中用逗号隔离就可以。spring:
resources:
static-locations: [classpath:/a/]
三、webjars资源
一、什么是webjars
其实就是把js、css这些东西,弄成了一个jar包,导入到项目中使用。比如:在pom文件中导入对应的jqery文件jar包,就可以在项目中使用。
二、访问路径
按照官方文档所说,如果需要访问对应的webjar中的资源,路径需要从/webjars/**开始,然后拼接上webjar下面的文件路径以及目标名,即http://localhost:8080/webjars/jquery/3.5.1/jquery.js
四、欢迎页支持
Spring Boot 支持静态和模板化的欢迎页面。它首先在配置的静态内容位置查找
index.html
文件。如果没有找到,它会寻找index
模板(例如写一个能够处理index请求idea接口,最终跳回index页面)。如果找到任何一个,它会自动用作应用程序的欢迎页面。访问项目根路径就可以访问到欢迎页。
一、静态资源路径下 index.html
1、 可以配置静态资源路径,但是不可以配置静态资源的访问前缀。否则导致 index.html不能被默认访问
spring:
# mvc:
# static-path-pattern: /res/** 这个会导致welcome page功能失效resources:
static-locations: [classpath:/haha/]二、controller能处理/index
五、自定义Favicon
favicon.ico 放在静态资源目录下即可。以后访问每个页面都会显示这个图标
spring:
# mvc:
# static-path-pattern: /res/** 这个会导致 Favicon 功能失效
四、静态资源配置原理
一、SpringBoot启动默认加载 META-INF/spring.factories 路径下的所有xxxAutoConfiguration 类(自动配置类),这个时候SpringMvc功能的自动配置类 WebMvcAutoConfiguration就被加载到容器中。
二、springboot加载了WebMvcAutoConfiguration后给容器中配置了那些组件,可参考WebMvcAutoConfiguration类。
一、OrderedHiddenHttpMethodFilter组件,主要是兼容rest风格请求
@Bean @ConditionalOnMissingBean({HiddenHttpMethodFilter.class}) @ConditionalOnProperty( prefix = "spring.mvc.hiddenmethod.filter", name = {"enabled"}, matchIfMissing = false ) public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() { return new OrderedHiddenHttpMethodFilter(); }
二、OrderedFormContentFilter组件,表达内容过滤器
@Bean @ConditionalOnMissingBean({FormContentFilter.class}) @ConditionalOnProperty( prefix = "spring.mvc.formcontent.filter", name = {"enabled"}, matchIfMissing = true ) public OrderedFormContentFilter formContentFilter() { return new OrderedFormContentFilter(); }
三、 WebMvcAutoConfigurationAdapter内部配置类。
一、@EnableConfigurationProperties注解在这个类的作用
在这个类上方有一个注解@EnableConfigurationProperties({WebMvcProperties.class, ResourceProperties.class, WebProperties.class}),说明这个配置文件的相关属性和这三个类进行了绑定,也就是说我们可以在yaml文件中配置绑定的数据,这个配置文件对应的组件就会产生不同的功能。
1、WebMvcProperties==spring.mvc
2、ResourceProperties==spring.resources
3、WebProperties==spring.web
二、有参构造器赋值
这个WebMvcAutoConfigurationAdapter类只有一个有参构造器,也就是说这个类的成员属性都是通过这个有参构造器赋值的。 有参构造器所有参数的值都会从容器中找
//WebProperties webProperties;可以获取和spring.resources绑定的所有的值的对象
//WebMvcProperties mvcProperties 获取和spring.mvc绑定的所有的值的对象
//ListableBeanFactory beanFactory Spring的beanFactory
//HttpMessageConverters 找到所有的HttpMessageConverters
//ResourceHandlerRegistrationCustomizer 找到 资源处理器的自定义器。=========
//DispatcherServletPath
//ServletRegistrationBean 给应用注册Servlet、Filter....
public WebMvcAutoConfigurationAdapter(WebProperties webProperties, WebMvcProperties mvcProperties, ListableBeanFactory beanFactory, ObjectProvider<HttpMessageConverters> messageConvertersProvider, ObjectProvider<ResourceHandlerRegistrationCustomizer> resourceHandlerRegistrationCustomizerProvider, ObjectProvider<DispatcherServletPath> dispatcherServletPath, ObjectProvider<ServletRegistrationBean<?>> servletRegistrations) {
this.mvcProperties = mvcProperties;
this.beanFactory = beanFactory;
this.messageConvertersProvider = messageConvertersProvider;
this.resourceHandlerRegistrationCustomizer = (ResourceHandlerRegistrationCustomizer)resourceHandlerRegistrationCustomizerProvider.getIfAvailable();
this.dispatcherServletPath = dispatcherServletPath;
this.servletRegistrations = servletRegistrations;
this.mvcProperties.checkConfiguration();
}
三、WebMvcAutoConfigurationAdapter内部配置类里面往容器中加入了视图解析器
@Bean @ConditionalOnMissingBean public InternalResourceViewResolver defaultViewResolver() { InternalResourceViewResolver resolver = new InternalResourceViewResolver(); resolver.setPrefix(this.mvcProperties.getView().getPrefix()); resolver.setSuffix(this.mvcProperties.getView().getSuffix()); return resolver; }
四、EnableWebMvcConfiguration内部配置类
一、资源处理的默认规则
这个类绑定了@EnableConfigurationProperties({WebProperties.class}),其中成员属性通过有参构造方法进行复制,会在容器中找到对应类型的值。
// ResourceProperties 继承了WebProperties.Resources资源类
public EnableWebMvcConfiguration(ResourceProperties resourceProperties, WebMvcProperties mvcProperties, WebProperties webProperties, ObjectProvider<WebMvcRegistrations> mvcRegistrationsProvider, ObjectProvider<ResourceHandlerRegistrationCustomizer> resourceHandlerRegistrationCustomizerProvider, ListableBeanFactory beanFactory) {
this.resourceProperties = (WebProperties.Resources)(resourceProperties.hasBeenCustomized() ? resourceProperties : webProperties.getResources());
this.mvcProperties = mvcProperties;
this.webProperties = webProperties;
this.mvcRegistrations = (WebMvcRegistrations)mvcRegistrationsProvider.getIfUnique();
this.resourceHandlerRegistrationCustomizer = (ResourceHandlerRegistrationCustomizer)resourceHandlerRegistrationCustomizerProvider.getIfAvailable();
this.beanFactory = beanFactory;
}
静态资源处理规则主要是这个方法:
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
super.addResourceHandlers(registry);
// 如果addMappings为false,则禁用了静态资源,默认为true,else才是静态资源请求规则
if (!this.resourceProperties.isAddMappings()) {
logger.debug("Default resource handling disabled");
} else {
ServletContext servletContext = this.getServletContext();
// 这个方法是先从缓存中获取webjars,单位为秒,并且注册一下路径作为访问规则,这个只是webjar的规则
this.addResourceHandler(registry, "/webjars/**", "classpath:/META-INF/resources/webjars/");
// 这个是先从缓存中获取静态资源,并且注册一下路径作为访问规则,若没有再yaml中配置默认是/**,例如localhost:80/a.jpg,就会默认在指定路径下查找,代码有设置默认值,这里就不放了。如果再yaml文件中配置了,就会按照配置的路劲来。
this.addResourceHandler(registry, this.mvcProperties.getStaticPathPattern(), (registration) -> {
registration.addResourceLocations(this.resourceProperties.getStaticLocations());
if (servletContext != null) {
// 这里是如果有在yaml文件中配置,static-path-pattern属性,就会按照配置上的来。
registration.addResourceLocations(new Resource[]{new ServletContextResource(servletContext, "/")});
}
});
}
}
spring:
resources:
add-mappings: false 禁用所有静态资源规则
默认静态资源访问路径
二、欢迎页规则
当前类下会由一个方法注入欢迎页配置规则。请求进来会经过前端控制器,然后前端控制器就会去请求处理器映射器,通过处理器映射器找到能够处理的handle,然后通过反射去调用。而欢迎页的处理则是在处理器映射器这一步做了业务处理。
@Bean
public WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext applicationContext, FormattingConversionService mvcConversionService, ResourceUrlProvider mvcResourceUrlProvider) {
// 主要规则在这个有参构造方法里面,this.getWelcomePage()这个路径和静态资源路径,系统给以同一个默认路径。this.mvcProperties.getStaticPathPattern()这个默认是/**
WelcomePageHandlerMapping welcomePageHandlerMapping = new WelcomePageHandlerMapping(new TemplateAvailabilityProviders(applicationContext), applicationContext, this.getWelcomePage(), this.mvcProperties.getStaticPathPattern());
welcomePageHandlerMapping.setInterceptors(this.getInterceptors(mvcConversionService, mvcResourceUrlProvider));
welcomePageHandlerMapping.setCorsConfigurations(this.getCorsConfigurations());
return welcomePageHandlerMapping;
}
WelcomePageHandlerMapping(TemplateAvailabilityProviders templateAvailabilityProviders, ApplicationContext applicationContext, Resource welcomePage, String staticPathPattern) {
// 如果欢迎页不为空,同时请求路径符合/** (指的就是项目根路径/index.html,其中index.html指的就是欢迎页,项目根路径指的就是localhost:80,或者是域名),这个时候就能访问到欢迎页,也就是说如果yaml配置了static-path-pattern: /res/**就无法访问到欢迎页
if (welcomePage != null && "/**".equals(staticPathPattern)) {
logger.info("Adding welcome page: " + welcomePage);
this.setRootViewName("forward:index.html");
} else if (this.welcomeTemplateExists(templateAvailabilityProviders, applicationContext)) {
// 如果只有欢迎页,但是请求路径不符和,则会去调用Controller找到/index结尾的接口
logger.info("Adding welcome page template: index");
this.setRootViewName("index");
}
}