目录
1.2 @EnableAutoConfiguration的作用
3. SpringApplication的run方法的执行流程
3.1 获取监听器SpringApplicationRunListeners
SpringBoot项目的启动主要可以分为两个部分,第一个部分是@SpringBootApplication注解,第二个部分是SpringApplication的实例化和run方法执行流程。
1. @SpringBootApplication注解
@SpringBootApplication是SpringBoot的核心注解,它包含了@Configuration和@EnableAutoConfiguration以及@ComponentScan这三个注解。
1.1 @Configuration的作用
@Configuration注解可以实现标识为配置类的作用,@SpringBootApplication中包含这个注解代表可以将Bean的创建和配置在该类中。
1.2 @EnableAutoConfiguration的作用
@EnableAutoConfiguration会扫描应用程序类路径上的所有依赖项,包括项目依赖项和SpringBoot的自动配置模块。SpringBoot会根据条件(应用程序的类路径、已经存在的配置、其他配置属性)选择合适的自动配置类,并将他们作为Spring Bean注册到应用程序上下文中。SpringBoot还提供了包括数据源、Web服务器、消息队列、缓存等常见组件的默认配置。
SpringBoot的自动装配是指SpringBoot在启动时会扫描外部引用jar包的META-INF/spring.factories文件,将文件中配置的类型信息加载到Spring容器,并执行类中定义的各种操作。对于外部jar来说只需要按照SpringBoot定义的标准(引入时引入SpringBoot版本的jar包)就能将自己的功能装置进SpringBoot。
我们也可以关闭某个自动配置的选项,如关闭数据源自动装配功能:@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class }),这里就可以设置关闭SpringBoot的数据源的自动装配,这个功能,我们在使用Redis的时候使用过,配置Redis就是相当于配置了一个数据源,但是因为SpringBoot会自动装配数据源,就会发生循环依赖的情况,此时我们就需要关闭他的自动装配功能。
我们可以通过application.properties和application.yml修改SpringBoot自动配置的默认值,比如修改自动配置的服务启动端口,微服务的限流规则,数据库的连接地址等。
对于SpringBoot的自动装配式通过AutoConfigurationImportSelector类实现的。该类实现了ImportSelector接口,也就是实现了该接口下的selectImports方法,能够获取所有符合条件的类的全限定类名,并将这些类加载到IOC容器中。即能调用这些类的方法了。
1.3 @ComponentScan的作用
@ComponentScan注解用户告诉Spring哪个packages的用注解标识的类,会被spring自动扫描并装入bean容器。@EnableAutoConfiguration默认会自动扫描@SpringBootApplication所在类的同级别包以及下级包的Bean(JPA项目还会扫描@Entity的实体类)。
2. SpringApplication的实例化
对于SpringApplication的实例创建,会进入如下所示run源码,目的是创建一个primarySource(这个类就是我们项目的启动类)的类。
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class[]{primarySource}, args);
}
对于上面的run(new Class[]{primarySource}, args);会调用源码中的另外一个run方法,该方法首先根据SpringApplication的构造函数,传入我们的primarySource启动类创建一个SpringApplication类。
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return (new SpringApplication(primarySources)).run(args);
}
对于new SpringApplication(primarySources)调用SpringApplication.class的构造方法,在该构造方法中进行了复制成员变量、推断web应用类型、加载并初始化组件、配置主方法所在类这四个操作。
- 使用this.resourceLoader = resourceLoader代码确定resumeLoader的值,使用this.primarySources = new LinkedHashSet(Arrays.asList(primarySources))确定成员变量primarySources的值。
- 接下来是对web的应用类型的判断,判断web应用是none还是servlet还是reactive。
- 然后加载并初始化组件ApplicationContextInitializer和ApplicationListener
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.sources = new LinkedHashSet();
this.bannerMode = Mode.CONSOLE;
this.logStartupInfo = true;
this.addCommandLineProperties = true;
this.addConversionService = true;
this.headless = true;
this.registerShutdownHook = true;
this.additionalProfiles = new HashSet();
this.isCustomEnvironment = false;
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
this.webApplicationType = WebApplicationType.deduceFromClasspath();
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = this.deduceMainApplicationClass();
}
3. SpringApplication的run方法的执行流程
创建SpringApplication类后再调用该类的run(args)方法,生成一个ConfigurableApplicationContext类(SpringBoot最核心的内容-应用容器)。SpringApplication类的run方法主要有接下来几个功能。
3.1 获取监听器SpringApplicationRunListeners
我们首先获取SpringApplicationRunListeners对象listeners,然后使用代码listeners.starting();启动监听器。
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
this.configureHeadlessProperty();
SpringApplicationRunListeners listeners = this.getRunListeners(args);
listeners.starting();
3.2 启动监听器后开始配置环境,进行环境的初始化。
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = this.prepareEnvironment(listeners,applicationArguments);
this.configureIgnoreBeanInfo(environment);
然后设置Banner的配置
Banner printedBanner = this.printBanner(environment);
3.3 创建应用上下文模块
首先创建上下文对象context,然后使用方法prepareContext设置基础属性配置,最后使用refreshContext方法更新上下文,利用工程模式,产生Bean。
context = this.createApplicationContext();
exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
this.refreshContext(context);