手写SpringCloud项目地址,深入理解微服务核心原理.
github:https://github.com/huangjianguo2000/spring-cloud-lightweight
gitee:https://gitee.com/huangjianguo2000/spring-cloud-lightweigh
序目
SpringBoot启动流程就是创建IOC容器的过程。
版本不一样,代码实现也可能不一样,本文参考的2.2.6和2.7.14
我们运行main方法, 会调用SpringApplication的run方法, 是一个静态方法。
@SpringBootApplication只是标注当前类为配置类,这里run方法参数不是只能传启动类,只要传的是一个配置类就行, 该配置类就可以理解成定义了SpringBean的扫描文件。
@SpringBootApplication
public class DemoApplication {public static void main(String[] args) {SpringApplication.run(DemoApplication.class, args);}
}
run方法最终调用分为两步, 实例化SpringApplication对象和调用该对象的run方法。
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {return (new SpringApplication(primarySources)).run(args);
}
所以SpringBoot启动流程分为两大步,1.实例化SpringApplication对象。2. 调用该对象的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();}
1. 判断容器类型
这里主要是做属性赋值,和判断当前项目容器的类型的类型。就是一些初始化操作
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {this.XXXX = XXXX;.......this.webApplicationType = WebApplicationType.deduceFromClasspath(); // 判断容器类型// ......
}
这里注意一点: 我们使用的starter-web用的是servlet模式。 容器类型是SERVLET
public enum WebApplicationType {NONE,SERVLET, // 传统的基于Servlet的编程模型REACTIVE; // 响应式编程模型
}
2. 去spring.factories获取监听器。
这里获取的是实现了ApplicationContextInitializer
或者ApplicationListener
接口的监听器。
二: run方法
public ConfigurableApplicationContext run(String... args) {listeners.starting(); // 发布开始事件try {// 封装参数 如--server.port = 8081ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);// 准备环境ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);configureIgnoreBeanInfo(environment);// 打印bannerBanner printedBanner = printBanner(environment);// 创建容器context = createApplicationContext();exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,new Class[] { ConfigurableApplicationContext.class }, context);//预处理容器prepareContext(context, environment, listeners, applicationArguments, printedBanner);// 调用Spring容器的refresh方法。refreshContext(context);afterRefresh(context, applicationArguments);stopWatch.stop();if (this.logStartupInfo) {new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);}// 发布启动完成事件listeners.started(context);callRunners(context, applicationArguments);}catch (Throwable ex) {handleRunFailure(context, ex, exceptionReporters, listeners);throw new IllegalStateException(ex);}try {// 发布正在运行事件listeners.running(context);}catch (Throwable ex) {handleRunFailure(context, ex, exceptionReporters, null);throw new IllegalStateException(ex);}return context;}
1: 发布starting事件
获取SpringApplicationRunListeners, 发布starting事件。默认情况下获取到的是EventPublishingRunListener,他会发布ApplicationStartingEvent事件。我们可以去消费这条事件。
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting(bootstrapContext, this.mainApplicationClass);
消费事件示例:
public class ApplicationStartingEventListener implements ApplicationListener<ApplicationStartingEvent> {@Overridepublic void onApplicationEvent(ApplicationStartingEvent event) {System.out.println("收到了发布事件");}
}需要在spring.factories文件中注册监听器
org.springframework.context.ApplicationListener=\
com.example.demo.test.TestL
2: 获取环境变量
这里会获取到JVM的参数,操作系统的环境变量等,
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);//1
ConfigurableEnvironment environment =
this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);//2
获取环境变量这个方法里面会调用listeners.environmentPrepared(bootstrapContext, environment);
来发布ApplicationEnvironmentPreparedEvent
事件。同样我们可以对这个事件进行消费。
SpringBoot会在EnvironmentPostProcessorApplicationListener中来处理这个事件,他定义了很多个处理该事件的postprocessor来处理配置, 向environment对象中添加属性。
private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {ConfigurableEnvironment environment = event.getEnvironment();SpringApplication application = event.getSpringApplication();for (EnvironmentPostProcessor postProcessor : getEnvironmentPostProcessors(application.getResourceLoader(),event.getBootstrapContext())) {postProcessor.postProcessEnvironment(environment, application);}
}
环境变量步骤完后会打印banner。Banner printedBanner = printBanner(environment);
3:创建Spring容器。
context = createApplicationContext();
这里会根据实例化SpringBootApplication对象时候获取到的webApplicationType类型来创建容器。我们平时用的阻塞式编程创建的就是AnnotationConfigServletWebServerApplicationContext
。
我使用的2.2.6代码如下,2.7.X写法变了, 但是效果都是一样的
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);
}
4: 预处理容器
就是对刚才创建的容器对象进行赋值,加载配置类等操作。
prepareContext(context, environment, listeners, applicationArguments, printedBanner){context.setEnvironment(environment);// 1. 环境对象赋值postProcessApplicationContext(context);// 2. 处理器 设置 Bean 名称生成器、设置资源加载器以及配置类型转换服务applyInitializers(context);// 3. 利用ApplicationContextInitializer进行一些初始化操作。比如添加Bean工厂后置处理器等listeners.contextPrepared(context);// 4. 发布事件....if (this.lazyInitialization) { // 5. 如果Bean容器是设置了懒加载的,就添加懒加载的处理器context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());// 懒加载的实现很简单, 就是给所有的bean定义设置懒加载属性为true// beanDefinition.setLazyInit(true);}.....load(context, sources.toArray(new Object[0]));//6. 把我们传入的配置类加载到容器里,正常情况下就是启动类isteners.contextLoaded(context);// 7. 发布加载完成事件}
5:刷新容器
refreshContext(context);
, 这个就是我们熟知的Spring的核心方法,这不用细说了。Tomcat就是在这个阶段创建的,具体是onRefresh方法调用TomcatServletWebServerFactory的getWebServer方法进行创建。其实就是Tomcat tomcat = new Tomcat();
6:发布启动完成事件
调用listeners.started(context);
发布事件ApplicationStartedEvent
7:发布SpringBoot正在运行事件
listeners.running(context);
8: 结束
return 返回我们创建好的容器。