启动类
@SpringBootApplication
public class Application {
public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class, args);
}
}
进入启动类的方法
直接看SpringApplication.run方法,往下跟两下发现整个启动流程分为两部分,一个SpringBootApplication构造方法和运行run方法
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
注解
@SpringBootApplication注解点开之后一堆注解,只需要关注三个核心注解:
@SpringBootConfiguration
- 本质上是包装了一层@Configuration注解,通过JavaConfig方式对bean进行配置,标识当前类为配置类。
@EnableAutoConfiguration
- 点开@EnableAutoConfiguration注解,只看两个核心注解AutoConfigurationPackage和 @Import(AutoConfigurationImportSelector.class)
- @AutoConfigurationPackage,这个注解跟@Component注解含义类似,一个用来手动添加注解加载自动配置类一个用来扫描指定路径注入bean。@AutoConfigurationPackage 是为了在一个路径下面有多个自动配置的类要加载,这个注解就不用对每个类单独添加 @Import了,直接引入包路径更方便。@ComponentScan 方便对包路径进行自定义,比如一些 Bean 跟 SpringBootApplication 不在一个路径下面。
- @Import(AutoConfigurationImportSelector.class),AutoConfigurationImportSelector类中getCandidateConfigurations()方法,这个方法通过SpringFactoriesLoader.loadFactoryNames()查找位于META-INF/spring.factories文件中的所有自动配置类并加载这些类
@ComponentScan
- 默认扫描当前包以及子包,将有@Component,@Controller,@Service,@Repository等注解的类注册到容器中,以便调用。
SpringApplication构造方法
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 获取应用类型,根据是否加载Servlet类判断是否是web环境
this.webApplicationType = WebApplicationType.deduceFromClasspath();
this.bootstrappers = new ArrayList<>(getSpringFactoriesInstances(Bootstrapper.class));
// 读取META-INFO/spring.factories文件,获取对应的ApplicationContextInitializer装配到集合
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 设置所有监听器
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 推断main函数
this.mainApplicationClass = deduceMainApplicationClass();
}
run 方法
/**
* Run the Spring application, creating and refreshing a new
* {@link ApplicationContext}.
*
* @param args the application arguments (usually passed from a Java main method)
* @return a running {@link ApplicationContext}
*/
public ConfigurableApplicationContext run(String... args) {
// 启动一个秒表计时器,用于统计项目启动时间
StopWatch stopWatch = new StopWatch();
stopWatch.start();
// 创建启动上下文对象即spring根容器
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
// 定义可配置的应用程序上下文变量
ConfigurableApplicationContext context = null;
/**
* 设置jdk系统属性
* headless直译就是无头模式,
* headless模式的意思就是明确Springboot要在无鼠键支持的环境中运行,一般程序也都跑在Linux之类的服务器上,无鼠键支持,这里默认值是true;
*/
configureHeadlessProperty();
/**
* 获取运行监听器 getRunListeners, 其中也是调用了上面说到的getSpringFactoriesInstances 方法
* 从spring.factories中获取配置
*/
SpringApplicationRunListeners listeners = getRunListeners(args);
// 启动监听器
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
// 包装默认应用程序参数,也就是在命令行下启动应用带的参数,如--server.port=9000
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//
/**
* 准备环境 prepareEnvironment 是个硬茬,里面主要涉及到
* getOrCreateEnvironment、configureEnvironment、configurePropertySources、configureProfiles
* environmentPrepared、bindToSpringApplication、attach诸多方法可以在下面的例子中查看
*/
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
// 配置忽略的 bean
configureIgnoreBeanInfo(environment);
// 打印 SpringBoot 标志,即启动的时候在控制台的图案logo,可以在src/main/resources下放入名字是banner的自定义文件
Banner printedBanner = printBanner(environment);
// 创建 IOC 容器
context = createApplicationContext();
// 设置一个启动器,设置应用程序启动
context.setApplicationStartup(this.applicationStartup);
// 配置 IOC 容器的基本信息 (spring容器前置处理)
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
/**
* 刷新IOC容器
* 这里会涉及Spring容器启动、自动装配、创建 WebServer启动Web服务即SpringBoot启动内嵌的 Tomcat
*/
refreshContext(context);
/**
* 留给用户自定义容器刷新完成后的处理逻辑
* 刷新容器后的扩展接口(spring容器后置处理)
*/
afterRefresh(context, applicationArguments);
// 结束计时器并打印,这就是我们启动后console的显示的时间
stopWatch.stop();
if (this.logStartupInfo) {
// 打印启动完毕的那行日志
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
// 发布监听应用上下文启动完成(发出启动结束事件),所有的运行监听器调用 started() 方法
listeners.started(context);
// 执行runner,遍历所有的 runner,调用 run 方法
callRunners(context, applicationArguments);
} catch (Throwable ex) {
// 异常处理,如果run过程发生异常
handleRunFailure(context, ex, listeners);
throw new IllegalStateException(ex);
}
try {
// 所有的运行监听器调用 running() 方法,监听应用上下文
listeners.running(context);
} catch (Throwable ex) {
// 异常处理
handleRunFailure(context, ex, null);
throw new IllegalStateException(ex);
}
// 返回最终构建的容器对象
return context;
}