我们之前写的HelloSpringBoot,到底是怎么运行的呢,Maven项目,我们一般从pom.xml文件探究起;
1、pom.xml
父依赖
其中它主要是依赖一个父项目,主要是管理项目的资源过滤及插件!
点进去,发现还有一个父依赖和一些资源配置,比如yml
spring-boot-dependencies点进去可以看到有很多版本仓库
- spring-boot-dependencies:核心依赖在父工程中
- 我们在写或者引入一些springboot依赖的时候,不需要指定版本,就因为有这些版本仓库
这里才是真正管理SpringBoot应用里面所有依赖版本的地方,SpringBoot的版本控制中心;以后我们导入依赖默认是不需要写版本;但是如果导入的包没有在依赖中管理着就需要手动配置版本了;
2、启动器spring-boot-starter
默认有一个这样的依赖,如果没有这个那么springboot下面的就崩了。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
- springboot-boot-starter-xxx:就是spring-boot的场景启动器
- spring-boot-starter-web:帮我们导入了web模块正常运行所依赖的组件;
- SpringBoot将所有的功能场景都抽取出来,做成一个个的starter (启动器),只需要在项目中引入这些starter即可,所有相关的依赖都会导入进来 , 我们要用什么功能就导入什么样的场景启动器即可 ;我们未来也可以自己自定义 starter;
3、主程序
分析完了 pom.xml 来看看这个启动类
//@SpringBootApplication 来标注这一个主程序类
//说明这是一个Spring Boot应用
@SpringBootApplication
public class SpringbootApplication {
public static void main(String[] args) {
//将springboot应用启动
SpringApplication.run(SpringbootApplication.class, args);
}
}
3.1、run方法详细分析
不简单的方法
我最初以为就是运行了一个main方法,没想到却开启了一个服务;
@SpringBootApplication
public class SpringbootApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootApplication.class, args);
}
}
SpringApplication.run分析
分析该方法主要分两部分,一部分是SpringApplication的实例化,二是run方法的执行;
查看构造器:
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
this.webApplicationType = WebApplicationType.deduceFromClasspath();
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
run方法流程分析
对照上面的图查看相对应的源码
springboot启动过程:
springboot
是通过main
方法下的SpringApplication.run
方法启动的,启动的时候他会调用refshContext
方法,先刷新容器,然后根据解析注解或者解析配置文件的形式注册bean
,而它是通过启动类的SpringBootApplication
注解进行开始解析的,他会根据EnableAutoConfiguration
开启自动化配置,里面有个核心方法ImportSelect
选择性的导入,根据loadFanctoryNames
根据classpash
路径以MATA-INF/spring.factorces
下面以什么什么EnableAutoConfiguration
开头的key
去加载里面所有对应的自动化配置,他并不是把这一百二十多个自动化配置全部导入,在他每个自动化配置里面都有条件判断注解,先判断是否引入相互的jar包,再判断容器是否有bean再进行注入到bean容器
4、启动类的注解分析
4.1、@SpringBootApplication
- 作用:标注在某个类上说明这个类是SpringBoot的主配置类 , SpringBoot就应该运行这个类的main方法来启动SpringBoot应用;
- 进入这个注解:可以看到上面还有很多其他注解!
进去之后有这些注解
4.1.1、@ComponentScan
- 扫描当前主启动类同级的包
- 这个注解在Spring中很重要 ,它对应XML配置中的元素。
- 作用:自动扫描并加载符合条件的组件或者bean , 将这个bean定义加载到IOC容器中
4.1.2、@SpringBootConfiguration
- 作用:SpringBoot的配置类 ,标注在某个类上 , 表示这是一个SpringBoot的配置类;
- 我们继续进去这个注解查看
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
//...
}
- 可以看到进去之后还有有一个
@Configuration
注解
4.1.2.1、@Configuration
- 这里的 @Configuration,说明这是一个配置类 ,配置类就是对应Spring的xml 配置文件;
- 这个注解点进去还有
@Component
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
//
}
@Component
这就说明,启动类本身也是Spring中的一个组件而已,负责启动应用!- 我们回到 SpringBootApplication 注解中继续看。
4.1.3、@EnableAutoConfiguration
- @EnableAutoConfiguration :开启自动配置功能,自动导入包
- 以前我们需要自己配置的东西,而现在SpringBoot可以自动帮我们配置 ;
- @EnableAutoConfiguration告诉SpringBoot开启自动配置功能,这样自动配置才能生效;
- 点进注解接续查看:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
//
}
@AutoConfigurationPackage
注解@Import(AutoConfigurationImportSelector.class)
注解
4.1.3.1、@AutoConfigurationPackage
- @AutoConfigurationPackage :自动配置包
- 点进去里面查看
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
//
}
@Import(AutoConfigurationPackages.Registrar.class)
:自动注册包,扫描到包之后就进行注册@import
:Spring底层注解@import , 给容器中导入一个组件- Registrar.class 作用:将主启动类的所在包及包下面所有子包里面的所有组件扫描到Spring容器 ;
- 这个分析完了,退到上一步,继续看
4.1.3.2、@Import({AutoConfigurationImportSelector.class})
@Import({AutoConfigurationImportSelector.class})
:自动导入包的核心,给容器导入组件 ;AutoConfigurationImportSelector
:自动配置导入选择器,那么它会导入哪些组件的择器呢?我们点击去这个类看源码:- 方法
getAutoConfigurationEntry(AnnotationMetadata annotationMetadata)
获得自动配置的实体- 方法里面调用方法:
getCandidateConfigurations(annotationMetadata, attributes);
:获取候选的配置
- 方法里面调用方法:
- 方法
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
//获取候选的配置
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = getConfigurationClassFilter().filter(configurations);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
- 点击
getCandidateConfigurations
进来方法getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes)
:- 这个方法又调用了
SpringFactoriesLoader
类的静态方法!我们进入SpringFactoriesLoader
类有个loadFactoryNames()
方法,这个方法有一个参数getSpringFactoriesLoaderFactoryClass()
- 这个方法又调用了
// 获得候选的配置
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;
}
- 点进去
getSpringFactoriesLoaderFactoryClass()
有如下方法。
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
return EnableAutoConfiguration.class;//标注了EnableAutoConfiguration注解的类
}
- 进入
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
类。可以获取项目资源和系统资源。
- 点击资源那里的参数
FACTORIES_RESOURCE_LOCATION
进去可以获取配置资源路径,知道从这里获取配置。
- 继续看下面的代码有个while循环,从这些资源中遍历所有的nextElement(自动配置),遍历完成之后,封装为Properties供我们使用
- 发现一个多次出现的文件:spring.factories,全局搜索它。所有的自动配置类都在这里
- 思考:spring.factories里面有很多自动配置为什么有的没有生效,需要导入对应的start才能有作用!
- 有个核心注解
@ConditionalOnxxx
:如果这里面的条件都满足,才会生效。
结论:springboot所有的自动配置都是在启动的时候扫描并加载:spring.factories
所有的自动配置类都在这里面,但是不一定生效,,要判断条件是否成立,只要导入了对应的start,就有对应的启动器了,有了启动器,我们自动装配就会生效,然后就配置成功了
- springboot在启动的时候,从路径下
/META-INF/spring.factories
获取指定的值 - 将这些自动装配的类导入容器,自动配置就会生效,帮我们进行自动配置
- 以前我们需要手动配置的东西,现在springboot帮我们做了
- 整合javaEE,解决方案和自动配置的东西都在
spring-boot-autoconfigure-2.2.0.RELEASE.jar
这个包下 - 他会把所有需要导入的组件,以类名的方式返回,这些组件就会被添加到容器
- 容器中也会存在非常多的
xxxAutoConfiguration(@Bean)
的文件,就是这些类给容器中导入了这个场景需要的所有组件,并自动配置,@Configuration,JavaConfig - 有了自动配置类,免去了我们手动编写配置文件的工作
4.2、spring.factories
我们根据源头打开spring.factories , 看到了很多自动配置的文件;这就是自动配置根源所在!
WebMvcAutoConfiguration
我们在上面的自动配置类随便找一个打开看看,比如 :WebMvcAutoConfiguration
可以看到这些一个个的都是JavaConfig配置类,而且都注入了一些Bean,可以找一些自己认识的类,看着熟悉一下!
所以,自动配置真正实现是从classpath中搜寻所有的META-INF/spring.factories配置文件 ,并将其中对应的 org.springframework.boot.autoconfigure. 包下的配置项,通过反射实例化为对应标注了 @Configuration的JavaConfig形式的IOC容器配置类 , 然后将这些都汇总成为一个实例并加载到IOC容器中。
结论:
- SpringBoot在启动的时候从类路径下的META-INF/spring.factories中获取EnableAutoConfiguration指定的值
- 将这些值作为自动配置类导入容器 , 自动配置类就生效 , 帮我们进行自动配置工作;
- 整个J2EE的整体解决方案和自动配置都在springboot-autoconfigure的jar包中;
- 它会给容器中导入非常多的自动配置类 (xxxAutoConfiguration), 就是给容器中导入这个场景需要的所有组件 ,
并配置好这些组件 ; - 有了自动配置类 , 免去了我们手动编写配置注入功能组件等的工作;
现在大家应该大概的了解了下,SpringBoot的运行原理,后面我们还会深化一次!