轉載請務必註明原創地址爲:http://www.54tianzhisheng.cn/2018/04/30/springboot_SpringApplication/java
在 Spring Boot 項目的啓動類中常見代碼以下:react
@SpringBootApplication public class SpringbotApplication { public static void main(String[] args) { SpringApplication.run(SpringbotApplication.class, args); } }
其中也就兩個比較引人注意的地方:web
@SpringBootApplication
SpringApplication.run()
對於第一個註解 @SpringBootApplication
,我已經在博客 Spring Boot 2.0系列文章(六):Spring Boot 2.0中SpringBootApplication註解詳解 中詳細的講解了。接下來就是深刻探究第二個了 SpringApplication.run()
。spring
上面的姿式太簡單了,只一行代碼就完事了。編程
SpringApplication.run(SpringbotApplication.class, args);
實際上是支持作一些個性化的設置,接下來咱們換個姿式瞧瞧:bootstrap
@SpringBootApplication public class SpringbotApplication { public static void main(String[] args) { SpringApplication app = new SpringApplication(SpringbotApplication.class); // 自定義應用程序的配置 //app.setXxx() app.run(args) } }
沒錯,就是經過一個構造函數,而後設置相關的屬性,從而達到定製化服務。有哪些屬性呢?springboot
屬性對應的 get/set 方法app
看到沒,還不少呢!less
舉個例子:你想把 Spring Boot 項目的默認 Banner 換成你本身的,就須要在這裏以下:webapp
public static void main(String[] args) { // SpringApplication.run(Springboot2Application.class, args); SpringApplication application = new SpringApplication(Springboot2Application.class); application.setBanner((environment, sourceClass, out) -> { //這裏打印一個logo System.out.println(" _ _ _\n" + " | | (_) | |\n" + " ____| |__ _ ___ | |__ ___ _ __ __ _\n" + "|_ /| '_ \\ | |/ __|| '_ \\ / _ \\| '_ \\ / _` |\n" + " / / | | | || |\\__ \\| | | || __/| | | || (_| |\n" + "/___||_| |_||_||___/|_| |_| \\___||_| |_| \\__, |\n" + " __/ |\n" + " |___/\n"); }); application.setBannerMode(Banner.Mode.CONSOLE); //你還能夠幹其餘的定製化初始設置 application.run(args); }
如今重啓項目,你就會發現,控制檯的 logo 已經換成你本身的了。
固然了,你可能會以爲這樣寫有點複雜,嗯嗯,確實,這樣硬編碼在代碼裏確實不太友好。你還能夠在src/main/resources
路徑下新建一個banner.txt
文件,banner.txt
中填寫好須要打印的字符串內容便可。
從該類中能夠看到在 Spring Boot 2 中引入了個新的 WebApplicationType 和 WebEnvironment。
確實,這也是 Spring Boot 2 中比較大的特性,它是支持響應式編程的。我以前在文章 Spring Boot 2.0系列文章(二):Spring Boot 2.0 新特性詳解 中也介紹過,之後有機會會介紹它的,這裏我先賣個關子。
SpringApplication.run()
的實現纔是咱們要深刻探究的主角,該方法代碼以下:
//靜態方法,可用於使用默認配置運行 SpringApplication public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) { return run(new Class<?>[] { primarySource }, args); } public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) { return new SpringApplication(primarySources).run(args); }
在這個靜態方法中,建立 SpringApplication 對象,並調用該對象的 run 方法。
public SpringApplication(Class<?>... primarySources) { this(null, primarySources); } //建立一個 SpringApplication 實例,應用上下文會根據指定的主要資源加載 beans ,實例在調用 run 方法以前能夠定製化 @SuppressWarnings({ "unchecked", "rawtypes" }) 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 = deduceWebApplicationType(); setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer.class)); setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); this.mainApplicationClass = deduceMainApplicationClass(); }
首先是進入單個參數的構造方法,而後進入兩參數的構造方法(ResourceLoader 爲 null),而後進行初始化。
一、deduceWebApplicationType() : 推斷應用的類型 ,建立的是一個 SERVLET 應用仍是 REACTIVE應用或者是 NONE
private static final String REACTIVE_WEB_ENVIRONMENT_CLASS = "org.springframework.web.reactive.DispatcherHandler"; private static final String MVC_WEB_ENVIRONMENT_CLASS = "org.springframework.web.servlet.DispatcherServlet"; private static final String[] WEB_ENVIRONMENT_CLASSES = { "javax.servlet.Servlet", "org.springframework.web.context.ConfigurableWebApplicationContext" }; private WebApplicationType deduceWebApplicationType() { if (ClassUtils.isPresent(REACTIVE_WEB_ENVIRONMENT_CLASS, null) && !ClassUtils.isPresent(MVC_WEB_ENVIRONMENT_CLASS, null)) { return WebApplicationType.REACTIVE; //該程序是 REACTIVE 程序 } for (String className : WEB_ENVIRONMENT_CLASSES) { if (!ClassUtils.isPresent(className, null)) { return WebApplicationType.NONE; //該程序爲 NONE } } return WebApplicationType.SERVLET; //默認返回是 SERVLET 程序 }
二、setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)):初始化 classpath 下的全部的可用的 ApplicationContextInitializer。
1)、getSpringFactoriesInstances()
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) { return getSpringFactoriesInstances(type, new Class<?>[] {}); } //獲取全部的 Spring 工廠實例 private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) { ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); // Use names and ensure unique to protect against duplicates Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader)); //獲取全部 Spring Factories 的名字 List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); AnnotationAwareOrderComparator.sort(instances); //Spring 工廠實例排序 return instances; } //根據讀取到的名字建立對象(Spring 工廠實例) private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args, Set<String> names) { List<T> instances = new ArrayList<>(names.size()); for (String name : names) { try { Class<?> instanceClass = ClassUtils.forName(name, classLoader); Assert.isAssignable(type, instanceClass); Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes); T instance = (T) BeanUtils.instantiateClass(constructor, args); instances.add(instance); } catch (Throwable ex) { throw new IllegalArgumentException( "Cannot instantiate " + type + " : " + name, ex); } } return instances; }
上面的 SpringFactoriesLoader.loadFactoryNames() ,是從 META-INF/spring.factories 的資源文件中,讀取 key 爲org.springframework.context.ApplicationContextInitializer 的 value。
而 spring.factories 的部份內容以下:
能夠看到,最近的獲得的,是 ConfigurationWarningsApplicationContextInitializer,ContextIdApplicationContextInitializer,DelegatingApplicationContextInitializer,ServerPortInfoApplicationContextInitializer 這四個類的名字。
2)、setInitializers():
public void setInitializers( Collection<? extends ApplicationContextInitializer<?>> initializers) { this.initializers = new ArrayList<>(); this.initializers.addAll(initializers); }
因此,這裏 setInitializers() 所獲得的成員變量 initializers 就被初始化爲ConfigurationWarningsApplicationContextInitializer,ContextIdApplicationContextInitializer,DelegatingApplicationContextInitializer,ServerPortInfoApplicationContextInitializer 這四個類的對象組成的 list。
三、setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)):初始化 classpath 下的全部的可用的 ApplicationListener。
1)、getSpringFactoriesInstances() 和上面的相似,可是它是從 META-INF/spring.factories 的資源文件中,獲取到 key 爲 org.springframework.context.ApplicationListener 的 value。
2)、setListeners():
public void setListeners(Collection<? extends ApplicationListener<?>> listeners) { this.listeners = new ArrayList<>(); this.listeners.addAll(listeners); }
因此,這裏 setListeners() 所獲得的成員變量 listeners 就被初始化爲 ClearCachesApplicationListener,ParentContextCloserApplicationListener,FileEncodingApplicationListener,AnsiOutputApplicationListener ,ConfigFileApplicationListener,DelegatingApplicationListener,ClasspathLoggingApplicationListener,LoggingApplicationListener,LiquibaseServiceLocatorApplicationListener 這九個類的對象組成的 list。
四、deduceMainApplicationClass() :根據調用棧,推斷出 main 方法的類名
private Class<?> deduceMainApplicationClass() { try { StackTraceElement[] stackTrace = new RuntimeException().getStackTrace(); for (StackTraceElement stackTraceElement : stackTrace) { if ("main".equals(stackTraceElement.getMethodName())) { return Class.forName(stackTraceElement.getClassName()); } } } catch (ClassNotFoundException ex) { // Swallow and continue } return null; }
上面看完了構造方法後,已經初始化了一個 SpringApplication 對象,接下來調用其 run 方法,代碼以下:
//運行 Spring 應用程序,建立並刷新一個新的 ApplicationContext public ConfigurableApplicationContext run(String... args) { StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>(); configureHeadlessProperty(); SpringApplicationRunListeners listeners = getRunListeners(args); listeners.starting(); try { ApplicationArguments applicationArguments = new DefaultApplicationArguments( args); ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); 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); } 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; }
可變個數參數 args 便是咱們整個應用程序的入口 main 方法的參數。StopWatch 是來自 org.springframework.util 的工具類,能夠用來方便的記錄程序的運行時間。
再來看看 1.5.12 與 2.0.1 版本的 run 方法 有什麼不同的地方?
接下來好好分析上面新版本(2.0.1)的 run 方法的代碼並配合比較舊版本(1.5.12)。
一、configureHeadlessProperty():設置 headless 模式
private static final String SYSTEM_PROPERTY_JAVA_AWT_HEADLESS = "java.awt.headless"; private boolean headless = true; private void configureHeadlessProperty() { System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, System.getProperty( SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless))); }
其實是就是設置系統屬性 java.awt.headless,該屬性會被設置爲 true。
二、getRunListeners():加載 SpringApplicationRunListener 對象
//TODO: xxx SpringApplicationRunListeners listeners = getRunListeners(args);//初始化監聽器 listeners.starting(); try { prepareContext(context, environment, listeners, applicationArguments, printedBanner); refreshContext(context); afterRefresh(context, applicationArguments); listeners.started(context); callRunners(context, applicationArguments); } try { listeners.running(context); } private SpringApplicationRunListeners getRunListeners(String[] args) { Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class }; return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances( SpringApplicationRunListener.class, types, this, args)); }
三、new DefaultApplicationArguments(args) :獲取啓動時傳入參數 args(main 方法傳進來的參數) 並初始化爲 ApplicationArguments 對象。
public DefaultApplicationArguments(String[] args) { Assert.notNull(args, "Args must not be null"); this.source = new Source(args); this.args = args; }
四、prepareEnvironment(listeners, applicationArguments):根據 listeners 和 applicationArguments 配置SpringBoot 應用的環境。
private ConfigurableEnvironment prepareEnvironment( SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) { // Create and configure the environment ConfigurableEnvironment environment = getOrCreateEnvironment(); configureEnvironment(environment, applicationArguments.getSourceArgs()); listeners.environmentPrepared(environment); bindToSpringApplication(environment); if (this.webApplicationType == WebApplicationType.NONE) { environment = new EnvironmentConverter(getClassLoader()) .convertToStandardEnvironmentIfNecessary(environment); } ConfigurationPropertySources.attach(environment); return environment; } //若是 environment 不爲空,直接 get 到,不然建立 private ConfigurableEnvironment getOrCreateEnvironment() { if (this.environment != null) { return this.environment; } if (this.webApplicationType == WebApplicationType.SERVLET) { return new StandardServletEnvironment(); } return new StandardEnvironment(); } //配置環境 protected void configureEnvironment(ConfigurableEnvironment environment,String[] args) { configurePropertySources(environment, args);//配置要使用的PropertySources configureProfiles(environment, args);//配置要使用的Profiles } //將環境綁定到 SpringApplication protected void bindToSpringApplication(ConfigurableEnvironment environment) { try { Binder.get(environment).bind("spring.main", Bindable.ofInstance(this)); } catch (Exception ex) { throw new IllegalStateException("Cannot bind to SpringApplication", ex); } }
五、configureIgnoreBeanInfo(environment):根據環境信息配置要忽略的 bean 信息
public static final String IGNORE_BEANINFO_PROPERTY_NAME = "spring.beaninfo.ignore"; private void configureIgnoreBeanInfo(ConfigurableEnvironment environment) { if (System.getProperty( CachedIntrospectionResults.IGNORE_BEANINFO_PROPERTY_NAME) == null) { Boolean ignore = environment.getProperty("spring.beaninfo.ignore", Boolean.class, Boolean.TRUE); System.setProperty(CachedIntrospectionResults.IGNORE_BEANINFO_PROPERTY_NAME, ignore.toString()); } }
六、printBanner(environment):打印標誌,上面我已經說過了。
private Banner printBanner(ConfigurableEnvironment environment) { if (this.bannerMode == Banner.Mode.OFF) { //若是設置爲 off,不打印 Banner return null; } ResourceLoader resourceLoader = this.resourceLoader != null ? this.resourceLoader : new DefaultResourceLoader(getClassLoader()); SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter( resourceLoader, this.banner); if (this.bannerMode == Mode.LOG) { return bannerPrinter.print(environment, this.mainApplicationClass, logger); } return bannerPrinter.print(environment, this.mainApplicationClass, System.out); }
七、createApplicationContext():根據應用類型來肯定該 Spring Boot 項目應該建立什麼類型的 ApplicationContext ,默認狀況下,若是沒有明確設置的應用程序上下文或應用程序上下文類,該方法會在返回合適的默認值。
public static final String DEFAULT_WEB_CONTEXT_CLASS = "org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext"; public static final String DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = "org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext"; public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context.annotation.AnnotationConfigApplicationContext"; protected ConfigurableApplicationContext createApplicationContext() { Class<?> contextClass = this.applicationContextClass; if (contextClass == null) { try { switch (this.webApplicationType) { //根據應用程序的類型來初始化容器 case SERVLET: //servlet 應用程序 contextClass = Class.forName(DEFAULT_WEB_CONTEXT_CLASS); break; case REACTIVE: //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); } } //最後經過Spring的工具類 BeanUtils 初始化容器類 bean return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass); }
來看看在 1.5.12 中是怎麼樣的?
八、exceptionReporters = getSpringFactoriesInstances( SpringBootExceptionReporter.class, new Class[] { ConfigurableApplicationContext.class }, context)
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) { ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); // Use names and ensure unique to protect against duplicates Set<String> names = new LinkedHashSet<>( SpringFactoriesLoader.loadFactoryNames(type, classLoader)); List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);//根據類型 key 爲 SpringBootExceptionReporter 去加載 AnnotationAwareOrderComparator.sort(instances);//對實例排序 return instances; }
這裏也是經過 SpringFactoriesLoader 加載 META-INF/spring.factories 中 key 爲 SpringBootExceptionReporter 的。
九、prepareContext(context, environment, listeners, applicationArguments, printedBanner):完成整個容器的建立與啓動以及 bean 的注入功能。
//裝配 Context private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) { //將以前準備好的 environment 設置給建立好的 ApplicationContext 使用 context.setEnvironment(environment); //一、 postProcessApplicationContext(context); //二、 applyInitializers(context); listeners.contextPrepared(context); if (this.logStartupInfo) {//啓動日誌 logStartupInfo(context.getParent() == null); logStartupProfileInfo(context); } // Add boot specific singleton beans context.getBeanFactory().registerSingleton("springApplicationArguments", applicationArguments); if (printedBanner != null) { context.getBeanFactory().registerSingleton("springBootBanner", printedBanner); } // Load the sources Set<Object> sources = getAllSources(); Assert.notEmpty(sources, "Sources must not be empty"); //三、 load(context, sources.toArray(new Object[0])); listeners.contextLoaded(context); }
1)、postProcessApplicationContext(context)
public static final String CONFIGURATION_BEAN_NAME_GENERATOR = "org.springframework.context.annotation.internalConfigurationBeanNameGenerator"; protected void postProcessApplicationContext(ConfigurableApplicationContext context) { if (this.beanNameGenerator != null) { context.getBeanFactory().registerSingleton( AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR, this.beanNameGenerator); } if (this.resourceLoader != null) { if (context instanceof GenericApplicationContext) { ((GenericApplicationContext) context) .setResourceLoader(this.resourceLoader); } if (context instanceof DefaultResourceLoader) { ((DefaultResourceLoader) context) .setClassLoader(this.resourceLoader.getClassLoader()); } } }
該方法對 context 進行了預設置,設置了 ResourceLoader 和 ClassLoader,並向 bean 工廠中添加了一個beanNameGenerator 。
2)、applyInitializers(context)
protected void applyInitializers(ConfigurableApplicationContext context) { for (ApplicationContextInitializer initializer : getInitializers()) { Class<?> requiredType = GenericTypeResolver.resolveTypeArgument( initializer.getClass(), ApplicationContextInitializer.class); Assert.isInstanceOf(requiredType, context, "Unable to call initializer."); initializer.initialize(context); } }
在刷新以前將任何 ApplicationContextInitializer 應用於上下文
3)、load(context, sources.toArray(new Object[0]))
主要是加載各類 beans 到 ApplicationContext 對象中。
protected void load(ApplicationContext context, Object[] sources) { BeanDefinitionLoader loader = createBeanDefinitionLoader( //2 getBeanDefinitionRegistry(context), sources);// 1 if (this.beanNameGenerator != null) { loader.setBeanNameGenerator(this.beanNameGenerator); } if (this.resourceLoader != null) { loader.setResourceLoader(this.resourceLoader); } if (this.environment != null) { loader.setEnvironment(this.environment); } loader.load();//3 }
(1)、getBeanDefinitionRegistry(context)
獲取 bean 定義註冊表
private BeanDefinitionRegistry getBeanDefinitionRegistry(ApplicationContext context) { if (context instanceof BeanDefinitionRegistry) { return (BeanDefinitionRegistry) context; } if (context instanceof AbstractApplicationContext) { return (BeanDefinitionRegistry) ((AbstractApplicationContext) context) .getBeanFactory(); } throw new IllegalStateException("Could not locate BeanDefinitionRegistry"); }
(2)、createBeanDefinitionLoader()
經過 BeanDefinitionLoader 的構造方法把參數(註冊表、資源)傳進去,而後建立 BeanDefinitionLoader。
(3)、load()
把資源所有加載。
十、refreshContext(context)
private void refreshContext(ConfigurableApplicationContext context) { refresh(context);//1 if (this.registerShutdownHook) { try { context.registerShutdownHook(); } catch (AccessControlException ex) { // Not allowed in some environments. } } } //刷新底層的 ApplicationContext protected void refresh(ApplicationContext applicationContext) { Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext); ((AbstractApplicationContext) applicationContext).refresh(); }
refreshContext(context) 方法又調用了 refresh(context)。在調用了 refresh(context) 方法以後,調用了 registerShutdownHook 方法。繼續看它的 refresh 方法:
public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. prepareRefresh(); // Tell the subclass to refresh the internal bean factory. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. prepareBeanFactory(beanFactory); try { // 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. initMessageSource(); // Initialize event multicaster for this context. initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. onRefresh(); // Check for listener beans and register them. registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); //1 // Last step: publish corresponding event. finishRefresh(); } catch (BeansException ex) { 。。。 // Destroy already created singletons to avoid dangling resources. destroyBeans(); // Reset 'active' flag. cancelRefresh(ex); // Propagate exception to caller. throw ex; } finally { // Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore... resetCommonCaches(); } } }
到這裏,咱們就看見重點了,仔細看上的註釋,正在作各類初始化工做,而今天咱們關注的重點就是方法 finishBeanFactoryInitialization(beanFactory)。該方法進行了非懶加載 beans 的初始化工做。如今咱們進入該方法內部,一探究竟。
看上圖方法中的最後一步,調用了 beanFactory 的 preInstantiateSingletons() 方法。此處的 beanFactory 是哪一個類的實例對象呢?
能夠看到 ConfigurableListableBeanFactory 接口的實現類只有 DefaultListableBeanFactory,咱們看下實現類中的 preInstantiateSingletons 方法是怎麼作的。
public void preInstantiateSingletons() throws BeansException { // Iterate over a copy to allow for init methods which in turn register new bean definitions. // While this may not be part of the regular factory bootstrap, it does otherwise work fine. List<String> beanNames = new ArrayList<>(this.beanDefinitionNames); // Trigger initialization of all non-lazy singleton beans... for (String beanName : beanNames) { RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName); if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) { if (isFactoryBean(beanName)) { Object bean = getBean(FACTORY_BEAN_PREFIX + beanName); if (bean instanceof FactoryBean) { final FactoryBean<?> factory = (FactoryBean<?>) bean; boolean isEagerInit; if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean){ isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>) ((SmartFactoryBean<?>) factory)::isEagerInit, getAccessControlContext()); } else { isEagerInit = (factory instanceof SmartFactoryBean && ((SmartFactoryBean<?>) factory).isEagerInit()); } if (isEagerInit) { getBean(beanName); } } } else { getBean(beanName); } } } // Trigger post-initialization callback for all applicable beans... for (String beanName : beanNames) { Object singletonInstance = getSingleton(beanName); if (singletonInstance instanceof SmartInitializingSingleton) { final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance; if (System.getSecurityManager() != null) { AccessController.doPrivileged((PrivilegedAction<Object>) () -> { smartSingleton.afterSingletonsInstantiated(); return null; }, getAccessControlContext()); } else { smartSingleton.afterSingletonsInstantiated(); } } } }
從上面的代碼中能夠看到不少調用了 getBean(beanName) 方法,跟蹤此方法進去後,最終發現 getBean 調用了AbstractBeanFactory 類的 doGetBean(xxx) 方法,doGetBean(xxx) 方法中有這麼一段代碼:
可是 createBean() 方法並無獲得實現,實現類在 AbstractAutowireCapableBeanFactory 中。這纔是建立 bean 的核心方法。
不知不覺,代碼看的愈來愈深,感受思惟都差點回不去 run 方法了,切回大腦的上下文線程到 run 方法去。
十一、afterRefresh(context, applicationArguments):在上下文刷新後調用該方法,其內部沒有作任何操做。
發現沒作任何操做了以後,就以爲有點奇怪,因此把當前版本和 1.5.12 對比了下,發現:
在 1.5.12 中的 afterRefresh() 方法中調用了 callRunners() 方法,可是在 2.0.1 版本中的 run 方法中調用了 callRunners () 方法:
這裏不得不說 SpringApplicationRunListeners 在 2.0.1 中的改變:
能夠發如今 run 方法中,SpringApplicationRunListeners 監聽器的狀態花生了變化,這也是經過對比不一樣版本的代碼才知道的區別,因此說咱們看源碼須要多對比着看。
so,咱們來看下這個 SpringApplicationRunListeners 這個接口:
started 狀態:The context has been refreshed and the application has started but CommandLineRunner and ApplicationRunner have not been called
running 狀態:Called immediately before the run method finishes, when the application context has been refreshed and all CommandLineRunner and ApplicationRunners have been called.
一、Spring Boot 2.0系列文章(一):Spring Boot 2.0 遷移指南
二、Spring Boot 2.0系列文章(二):Spring Boot 2.0 新特性詳解
三、Spring Boot 2.0系列文章(三):Spring Boot 2.0 配置改變
四、Spring Boot 2.0系列文章(四):Spring Boot 2.0 源碼閱讀環境搭建
五、Spring Boot 2.0系列文章(五):Spring Boot 2.0 項目源碼結構預覽
六、Spring Boot 2.0系列文章(六):Spring boot 2.0 中 SpringBootApplication 註解詳解
七、Spring Boot 2.0系列文章(七):SpringApplication 深刻探索
本文從源碼級別分析了 Spring Boot 應用程序的啓動過程,着重看了 SpringApplication 類中的 run 方法其內部實現,並把涉及到的流程代碼都過了一遍。
感悟:有時候跟代碼跟着跟着,發現越陷越深,好難跳出來!後面還需多向別人請教閱讀源碼的技巧!
雖然源碼很難,但隨着不斷的探索,源碼在你面前將會一覽無遺,享受這種探索後的成就感!加油!騷年!
本身本人能力有限,源碼看的很少,上面若有不對的還請留言交流。