一直很好奇SpringBoot這么一個大怪物,啟動的時候做了哪些事情,然后看了很多老師講的教學視頻,然后自己想好好整理一下,做下學習筆記下次也方便自己閱讀
1.執行main方法
public static void main(String[] args) { //代碼很簡單SpringApplication.run(); SpringApplication.run(ConsumerApp.class, args);
public static ConfigurableApplicationContext run(Class<?> primarySource, return run(new Class<?>[] { primarySource }, args); //1、new了一個SpringApplication() 這么一個對象 //2、執行new出來的SpringApplication()對象的run()方法 public static ConfigurableApplicationContext run(Class<?>[] primarySources, return new SpringApplication(primarySources).run(args);
上面代碼主要做了兩件事情。第一步new了一個SpringApplication對象 ,第二部調用了run()方法。接下來我們一起看下new 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)); 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)); this.mainApplicationClass = deduceMainApplicationClass();
利用SPI機制掃描 META-INF/spring.factories 這個文件,并且加載 ApplicationContextInitializer、ApplicationListener 接口實例。
1、ApplicationContextInitializer 這個類當springboot上下文Context初始化完成后會調用
2、ApplicationListener 當springboot啟動時事件change后都會觸發
下面我們來自定義ApplicationContextInitializer、ApplicationListener 接口實現類,然后Debug來看下效果。
public class StarterApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> { public void initialize(ConfigurableApplicationContext applicationContext) { System.out.println("applicationContext 初始化完成 ... ");
public class StarterApplicationListener implements ApplicationListener { public void onApplicationEvent(ApplicationEvent event) { System.out.println(event.toString()); System.out.println("ApplicationListener .... " + System.currentTimeMillis());
我們需要把這兩個類集成到springboot里面去,其實操作也挺簡單的

然后在META-INF/spring.factories 文件配置那兩個類
org.springframework.context.ApplicationContextInitializer=\ org.admin.starter.test.listener.StarterApplicationContextInitializer org.springframework.context.ApplicationListener=\ org.admin.starter.test.listener.StarterApplicationListener
4、我們代碼DEBUG一下,在loadSpringFactories() 方法打一個斷點
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) { MultiValueMap<String, String> result = cache.get(classLoader); Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) : ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION)); result = new LinkedMultiValueMap<>(); while (urls.hasMoreElements()) { URL url = urls.nextElement(); UrlResource resource = new UrlResource(url); Properties properties = PropertiesLoaderUtils.loadProperties(resource); for (Map.Entry<?, ?> entry : properties.entrySet()) { List<String> factoryClassNames = Arrays.asList( StringUtils.commaDelimitedListToStringArray((String) entry.getValue())); result.addAll((String) entry.getKey(), factoryClassNames); cache.put(classLoader, result); throw new IllegalArgumentException("Unable to load factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex);

總結:上面就是SpringApplication初始化的代碼,new SpringApplication()沒做啥事情 ,利用SPI機制主要加載了META-INF/spring.factories 下面定義的事件監聽器接口實現類
2.執行run() 方法
public ConfigurableApplicationContext run(String... args) { StopWatch stopWatch = new StopWatch(); ConfigurableApplicationContext context = null; Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>(); <!--2、這個也不是重點,就是設置了一些環境變量--> configureHeadlessProperty(); <!--3、獲取事件監聽器SpringApplicationRunListener類型,并且執行starting()方法--> SpringApplicationRunListeners listeners = getRunListeners(args); <!--4、把參數args封裝成DefaultApplicationArguments,這個了解一下就知道--> ApplicationArguments applicationArguments = new DefaultApplicationArguments( <!--5、這個很重要準備環境了,并且把環境跟spring上下文綁定好,并且執行environmentPrepared()方法--> ConfigurableEnvironment environment = prepareEnvironment(listeners, <!--6、判斷一些環境的值,并設置一些環境的值--> configureIgnoreBeanInfo(environment); Banner printedBanner = printBanner(environment); <!--8、創建上下文,根據項目類型創建上下文--> context = createApplicationContext(); exceptionReporters = getSpringFactoriesInstances( SpringBootExceptionReporter.class, new Class[] { ConfigurableApplicationContext.class }, context); <!--10、準備上下文,執行完成后調用contextPrepared()方法,contextLoaded()方法--> prepareContext(context, environment, listeners, applicationArguments, <!--11、這個是spring啟動的代碼了,這里就回去里面就回去掃描并且初始化單實列bean了--> //這個refreshContext()加載了bean,還啟動了內置web容器,需要細細的去看看 afterRefresh(context, applicationArguments); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass) .logStarted(getApplicationLog(), stopWatch); <!--13、執行ApplicationRunListeners中的started()方法--> listeners.started(context); <!--執行Runner(ApplicationRunner和CommandLineRunner)--> callRunners(context, applicationArguments); handleRunFailure(context, listeners, exceptionReporters, ex); throw new IllegalStateException(ex); listeners.running(context);
2.1 createApplicationContext()
一起來看下context = createApplicationContext(); 這段代碼,這段代碼主要是根據項目類型創建上下文,并且會注入幾個核心組件類。
protected ConfigurableApplicationContext createApplicationContext() { Class<?> contextClass = this.applicationContextClass; if (contextClass == null) { switch (this.webApplicationType) { contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS); contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS); 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(DefaultListableBeanFactory beanFactory) { this.reader = new AnnotatedBeanDefinitionReader(this); this.scanner = new ClassPathBeanDefinitionScanner(this);
Web類型項目創建上下文對象 AnnotationConfigServletWebServerApplicationContext 。這里會把 ConfigurationClassPostProcessor 、AutowiredAnnotationBeanPostProcessor 等一些核心組件加入到Spring容器(扯淡有點遠,這是spring容器啟動的一些知識點,去了解一下對理解springboot有很大的幫助)
2.2 refreshContext()
下面一起來看下refreshContext(context) 這個方法,這個方法啟動spring的代碼加載了bean,還啟動了內置web容器
private void refreshContext(ConfigurableApplicationContext context) { if (this.registerShutdownHook) { context.registerShutdownHook(); catch (AccessControlException ex) { // Not allowed in some environments.
protected void refresh(ApplicationContext applicationContext) { Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext); ((AbstractApplicationContext) applicationContext).refresh();
轉到AbstractApplicationContext - >refresh()方法里面發現這是spring容器啟動代碼
public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. // Tell the subclass to refresh the internal bean factory. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. prepareBeanFactory(beanFactory); // Allows post-processing of the bean factory in context subclasses. postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context. invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation. registerBeanPostProcessors(beanFactory); // Initialize message source for this context. // Initialize event multicaster for this context. initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. // Check for listener beans and register them. // Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); // Destroy already created singletons to avoid dangling resources. // Propagate exception to caller. // Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore...
spring容器啟動代碼就不說了,這里主要看一下onRefresh() 這個方法。轉到定義發現這個方法里面啥都沒有,這明顯是一個鉤子方法,它會鉤到它子類重寫onRefresh()方法。所以去看子類里面的onRefresh()
protected void onRefresh() throws BeansException { //這是一個空方法,AbstractApplicationContext 這個類是一個抽象類, //所以我們要找到集成AbstractApplicationContext的子類,去看子類里面的onRefresh() // For subclasses: do nothing by default.
我們這里是一個Web項目,所以我們就去看 ServletWebServerApplicationContext 這個類 ,我還是把類的關系圖貼一下

我們就去看 ServletWebServerApplicationContext 這個類下面的 onRefresh() 方法
protected void onRefresh() { 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) { getSelfInitializer().onStartup(servletContext); catch (ServletException ex) { throw new ApplicationContextException("Cannot initialize servlet context",
我們繼續看下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 = 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了。
總結:
1、new了一個SpringApplication對象,使用SPI技術加載加載 ApplicationContextInitializer、ApplicationListener 接口實例
2、調用SpringApplication.run() 方法
3、調用createApplicationContext()方法創建上下文對象,創建上下文對象同時會注冊spring的核心組件類(ConfigurationClassPostProcessor 、AutowiredAnnotationBeanPostProcessor 等)。
4、調用refreshContext() 方法啟動Spring容器和內置的Servlet容器
本人也是一個剛入門的小白,也是看了很多大神寫的這類文章,按照自己思路整理一下,就是為了加深下印象。但也希望對大家有幫助
|