關注我
轉載請務必注明原創地址為:http://www.54tianzhisheng.cn/2018/04/30/springboot_SpringApplication/
前言
在 Spring Boot 項目的啟動類中常見代碼如下:
@SpringBootApplication
public class SpringbotApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbotApplication.class, args);
}
}
其中也就兩個比較引人注意的地方:
@SpringBootApplication
SpringApplication.run()
對於第一個注解 @SpringBootApplication
,我已經在博客 Spring Boot 2.0系列文章(六):Spring Boot 2.0中SpringBootApplication注解詳解 中詳細的講解了。接下來就是深入探究第二個了 SpringApplication.run()
。
換個姿勢
上面的姿勢太簡單了,只一行代碼就完事了。
SpringApplication.run(SpringbotApplication.class, args);
其實是支持做一些個性化的設置,接下來我們換個姿勢瞧瞧:
@SpringBootApplication
public class SpringbotApplication {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(SpringbotApplication.class);
// 自定義應用程序的配置
//app.setXxx()
app.run(args)
}
}
沒錯,就是通過一個構造函數,然后設置相關的屬性,從而達到定制化服務。有哪些屬性呢?
屬性對應的 get/set 方法
看到沒,還很多呢!
舉個例子:你想把 Spring Boot 項目的默認 Banner 換成你自己的,就需要在這里如下:
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 初始化
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),然后進行初始化。
1、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 程序
}
2、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。
3、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。
4、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;
}
run 方法背后的秘密
上面看完了構造方法后,已經初始化了一個 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)。
1、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。
2、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));
}
3、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;
}
4、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);
}
}
5、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());
}
}
6、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);
}
7、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 中是怎么樣的?
8、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 的。
9、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);
//1、
postProcessApplicationContext(context);
//2、
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");
//3、
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()
把資源全部加載。
10、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 方法去。
11、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.
相關文章
1、Spring Boot 2.0系列文章(一):Spring Boot 2.0 遷移指南
2、Spring Boot 2.0系列文章(二):Spring Boot 2.0 新特性詳解
3、Spring Boot 2.0系列文章(三):Spring Boot 2.0 配置改變
4、Spring Boot 2.0系列文章(四):Spring Boot 2.0 源碼閱讀環境搭建
5、Spring Boot 2.0系列文章(五):Spring Boot 2.0 項目源碼結構預覽
6、Spring Boot 2.0系列文章(六):Spring boot 2.0 中 SpringBootApplication 注解詳解
7、Spring Boot 2.0系列文章(七):SpringApplication 深入探索
總結
本文從源碼級別分析了 Spring Boot 應用程序的啟動過程,着重看了 SpringApplication 類中的 run 方法其內部實現,並把涉及到的流程代碼都過了一遍。
感悟:有時候跟代碼跟着跟着,發現越陷越深,好難跳出來!后面還需多向別人請教閱讀源碼的技巧!
最后
雖然源碼很難,但隨着不斷的探索,源碼在你面前將會一覽無遺,享受這種探索后的成就感!加油!騷年!
自己本人能力有限,源碼看的不多,上面如有不對的還請留言交流。