SpringBoot的核心思想:自动配置
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.1.7.RELEASEversion>
<relativePath/>
parent>
<groupId>com.muhangroupId>
<artifactId>springboot_study01artifactId>
<version>0.0.1-SNAPSHOTversion>
<name>springboot_study01name>
<description>Demo project for Spring Bootdescription>
<properties>
<java.version>1.8java.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
project>
我们发现,在springboot的依赖中都是 spring-boot-starter-???
,而这就是springboot的一个个场景启动器。如果我们想要配置web项目,那我们只需要导入springboot的web项目的启动器就可以了,springboot就会帮我们自动装配,就会帮我们配置我们需要的一切
我们从子项目中可以点击进入它的父项目
点进来之后我们可以看到,这个父项目就是springboot的主依赖,在父项目中配置了很多,字符编码,配置文件过滤,各种各样的插件等等,进入父项目,这里才是真正管理SpringBoot应用里面所有依赖版本的地方,SpringBoot的版本控制中心;
以后我们导入依赖默认是不需要写版本;但是如果导入的包没有在依赖中管理着就需要手动配置版本了。
我们要想加一个包,就要去找这个包的启动器,一般我们都会去官网上去找。如果我们自己添加了一个包的版本和springboot不兼容,那么这个包就不会被spring托管,也就不会产生任何作用。
分析这个主程序类,我们发现这个类上有一个注解@SpringBootApplication
,这个注解是用来标注这是一个springboot的主程序类,表明它是一个springboot应用,是创建项目时自己生成的,类中是程序主入口的main方法,方法里面有一个run
方法用来运行springboot
我们点进去@SpringBootApplication
这个注解,发现这个注解上有很多注解
下面我们一个个的去分析这些注解
其中前四个注解为元注解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Inherited
元注解是一个标记注解,@Inherited
阐述了某个被标注的类型是被继承的。 如果一个使用了@Inherited
修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。我们点进去这个注解,发现它除了三个元注解就只有一个@Configuration
自定义注解
然后我们再点进去@Configuration
注解,发现除了三个元注解还有一个@Component
注解
得出结论:有了@Component这个注解,说明这个类就是一个spring的一个组件,或者说是springboot的一个组件,因为有了这个组件所以它才能存在于IOC容器中,才能被spring所管理,
点进去@EnableAutoConfiguration
这个注解,我们发现除了元注解外有两个自定义注解@AutoConfigurationPackage
和@Import(AutoConfigurationImportSelector.class)
(1)@AutoConfigurationPackage——自动导入以及注册包
点进去这个注解,发现它注册了一个注册器Registrar
我们再点进去这个Registrar
,发现它实现了一个ImportBeanDefinitionRegistrar
接口,实现这个接口让该类成为了拥有注册bean的能力
得出结论:@AutoConfigurationPackage
注解就是一个自动导入以及注册包的注解,是因为它里面的Registrar实现了ImportBeanDefinitionRegistrar
接口
(2)@Import(AutoConfigurationImportSelector.class)——@EnableAutoConfiguration自动导入的核心
先点进去AutoConfigurationImportSelector
中,发现这个类是一个很庞大的类,里面定义了许多自动配置,导包东西。
重点方法:getCandidteConfigurations()
我们在这个类中找到getCandidteConfigurations()
这个方法
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
getBeanClassLoader());
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
+ "are using a custom packaging, make sure that file is correct.");
return configurations;
}
我们发现在getCandidteConfigurations()
这个方法里又调用了SpringFactoriesLoader.loadFactoryNames()
这个方法。
这个方法是由SpringFactoriesLoader
(spring的一个工厂加载器)去loadFactoryNames
(获得它的名字)。里面传入了getSpringFactoriesLoaderFactoryClass(),
参数,(获得这个类的Class),和getBeanClassLoader()
参数(获得它里面的bean,就是所有组件的类加载器)
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
getBeanClassLoader());
然后下面又使用了断言Assert,去断言这个configurations不为空,就去找一个文件META-INF/spring.factories
,如果找不到就需要去这个文件里配置(大概是这个意思)
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
+ "are using a custom packaging, make sure that file is correct.");
getCandidteConfigurations()—>loadFactoryNames()
分析完这些我们点进去loadFactoryNames
这个方法看一下
getCandidteConfigurations()—>loadFactoryNames()—>loadSpringFactories()
我们可以发现loadFactoryNames()
中传入的getBeanClassLoader()
类加载器 被传入loadSpringFactories()
方法中了
来分析loadSpringFactories()
方法,首先我们发现这个传入的参数ClassLoader
前面有一个@Nullable
的注解,这个注解的意思就是说该参数可以为空。然后我们分析代码发现如果ClassLoader
不为空它就回去找这个资源FACTORIES_RESOURCE_LOCATION
META-INF/spring.factories
我们点进去这个FACTORIES_RESOURCE_LOCATION
看看到底要找哪个资源,点进去发现,原来如此!如果这个传过来的Classloader
不为空,就回去找META-INF/spring.factories
这个资源。真的是应证了之前的断言。
然后我们去找这个文件看看,在Maven里面的spring-boot-autoconfigure
包下的META-INF/spring.factories
我们再看看这个文件里面有什么
打开之后发现这个文件里面配置了许多许多东西,有初始化的配置,应用程序监听器配置,自动配置导入监听器,自动配置导入过滤器,自动配置,故障分析仪,模板可用性提供者配置…一大堆配置
META-INF/spring.factories—>WebMvcAutoConfiguration
我们在Auto Configure自动配置中找到WebMvcAutoConfiguration
这个配置类(其他类也一样),然后点进去
WebMvcAutoConfiguration—>WebMvcAutoConfigurationAdapter
WebMvcAutoConfiguration
这个类是一个非常庞大的类,我们不去管它,直接去找这个类中WebMvcAutoConfigurationAdapter
这个类,这个类就是WebMvc自动配置的适配器类
我们看见里面有两个注解:@Import(EnableWebMvcConfiguration.class)
和@EnableConfigurationProperties({ WebMvcProperties.class, ResourceProperties.class })
这两个注解的作用是
1.导入自动配置的文件,
2.自动加载了一些配置文件:WebMvcProperties
WebMvcAutoConfigurationAdapter–>WebMvcProperties(类上注解的参数)
然后我们点进去WebMvcProperties
看一下都是什么,点进去发现它有一个前缀spring.mvc
,而且它可以配置很多东西,比如格式化,国际化,日期格式等等…一些配置
然后我们再分析WebMvcProperties
这个类,发现这个类是为了让我们去自己去配置一些东西的。那么我们分析一下,得出结论:每一个*****AutoConfiguration
都对应一个****Properties
配置
那么我们应该怎么去自己配置呢?
我们发现项目的resources目录下有一个application.properties文件(项目自动生成的),这个配置文件就是用来自定义配置的
根据刚才我们分析的源码,我们发现可以使用spring.mvc为前缀去配置一些东西(这里面的配置就是对应刚才我们找到的WebMvcProperties
中的一些可以配置的东西)。假如我们再这里配置了,那么springboot的默认配置就会失效
当然我们还可以新建一个application.yml,并使用yml的风格去配置
分析WebMvcAutoConfigurationAdapter
然后我们分析WebMvcAutoConfigurationAdapter
这个类,发现它配置了视图解析器
还配置了国际化资源,就是点击切换中英文功能
还有消息代码解析器,主要是用来处理乱码的
还配置了一些格式化的资源,比如时间日期就要用到这个对象,需要手动去配置
WebMvcAutoConfiguration—>WebMvcAutoConfigurationAdapter—>addResourceHandlers(重点方法)
然后!重点来了:还配置了资源处理器,addResourceHandlers
方法可以让我们自己去添加自己想要的资源。我们分析代码发现,如果我们想要导入一个外部资源就要去遵循addResourceHandlers
方法里面的规则。
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
if (!this.resourceProperties.isAddMappings()) {
logger.debug("Default resource handling disabled");
return;
}
Duration cachePeriod = this.resourceProperties.getCache().getPeriod();
CacheControl cacheControl = this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl();
//如果/webjars/**下面已经有资源了就去classpath:/META-INF/resources/webjars/下去找
if (!registry.hasMappingForPattern("/webjars/**")) {
customizeResourceHandlerRegistration(registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/")
.setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));
}
//如过没有的话,就会去staticPathPattern下去找,staticPathPattern实质就是"/**",也就是/webjars/**下面说没有的话就去/**下去找
String staticPathPattern = this.mvcProperties.getStaticPathPattern();
if (!registry.hasMappingForPattern(staticPathPattern)) {
customizeResourceHandlerRegistration(registry.addResourceHandler(staticPathPattern)
.addResourceLocations(getResourceLocations(this.resourceProperties.getStaticLocations()))
.setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));
}
}
我们还发现一个问题,在springboot项目中并没有之前我们使用ssm框架时的webapp
目录,那么我们的外部资源应该放在哪里呢?
然后我们再细细的分析这段代码,发现它会去找/webjars/**
目录下面所有的资源,我们再之后的springboot的学习中会用到。
导入资源的第一种方式:使用webjars
的导入一个jquery
资源
找到jquery的webjars的maven依赖
将maven依赖复制到pom.xml中
然后我们可以去maven中查找jquery的包,我们可以找到该资源
然后我们再来分析这段代码,发现它还有自己给出的静态资源路径
我们继续点进去看一下到底它设定了哪些静态资源路径供我们使用,发现它返回了一个staticPathPattern
我们再点进去看一下,发现它给staticPathPattern
赋值为"/**"
,也就是说静态资源放在根目录下也是可以的
分析完这个我们返回去继续分析WebMvcAutoConfigurationAdapter这个类
WebMvcAutoConfiguration—>WebMvcAutoConfigurationAdapter–>FaviconConfiguration(图标)
发现一个好玩的东西:可以设置网站的图标spring.mvc.favicon.enabled
,可以用过这个组件去自定义网站的图标,但是图标名必须是**/favicon.ico
,路径是根目录下的哪个位置,比如说resources目录下放一个名为favicon.ico
的图标就可以将原有的小叶子替换掉