Springboot Analysis (2) - Container Startup Process

Probably my level is limited. Recently, I feel that it takes a lot of time to read code and related books. It is difficult to understand everything in depth, so I can only learn springboot with problems. I will learn more about it when I encounter exact problems in the future. , set a goal for yourself, and temporarily only figure out the general process of container startup, understand component scanning, automatic configuration, and solve the problems of circular dependencies. The Main method of general startup is SpringApplication.run(启动类.class, args);, if you follow it, you will find that the call is new SpringApplication (startup class).run(args) Because the container refresh content is the most critical and complex, let's first understand the process other than container refresh.

(1) Initialization of SpringApplication

1. Code

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { 
  this.resourceLoader = resourceLoader;
  Assert.notNull(primarySources, "PrimarySources must not be null"); 
  //通常情况下primarySources就是启动类,暂时理解这里就是将启动类设置为主配置资源来源
 this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); 
 //通过类路径中寻找相关类,判断当前环境是NONE(标准环境(classPath下没有javax.servlet.Servlet以及org.springframework.web.context.ConfigurableWebApplicationContext)、SERVLET(Servlet环境)、REACTIVE(响应式)
 this.webApplicationType = WebApplicationType.deduceFromClasspath();
//添加initializers,设置初始化器,这些初始化器将在在容器刷新前回调,原理是通过SpringFactoriesLoader的loadFactoryNames方法在 spring.factories文件中找到的ApplicationContextInitializer接口的配置的实现类的全限定类名,并实例化。
  setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
 //同上,设置ApplicationListener,添加 spring.factories文件中ApplicationListener配置的响应实现类。
  setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); 
  //通过构造一个运行时异常,然后去栈帧中寻找方法名为main的方法来得到入口类的名字并设置为mainApplicationClass
 this.mainApplicationClass = deduceMainApplicationClass(); 
}

2. Note

(1) What are ApplicationContextInitializers?

Debug found these:

Their role is to:

  • ConfigurationWarningsApplicationContextInitializer: Reports some common misconfigurations of the IOC container
  • ContextIdApplicationContextInitializer: Set the ID of the Spring application context
  • DelegatingApplicationContextInitializer: Loads the classes configured in context.initializer.classes in application.properties
  • ServerPortInfoApplicationContextInitializer: Writes the listening port actually used by the built-in servlet container into the Environment property
  • SharedMetadataReaderFactoryContextInitializer: Create a CachingMetadataReaderFactory object shared by SpringBoot and ConfigurationClassPostProcessor
  • ConditionEvaluationReportLoggingListener: writes the ConditionEvaluationReport to the log

(2) What are ApplicationListeners?

Debug found these:

Their role is to:

  • ClearCachesApplicationListener: Clear the cache after the application context is loaded
  • ParentContextCloserApplicationListener: listens for the closing event of the parent application context and propagates it to its own child application context
  • FileEncodingApplicationListener: Detects whether the system file encoding is consistent with the application environment encoding, and terminates the application startup if the system file encoding and application environment encoding are different
  • AnsiOutputApplicationListener: Configure AnsiOutput according to the spring.output.ansi.enabled parameter
  • ConfigFileApplicationListener: Read configuration files from common those agreed-upon locations
  • DelegatingApplicationListener: After listening to the event, it is forwarded to the listener of context.listener.classes configured in application.properties
  • ClasspathLoggingApplicationListener: Responds to the environment ready event ApplicationEnvironmentPreparedEvent and the application failure event ApplicationFailedEvent
  • LoggingApplicationListener: Configure LoggingSystem. Use the configuration specified by the logging.config environment variable or the default configuration
  • LiquibaseServiceLocatorApplicationListener: Replaces LiquibaseServiceLocator with a version that works with the SpringBoot executable jar
  • BackgroundPreinitializer: Use a background thread to trigger some time-consuming initialization tasks as early as possible

(3) What is REACTIVE

REACTIVE is a responsive programming thing, which refers to the application environment under the WebFlux framework. It is NIO synchronous non-blocking IO. It may replace the current MVC in the future. Because it is a relatively new technology, the application scenarios are relatively limited, so I will not do in-depth understanding for the time being .

(2) Operations before the container is refreshed

1. Code

public ConfigurableApplicationContext run(String... args) {  
//这个组件是用来监控启动时间的,不是很重要
StopWatch stopWatch = new StopWatch();  
stopWatch.start();  
ConfigurableApplicationContext context = null;  
//SpringBootExceptionReporter这个东西是一个异常解析器,实现类只有一个是FailureAnalyzers,
//用于打印异常信息,这个集合在下面③处会初始化,集合里面装了针对各式各样的解析器,
//在catch到异常后,会遍历这个集合,寻找合适的解析器,然后打印异常日志
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>(); 
//刷新系统属性java.awt.headless的值,如果没有值则设为true,这个值表示无头模式(意指缺少显示设备,键盘或鼠标的系统配置),
//在无头模式下java.awt.Toolkit将使用特定的无头模式下的实现类,因为就算没有显示设备,有些操作任能够被允许。
configureHeadlessProperty();  
//①
SpringApplicationRunListeners listeners = getRunListeners(args);  
listeners.starting();  
try {  
//②
  ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);  
  ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
  //配置系统参数spring.beaninfo.ignore,默认值为ture,字面意思是跳过搜索BeanInfo类,但具体是什么我暂时也不清楚。
  configureIgnoreBeanInfo(environment);
  //③
  Banner printedBanner = printBanner(environment);  
  //④
  context = createApplicationContext();  
  //⑤
  exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,  
 new Class[]{ConfigurableApplicationContext.class}, context);  
 //⑥
  prepareContext(context, environment, listeners, applicationArguments, printedBanner);  
  refreshContext(context);  
  //刷新后的处理,是个空实现
  afterRefresh(context, applicationArguments); 
  //计时器结束
  stopWatch.stop();  
 if (this.logStartupInfo) {  
  new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);  
  }  
  //发布started事件
  listeners.started(context);  
  //运行器回调,即实现了ApplicationRunner接口或CommandLineRunner接口的bean
  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;
}

2. Code comments

Internally, the getSpringFactoriesInstances method is adjusted, and all SpringApplicationRunListeners in spring.factories are taken, and then SpringApplicationRunListeners are exposed externally. SpringApplicationRunListeners encapsulate all SpringApplicationRunListeners for publishing events between container startups to all SpringApplicationRunListeners.

The methods defined in SpringApplicationRunListener are:

  • void starting(); Called immediately when the run method is first started. Can be used for very early initialization.
  • void environmentPrepared(ConfigurableEnvironment environment); Prepares the environment (Environment build is complete), but called before the ApplicationContext is created.
  • void contextPrepared(ConfigurableApplicationContext context); Called after the ApplicationContext is created and built, but before it is loaded.
  • void contextLoaded(ConfigurableApplicationContext context); Called when ApplicationContext is loaded but before refresh.
  • void started(ConfigurableApplicationContext context); ApplicationContext has been refreshed, application has started, but CommandLineRunners and ApplicationRunners have not been called
  • void running(ConfigurableApplicationContext context); Called immediately before the run method is completely complete, refreshes the ApplicationContext and calls all CommandLineRunners and ApplicationRunners.
  • void failed(ConfigurableApplicationContext context, Throwable exception); Called when running the application fails.

It is worth noting that the started, running, and failed methods were added in Spring Boot 2.0.

Through Debug, it is found that there is one listeners loaded by default, and the type is EventPublishingRunListener. It publishes different application event types (ApplicationEvent) at different time points when the SpringBoot application starts. If any event listeners (ApplicationListener) are interested in these events, they can receive and process them. The difference between SpringApplicationRunListener and ApplicationListener is that SpringApplicationRunListener is more advanced than ApplicationListener. SpringApplicationRunListener monitors the execution of SpringApplication related methods. It belongs to the first layer of listeners. It will publish corresponding events to ApplicationListener.

The initialization of the Environment is completed according to different webApplicationTypes. Generally, the StandardServletEnvironment implementation class is used. The Environment is used to describe the current running environment of the application, which abstracts two aspects: profile and properties, which are actually corresponding The contents of configuration files, environment variables, and command-line parameters. Here, the environmentPrepared event is released when the Environment is built, and the latest configuration value is bound to the SpringbootApplication, which is the current object. For example, some attribute values ​​at the beginning of spring.main in yml.

If the corresponding banners are obtained according to the configuration in the Environment, the default SpringbootBanner is used to print the startup information, which is the logo printed on the console when the application is started.

According to WebApplicationType, reflection creates different ApplicationContext implementations (Servlet is AnnotationConfigServletWebServerApplicationContext). The servlet here is AnnotationConfigServletWebServerApplicationContext. In its parent class GenericApplicationContext construction method, a DefaultListableBeanFactory is injected. This BeanFactory is very critical. In fact, the BeanFactory capability of AnnotationConfigServletWebServerApplicationContext is extended from DefaultListableBeanFactory. In addition, beanDefinitions such as ConfigurationClassPostProcessor, DefaultEventListenerFactory, EventListenerMethodProcessor, AutowiredAnnotationBeanPostProcessor, and CommonAnnotationBeanPostProcessor are also registered in this step as basic components. The ConfigurationClassPostProcessor is the most important component, and the other ones have not been studied in depth for the time being. ConfigurationClassPostProcessor is a BeanFactoryPostProcessor, which is responsible for loading and scanning configuration class annotations for component analysis and registering BeanDefinition when the container is refreshed.

Create a series of SpringBootExceptionReporter. The creation process is to obtain all classes that implement the SpringBootExceptionReporter interface through SpringFactoriesLoader.

Initialize ApplicationContext, mainly to complete the following work:

  • Set the prepared Environment to the ApplicationContext
  • Further post-processing of ApplicationContext, including registering BeanName generator, setting resource loader and class loader, setting type converter ConversionService, etc., do not need to delve into the things here for the time being.
  • Traverse and call the initialize() method of all ApplicationContextInitializers to further process the created ApplicationContext.
  • Call the contextPrepared() method of SpringApplicationRunListener to notify all listeners that the ApplicationContext is ready.
  • The beanDefiniton that creates the startup class is registered with the container.
  • Call the contextLoaded() method of SpringApplicationRunListener to notify all listeners that the ApplicationContext has been loaded.

summary

Before the container is refreshed, the whole process is divided into three steps:

  1. Initialize the SpringApplication object, such as setting webApplicationType, loading ApplicationListener, ApplicationContextInitializer.
  2. Initialize the Environment object, encapsulate the configuration file, and command line parameters.
  3. Initialize ConfigurableApplicationContext (the difference from ApplicationContext is that ConfigurableApplicationContext can write to the container, while ApplicationContext only provides read methods), and register the beanDefiniton of the startup class with the container first. This is what the main thread can see, and what the other applicationContextInitializer and ApplicationListener did not see has not been deeply studied.
{{o.name}}
{{m.name}}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324124805&siteId=291194637