上一篇 【Spring】六、Spring与MyBatis整合核心源码分析
下一篇【SpringBoot】二、SpringBoot自动配置原理
启动流程主要分为三个部分:
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
public static ConfigurableApplicationContext run(Class<?> primarySource,
String... args) {
//调用run() 方法
return run(new Class<?>[] { primarySource }, args);
}
public static ConfigurableApplicationContext run(Class<?>[] primarySources,
String[] args) {
return new SpringApplication(primarySources).run(args);
}
//这个run方法主要做两件事情
//1、new了一个SpringApplication 这么一个对象;
(1)把main方法这个类赋给一个成员变量;
(2)判断是java项目还是web项目;
(3)设置了一个初始化器和事件发送的监听器;
//2、然后执行SpringApplication对象的run()方法;
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
//1、先把主类保存起来
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
//2、判断运行项目的类型
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//3、扫描当前路径下META-INF/spring.factories文件的,加载ApplicationContextInitializer接口实例
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
//4、扫描当前路径下META-INF/spring.factories文件的,加载ApplicationListener接口实例
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
//判断main方法的类
this.mainApplicationClass = deduceMainApplicationClass();
}
这里面用到了一种类似于Java SPI机制的方式扫描META-INF/spring.factories这个文件,并且加载?ApplicationContextInitializer、ApplicationListener 接口实例;
(1)ApplicationContextInitializer 这个类当springboot上下文Context初始化完成后会调用;
(2)ApplicationListener 当springboot启动时事件change后都会触发
我们可以自定义实现ApplicationContextInitializer、ApplicationListener 接口,
然后运行时会被触发执行;
public ConfigurableApplicationContext run(String... args) {
<!--这是一个计时器,不是很重要-->
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
<!--这是设置了一些环境变量信息,不是很重要-->
configureHeadlessProperty();
<!--获取事件监听器SpringApplicationRunListener类型,并且执行starting()方法-->
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
<!--把参数args封装成DefaultApplicationArguments,了解一下即可-->
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
<!--这个是准备环境,并且把环境跟spring上下文绑定好,并且执行environmentPrepared()方法-->
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
<!--判断一些环境的值,并设置一些环境的值-->
configureIgnoreBeanInfo(environment);
<!--打印banner-->
Banner printedBanner = printBanner(environment);
<!--创建spring ioc上下文,根据项目类型创建上下文-->
context = createApplicationContext();
<!--获取异常报告事件监听-->
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
<!--准备spring ioc上下文,执行完成后调用contextPrepared()方法,
contextLoaded()方法-->
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
<!--这是刷新和创建spring ioc容器,这里会扫描bean定义并初始化单例bean对象-->
//这个refreshContext()不仅创建bean,还启动了内嵌的web容器,非常重要
refreshContext(context);
<!--什么事都没有做-->
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
<!--执行ApplicationRunListeners中的started()方法-->
listeners.started(context);
<!--执行Runner(ApplicationRunner和CommandLineRunner)-->
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, listeners, exceptionReporters, ex);
throw new IllegalStateException(ex);
}
listeners.running(context);
return context;
}
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
switch (this.webApplicationType) {
case SERVLET:
contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
break;
case REACTIVE:
contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
break;
default:
contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
}
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(
"Unable create a default ApplicationContext, please specify an ApplicationContextClass", ex);
}
}
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
public AnnotationConfigServletWebServerApplicationContext() {
//1:会去注入一些spring核心组件
this.reader = new AnnotatedBeanDefinitionReader(this);
this.scanner = new ClassPathBeanDefinitionScanner(this);
}
Web类型项目创建上下文对象AnnotationConfigServletWebServerApplicationContext,这里会把?ConfigurationClassPostProcessor 、
AutowiredAnnotationBeanPostProcessor 等一些核心组件加入到Spring容器,这其实已经是spring ioc容器的源码,如果了解spring ioc源码,那么对springboot源码的阅读会有很大的帮助;
private void refreshContext(ConfigurableApplicationContext context) {
//刷新和创建spring容器
refresh(context);
if (this.registerShutdownHook) {
try {
context.registerShutdownHook();
}
catch (AccessControlException ex) {
// Not allowed in some environments.
}
}
}
protected void refresh(ApplicationContext applicationContext) {
Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
//进入refresh()方法
((AbstractApplicationContext) applicationContext).refresh();
}
接下来进入到Spring ioc容器刷新和创建的12个步骤的源码中,AbstractApplicationContext - >refresh()方法,在这12个步骤中,这里主要看一下onRefresh() 这个方法,但是发现这个方法里面什么都没有,显然是一个钩子方法,它会钩到它子类重写onRefresh()方法,所以去看子类里面的onRefresh()方法;
由于我们这里是一个Web项目,所以就可以看到 ServletWebServerApplicationContext 这个类,这个类下面的 onRefresh() 方法:
protected void onRefresh() {
super.onRefresh();
try {
//看到了内嵌web容器的影子
createWebServer();
}
catch (Throwable ex) {
throw new ApplicationContextException("Unable to start web server", ex);
}
}
private void createWebServer() {
WebServer webServer = this.webServer;
ServletContext servletContext = getServletContext();
if (webServer == null && servletContext == null) {
//1、获取webServerFactory
ServletWebServerFactory factory = getWebServerFactory();
this.webServer = factory.getWebServer(getSelfInitializer());
}
else if (servletContext != null) {
try {
getSelfInitializer().onStartup(servletContext);
}
catch (ServletException ex) {
throw new ApplicationContextException("Cannot initialize servlet context",
ex);
}
}
initPropertySources();
}
//继续看下getWebServletFactory() 这个方法,这里面选择出哪种类型的web容器
protected ServletWebServerFactory getWebServerFactory() {
// Use bean names so that we don't consider the hierarchy
String[] beanNames = getBeanFactory()
.getBeanNamesForType(ServletWebServerFactory.class);
if (beanNames.length == 0) {
throw new ApplicationContextException(
"Unable to start ServletWebServerApplicationContext due to missing "
+ "ServletWebServerFactory bean.");
}
if (beanNames.length > 1) {
throw new ApplicationContextException(
"Unable to start ServletWebServerApplicationContext due to multiple "
+ "ServletWebServerFactory beans : "
+ StringUtils.arrayToCommaDelimitedString(beanNames));
}
return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);
}
// 再回头去看factory.getWebServer(getSelfInitializer()) ,转到定义就会看到很熟悉的名字tomcat
public WebServer getWebServer(ServletContextInitializer... initializers) {
//看到了我们熟悉的tomcat
Tomcat tomcat = new Tomcat();
File baseDir = (this.baseDirectory != null ? this.baseDirectory
: createTempDir("tomcat"));
tomcat.setBaseDir(baseDir.getAbsolutePath());
Connector connector = new Connector(this.protocol);
tomcat.getService().addConnector(connector);
customizeConnector(connector);
tomcat.setConnector(connector);
tomcat.getHost().setAutoDeploy(false);
configureEngine(tomcat.getEngine());
for (Connector additionalConnector : this.additionalTomcatConnectors) {
tomcat.getService().addConnector(additionalConnector);
}
prepareContext(tomcat.getHost(), initializers);
return getTomcatWebServer(tomcat);
}
内置的Servlet容器就是在onRefresh() 方法里面启动的,至此一个Servlet容器就启动OK了;