- 声明原文出处:狂神说
文章目录
- 1. pom.xml
- 1 . 父依赖
- 2 . 启动器 spring-boot-starter
- 2. 主启动类的注解
- 1. 默认的主启动类
- 2. @SpringBootApplication
- 3. @ComponentScan
- 4. @SpringBootConfiguration
- 5. SpringBootApplication 注解
- 6. spring.factories
- 7. 结论
- 8. 简单图解
- 3. 主启动类的方法
- 1. SpringApplication
- 2. run方法流程分析
1. pom.xml
1 . 父依赖
其中它主要是依赖一个父项目,主要是管理项目的资源过滤及插件
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
点进去,发现还有一个父依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.2.5.RELEASE</version>
<relativePath>../../spring-boot-dependencies</relativePath>
</parent>
这里才是真正管理SpringBoot应用里面所有依赖版本的地方,SpringBoot的版本控制中心;
以后我们导入依赖默认是不需要写版本;但是如果导入的包没有在依赖中管理着就需要手动配置版本了
2 . 启动器 spring-boot-starter
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-xxx</artifactId>
</dependency>
springboot-boot-starter-xxx:就是spring-boot的场景启动器
比如 : spring-boot-starter-web:帮我们导入了web模块正常运行所依赖的组件;
SpringBoot将所有的功能场景都抽取出来,做成一个个的starter (启动器),只需要在项目中引入这些starter即可,所有相关的依赖都会导入进来 ,要用什么功能就导入什么样的场景启动器即可
2. 主启动类的注解
1. 默认的主启动类
//@SpringBootApplication 来标注一个主程序类
//说明这是一个Spring Boot应用
@SpringBootApplication
public class SpringbootApplication {
public static void main(String[] args) {
//以为是启动了一个方法,没想到启动了一个服务
SpringApplication.run(SpringbootApplication.class, args);
}
}
2. @SpringBootApplication
- 作用
标注在某个类上说明这个类是SpringBoot的主配置类 , SpringBoot就应该运行这个类的main方法来启动SpringBoot应用
进入这个注解
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)}
)
……
3. @ComponentScan
-
这个注解在Spring中很重要 ,它对应XML配置中的元素。
-
作用
自动扫描并加载符合条件的组件或者bean , 将这个bean定义加载到IOC容器中
4. @SpringBootConfiguration
- 作用
SpringBoot的配置类 ,标注在某个类上 , 表示这是一个SpringBoot的配置类
进入这个注解
// 点进去得到下面的 @Component
@Configuration
public @interface SpringBootConfiguration {}
@Component
public @interface Configuration {}
-
@Configuration
说明这是一个配置类 ,配置类就是对应Spring的xml 配置文件 -
@Component
说明启动类本身也是Spring中的一个组件而已,负责启动应用
5. SpringBootApplication 注解
- @EnableAutoConfiguration
-
作用
开启自动配置功能
该注解告诉SpringBoot开启自动配置功能,这样自动配置才能生效
以前我们需要自己配置的东西,而现在SpringBoot可以自动帮我们配置进入该注解
@Inherited @AutoConfigurationPackage @Import({AutoConfigurationImportSelector.class}) public @interface EnableAutoConfiguration {
- @AutoConfigurationPackage
- 作用
自动配置包
- @import
- 作用:Spring底层注解@import , 给容器中导入一个组件
- @Import({Registrar.class})*
- 作用:将主启动类的所在包及包下面所有子包里面的所有组件扫描到Spring容器
- @Import({AutoConfigurationImportSelector.class})
- 作用 :给容器导入组件
- AutoConfigurationImportSelector :自动配置导入选择器
-
进入AutoConfigurationImportSelector
- 这个类中有一个这样的方法
// 获得候选的配置 protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { //这里的getSpringFactoriesLoaderFactoryClass()方法 //返回的就是我们最开始看的启动自动导入配置文件的注解类;EnableAutoConfiguration List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.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; }
- 这个方法又调用了 SpringFactoriesLoader 类的静态方法!我们进入SpringFactoriesLoader类loadFactoryNames() 方法
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) { String factoryClassName = factoryClass.getName(); //这里它又调用了 loadSpringFactories 方法 return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList()); }
- 我们继续点击查看 loadSpringFactories 方法
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) { //获得classLoader , 我们返回可以看到这里得到的就是EnableAutoConfiguration标注的类本身 MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader); if (result != null) { return result; } else { try { //去获取一个资源 "META-INF/spring.factories" Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories"); LinkedMultiValueMap result = new LinkedMultiValueMap(); //将读取到的资源遍历,封装成为一个Properties while(urls.hasMoreElements()) { URL url = (URL)urls.nextElement(); UrlResource resource = new UrlResource(url); Properties properties = PropertiesLoaderUtils.loadProperties(resource); Iterator var6 = properties.entrySet().iterator(); while(var6.hasNext()) { Entry<?, ?> entry = (Entry)var6.next(); String factoryClassName = ((String)entry.getKey()).trim(); String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue()); int var10 = var9.length; for(int var11 = 0; var11 < var10; ++var11) { String factoryName = var9[var11]; result.add(factoryClassName, factoryName.trim()); } } } cache.put(classLoader, result); return result; } catch (IOException var13) { throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13); } } }
- 发现一个多次出现的文件:spring.factories
6. spring.factories
- 打开spring.factories , 看到了很多自动配置的文件;这就是自动配置根源所在
7. 结论
- SpringBoot 所有的自动配置都是在启动的时候扫描并加载的
- 自动配置真正实现
- 从classpath中搜寻所有的META-INF/spring.factories配置文件
- 并将其中对应的 org.springframework.boot.autoconfigure. 包下的配置项,通过反射实例化为对应标注了 @Configuration的JavaConfig形式的IOC容器配置类
- 然后将这些都汇总成为一个实例并加载到IOC容器中
- 不过有部分配置类不会一开始就自动加载,只有满足 @ConditionalOnXXX(xxxx) 中的条件才会生效 —— 导入对应的 start 启动器
8. 简单图解
3. 主启动类的方法
- 在主启动类的 main 方法中调用这样一个方法
SpringApplication.run - SpringApplication.run 方法主要分两部分
- 一部分是SpringApplication的实例化
- 二是run方法的执行
1. SpringApplication
- 这个类主要做了以下四件事情
- 推断应用的类型是普通的项目还是Web项目
- 查找并加载所有可用初始化器 , 设置到initializers属性中
- 找出所有的应用程序监听器,设置到listeners属性中
- 推断并设置main方法的定义类,找到运行的主类
查看构造器:
public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) {
// ......
this.webApplicationType = WebApplicationType.deduceFromClasspath();
this.setInitializers(this.getSpringFactoriesInstances();
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = this.deduceMainApplicationClass();
}