想必大家对springbootApplication很熟悉,它是我们springboot的项目的一个入口,来看一段大家都熟知的代码:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class AppApplication {
public static void main(String[] args) {
SpringApplication.run(AppApplication.class, args);
}
}
上面这是我创建的一个springboot项目,这段代码就是我们项目的入口,看似一段很简单的代码,其真正的作用在于注解SpringBootApplication身上,既然我们知道了入口,那么我们来看看:
- 注解SpringBootApplication在此处首先表明是一个springboot应用,通过该注解可以开启自动装配的功能.
- SpringApplication.run(...)是用来启动该应用.
所以接下来我们来看看springboot项目的启动的过程,关于注解SpringBootApplication我们专门来说.
SpringApplgication
SpringApplgication位于org.springframework.boot.SpringApplication包下,是spring应用的启动类,来看一下官方是如何说的:
Class that can be used to bootstrap and launch a Spring application from a Java main method. By default class will perform the following steps to bootstrap your application
在实际的开发中,我们应该用到的是它的static方法,来看代码:
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);
}
public static void main(String[] args) throws Exception {
SpringApplication.run(new Class>[0], args);
}
- 简单的三个静态方法,我们可以从中发现,第二个和第三个静态方法最后还是调用第一个静态方法
- 首先是new SpringApplication对象.
- 接着是调用SpringApplication#run(Class> primarySource, String... args)方法,真正的运行我们的spring应用
既然知道了上面代码的执行顺序,我们进行一步一步的学习,首先我们来看构造SpringApplication的过程,代码如下:
//用来保存javaConfig类的数组
private Set> primarySources;
//我们启动类的类型
private Class> mainApplicationClass;
//资源加载器
private ResourceLoader resourceLoader;
//针对于web类型的应用
private WebApplicationType webApplicationType;
//用来保存ApplicationContextInitializer类型的集合
private List> initializers;
//用来保存ApplicationListener的集合
private List> listeners;
public SpringApplication(Class>... primarySources) {
this(null, primarySources);
}
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();
//初始化ApplicationContextInitializer过程
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
//初始化ApplicationListener过程
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
简单的来看下我们上面提到的这些属性:
- primarySources:是用来保存javaConfig类的,也就是我们本篇开头示例中的AppApplication类.
- mainApplicationClass是通过调用 #deduceMainApplicationClass() 方法,来判断我们调用的是哪个main方法.
- resourceLoader也就是我们的资源加载器,我们在之前的spring文章中的spring容器实现之资源加载我们详细的讲解了加载资源的过程,感兴趣的可以去看看.
- webApplicationType其主要的作用是通过调用WebApplicationType#deduceFromClasspath()方法来判断我们的web类型.
- initializers 是ApplicationContextInitializer集合,详解后面来说.
- listeners是用来ApplicationListener的集合,详解见后面.
deduceMainApplicationClass()
private Class> deduceMainApplicationClass() {
try {
//获取当前StackTraceElement类型的数组
StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
//遍历处理
for (StackTraceElement stackTraceElement : stackTrace) {
//判断哪个执行了main方法
if ("main".equals(stackTraceElement.getMethodName())) {
return Class.forName(stackTraceElement.getClassName());
}
}
}
catch (ClassNotFoundException ex) {
// Swallow and continue
}
return null;
}
该方法是我们属性mainApplicationClass的作用,位于SpringApplication.java中.
getSpringFactoriesInstances()
该方法的是啥哈,主要是针对于上面的属性initializers和listeners来实现的,不同的是一个获取ApplicationContextInitializer的对象集合,一个是ApplicationListener的对象集合,我们来看实现过程:
SpringApplication.java
private Collection getSpringFactoriesInstances(Class type) {
return getSpringFactoriesInstances(type, new Class>[] {});
}
private Collection getSpringFactoriesInstances(Class type, Class>[] parameterTypes, Object... args) {
//获取ClassLoader加载器
ClassLoader classLoader = getClassLoader();
// Use names and ensure unique to protect against duplicates
//加载指定的类型的类名集合,从META-INF/spring.factories下加载
Set names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
//创建实例对象
List instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
//给创建的实例对象排序
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
几行代码,却做了很多事,简单的来看一下:
- 首先是调用#getClassLoader()来获取类加载器.
- 然后是调用SpringFactoriesLoader#loadFactoryNames(...)方法去加载指定了类型的类名集合,在META-INF/spring.factories路径下去加载.
- 接着是通过#createSpringFactoriesInstances(...)来进行实例的创建过程
- 最后对刚创建的实例们进行排序
这就是上面代码的为我们所做的事,其中在第二步中,可能我们对META-INF/spring.factories路径下的东东不太明白,其实就是以key-value的形式存在,关于具体的加载过程,后续我们来说,我们来看看创建实例的过程:
// SpringApplication.java
/**
* 创建对象的集合
*
* @param type 父类
* @param parameterTypes 构造方法的参数类型
* @param classLoader 类加载器
* @param args 参数
* @param names 类名的集合
* @param 泛型
* @return 对象的数组
*/
private List createSpringFactoriesInstances(Class type, Class>[] parameterTypes,
ClassLoader classLoader, Object[] args, Set names) {
//通过names集合的大小构建一个新的同样大小的集合
List instances = new ArrayList<>(names.size());
//遍历处理
for (String name : names) {
try {
//利用反射机制,获取name对应的具体类
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;
}
上述创建实例的过程 其实也并不复杂,还是利用反射的机制来完成实例的创建过程,看完了第一个核心过程,我们来看看第二个过程就是调用方法#run(...),方法明确表示就是用来启动我们的spring的运用的,接下来我们来看看具体的实现过程:
run方法
#SpringApplication.java
public ConfigurableApplicationContext run(String... args) {
//1.创建StopWatch实例
//调用它的start方法启动StopWatch来计时run方法的执行cd
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection exceptionReporters = new ArrayList<>();
//2.配置Headless属性
configureHeadlessProperty();
//通过getRunListeners来获取所有的SpringApplicationRunListeners实例,同时开启监听操作
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
//3.通过参数args来创建ApplicationArguments对象
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//4.预加载所有的配置信息,包括environment
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
//5.打印Banner图样
Banner printedBanner = printBanner(environment);
//6.创建上下文
context = createApplicationContext();
//7.创建异常报告器
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
//8.初始化上下文(通过调用别的方法)前的准备
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
//9.执行初始化操作
refreshContext(context);
//10.后置处理的操作,默认是空实现
afterRefresh(context, applicationArguments);
//11.停止执行的运行时间
stopWatch.stop();
//12.springboot的启动日志打印过程
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
//13.通知相应的监听器spring容器启动完成
listeners.started(context);
//14.调用 ApplicationRunner或CommandLineRunner的运行方法
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
//若在此过程中发生异常,直接抛IllegalStateException
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
//15.再次通知相应的监听器spring容器处于运行的状态
listeners.running(context);
}
catch (Throwable ex) {
//如果在此过程中发生异常,直接抛IllegalStateException
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
额,代码有点长,我们简单的来看一下每一步的过程:
- 在1.处,首先是创建StopWatch实例同时调用start方法启动它,其目的是用来监控run方法运行的时长.
- 在2.处,通过调用#configureHeadlessProperty()方法来配置Headless属性
private void configureHeadlessProperty() {
System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS,
System.getProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));
}
好像该属性和AWT有关,感觉对我们来说作用不大,哈哈哈...,不是很清楚.
- 在2.1.处,通过调用#getRunListeners()方法来获取SpringApplicationRunListeners集合,同时开启监听操作,代码如下:
//SpringApplication.java
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class>[] types = new Class>[] { SpringApplication.class, String[].class };
return new SpringApplicationRunListeners(logger,
getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
}
我们可以通过Dbug发现我们此时的listeners数组里的变量,如图所示:
listeners中仅有一个变量实例就是我们图中的SpringApplicationRunListener类型.关于它后面来说,这里知道即可.
- 在3.处通过我们传入的参数args来构建ApplicationArguments对象,作为下一步的必要参数.
- 在4.处通过调用#prepareEnvironment(...)来预加载所有的配置信息,包括environment.
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// Create and configure the environment
//1.通过调用#getOrCreateEnvironment()来创建ConfigurableEnvironment实例
ConfigurableEnvironment environment = getOrCreateEnvironment();
//2.进行相关的配置
configureEnvironment(environment, applicationArguments.getSourceArgs());
//3.设置environment的属性源
ConfigurationPropertySources.attach(environment);
//4.通知SpringApplicationRunListener当前环境已就绪
listeners.environmentPrepared(environment);
//5.将environment绑定到SpringApplication上
bindToSpringApplication(environment);
//6.非自定义的环境,通过条件去转换
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
deduceEnvironmentClass());
}
//7.将environment添加到PropertySource中
ConfigurationPropertySources.attach(environment);
return environment;
}
好像代码不是很多,逻辑挺多的,简单的来看一下:
- 在4.1.处,主要是构建ConfigurableEnvironment对象的过程,来看看如何构建的
//SpringApplication.java
private ConfigurableEnvironment environment;
//针对于web类型的应用
private WebApplicationType webApplicationType;
private ConfigurableEnvironment getOrCreateEnvironment() {
//如果有,直接返回即可.
if (this.environment != null) {
return this.environment;
}
//不存在的话,通过webApplicationType来创建
switch (this.webApplicationType) {
case SERVLET:
return new StandardServletEnvironment();
case REACTIVE:
return new StandardReactiveWebEnvironment();
default:
return new StandardEnvironment();
}
}
可以看到的是:
- 如果当前存在environment的话,直接用它
- 在不存在的情况下,通过webApplicationType来创建不同类型的ConfigurableEnvironment对象
- 在4.2.处,通过#configureEnvironment(...)方法进行环境的配置过程.
//是否添加共享的ConversionService
private boolean addConversionService = true;
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
//设置environment的ConversionService属性
if (this.addConversionService) {
ConversionService conversionService = ApplicationConversionService.getSharedInstance();
environment.setConversionService((ConfigurableConversionService) conversionService);
}
//1.1.添加environment的PropertySource属性源
configurePropertySources(environment, args);
//1.2.配置environment的activeProfiles属性
configureProfiles(environment, args);
}
该方法其主要的作用是设置environment的一些属性其中包括:
- 在4.1.1.处,通过方法#configurePropertySources(...)来设置environment的 PropertySource属性
//SpringApplication.java
/**jvm参数*/
private boolean addCommandLineProperties = true;
protected void configurePropertySources(ConfigurableEnvironment environment, String[] args) {
//获取Mu对象tablePropertySources
MutablePropertySources sources = environment.getPropertySources();
//设置默认的PropertySources
if (this.defaultProperties != null && !this.defaultProperties.isEmpty()) {
sources.addLast(new MapPropertySource("defaultProperties", this.defaultProperties));
}
//通过启动参数来设置
if (this.addCommandLineProperties && args.length > 0) {
String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME;
//如果存在的话,替换原来的
if (sources.contains(name)) {
PropertySource> source = sources.get(name);
CompositePropertySource composite = new CompositePropertySource(name);
composite.addPropertySource(
new SimpleCommandLinePropertySource("springApplicationCommandLineArgs", args));
composite.addPropertySource(source);
sources.replace(name, composite);
}
//不存在的话进行添加
else {
sources.addFirst(new SimpleCommandLinePropertySource(args));
}
}
}
从代码中可以看到的是作为PropertySources参数的分别有jvm参数以及默认的propertySources.
- 在4.1.2处通过方法#configureProfiles(...)来设置切换属性
private Set additionalProfiles = new HashSet<>();
protected void configureProfiles(ConfigurableEnvironment environment, String[] args) {
//确保已经被初始化
environment.getActiveProfiles(); // ensure they are initialized
// But these ones should go first (last wins in a property key clash)
Set profiles = new LinkedHashSet<>(this.additionalProfiles);
profiles.addAll(Arrays.asList(environment.getActiveProfiles()));
//设置ActiveProfiles
environment.setActiveProfiles(StringUtils.toStringArray(profiles));
}
代码简单,这里涉及到了springboot是如何进行Profiles的切换过程,后续来说.
- 在4.3处,主要是添加属性源的过程.
//ConfigurationPropertySource.java
private static final String ATTACHED_PROPERTY_SOURCE_NAME = "configurationProperties";
public static void attach(Environment environment) {
Assert.isInstanceOf(ConfigurableEnvironment.class, environment);
MutablePropertySources sources = ((ConfigurableEnvironment) environment).getPropertySources();
PropertySource> attached = sources.get(ATTACHED_PROPERTY_SOURCE_NAME);
if (attached != null && attached.getSource() != sources) {
sources.remove(ATTACHED_PROPERTY_SOURCE_NAME);
attached = null;
}
if (attached == null) {
sources.addFirst(new ConfigurationPropertySourcesPropertySource(ATTACHED_PROPERTY_SOURCE_NAME,
new SpringConfigurationPropertySources(sources)));
}
}
从代码中我们可以看到的通过获取到的attached来区分,存在的话从sources中移除,不存在的话,加入默认的即可,这里不多说了,接着看:
在4.4处通过方法#environmentPrepared(...)来通知SpringApplicationRunListener,其主要的目的是告诉它当前环境已经准备就绪.
在4.5.处,通过方法#bindToSpringApplication(...)将当前environment绑定在当前SpringApplication上.
在4.6.处,如果非自定义 environment ,则根据条件转换.默认情况下isCustomEnvironment 为 false ,所以会执行这块逻辑。但是,一般情况下,返回的还是 environment 自身,所以可以无视这块逻辑先.
在4.7.处.跟4.3处一样,这里就不多扯了.
在5处,通过调用#printBanner(...)来打印springboot启动的Banner图样,相信这个大家都知道,就不多扯了...
在6.处,通过方法#createApplicationContext()创建spring容器.后面来讲
在7.处,通过 #getSpringFactoriesInstances(Class
type) 方法,进行获取SpringBootExceptionReporter 类型的对象数组,SpringBootExceptionReporter ,记录启动过程中的异常信息.
通过dbug我们发现,在此处的exceptionReporters数组中有一个变量,如图:
- 在8处,通过调用#prepareContext(...)方法来初始化上下文环境,这里主要是通过调用别动初始化方法来完成的,详解见后面.
- 在9处,调用#refreshContext(ConfigurableApplicationContext context)方法,启动(刷新) Spring 容器,详解见后面.
- 在10处,调用#afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) 方法执,行 Spring 容器的初始化的后置处理,默认实现为空,代码如下:
protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) {
}
- 在11处,通过调用stopWatch#stop()来停止统计run方法的执行时长.
- 在12处,打印 Spring Boot 启动时的时长日志,如下图所示:
- 在13处,调用SpringApplicationRunListeners#started(ConfigurableApplicationContext context) 方法,通知 SpringApplicationRunListener 的集合,Spring 容器启动完成,关于该方法的详解后面来说.
- 在14.处,首先是调用# ApplicationRunner或CommandLineRunner的运行方法,若在此过程中发生异常,直接抛IllegalStateException
- 在15处,通过SpringApplicationRunListeners#running(ConfigurableApplicationContext context) 方法,通知 SpringApplicationRunListener 的数组,Spring 容器运行中,如果在此过程中发生异常,直接抛IllegalStateException
接下来我们分别来看看上述我们遗留的一些方法的详解,首先我们来看看6处遗留的创建spring容器的过程来看是如何实现的.
createApplicationContext()方法
//SpringApplication.java
private Class extends ConfigurableApplicationContext> applicationContextClass;
/**
* The class name of application context that will be used by default for web
* environments.
*/
public static final String DEFAULT_SERVLET_WEB_CONTEXT_CLASS = "org.springframework.boot."
+ "web.servlet.context.AnnotationConfigServletWebServerApplicationContext";
/**
* The class name of application context that will be used by default for reactive web
* environments.
*/
public static final String DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = "org.springframework."
+ "boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext";
/**
* The class name of application context that will be used by default for non-web
* environments.
*/
public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context."
+ "annotation.AnnotationConfigApplicationContext";
protected ConfigurableApplicationContext createApplicationContext() {
//通过webApplicationType类型类来完成ApplicationContext的创建过程
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);
}
}
//这里表示contextClass不为null,则直接通过反射的方法去创建
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
从上述代码可以看出,创建spring容器的过程并不复杂,还是根据webApplicationType来判断进行创建不同的ApplicationContext对象,接着我们来看在8处遗留的通过方法#prepareContext(....)来初始化spring容器
prepareContext()
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
//设置context的environment的环境
context.setEnvironment(environment);
//1.设置一些跟当前ApplicationContext有关的属性
postProcessApplicationContext(context);
//2.初始化ApplicationContextInitializer
applyInitializers(context);
//3.通知SpringApplicationRunListener的数组,Spring容器准备完成
listeners.contextPrepared(context);
//4.打印启动日志
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
//5.获取单例ConfigurableListableBeanFactory
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
//将applicationArguments注册到beanFactory中
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory) beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
// Load the sources
//6.加载资源文件主要是(beanDefinitions)
Set
我们可以看到的是该方法主要的作用是针对applicationContext对象的属性做了一些初始化的操作.我们简单的来看下过程:
- 首先是对environment的设置
- 在1处,通过调用#postProcessApplicationContext(ConfigurableApplicationContext context)方法,设置一些跟当前ApplicationContext有关的属性,代码如下:
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());
}
}
if (this.addConversionService) {
context.getBeanFactory().setConversionService(ApplicationConversionService.getSharedInstance());
}
}
- 在2处,通过调用 #applyInitializers(ConfigurableApplicationContext context) 方法,初始化 ApplicationContextInitializer,代码如下:
/**
* Apply any {@link ApplicationContextInitializer}s to the context before it is
* refreshed.
* @param context the configured ApplicationContext (not refreshed yet)
* @see ConfigurableApplicationContext#refresh()
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
protected void applyInitializers(ConfigurableApplicationContext context) {
//遍历处理ApplicationContextInitializer数组
for (ApplicationContextInitializer initializer : getInitializers()) {
//非空的泛型校验
Class> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),
ApplicationContextInitializer.class);
Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
//初始化context
initializer.initialize(context);
}
}
- 在3处,通过SpringApplicationRunListeners#contextPrepared(ConfigurableApplicationContext context) 方法,通知 SpringApplicationRunListener 的数组,Spring 容器准备完成
- 在4处,打印springboot启动时的日志
- 在5处,只要是获取单例的ConfigurableListableBeanFactory实例并设置相关属性
- 在6处,主要是加载资源文件(beanDefinition),来看代码:
protected void load(ApplicationContext context, Object[] sources) {
if (logger.isDebugEnabled()) {
logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
}
//<1>创建BeanDefinitionLoader实例
BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);
if (this.beanNameGenerator != null) {
//2.设置loader的BeanNameGenerator属性
loader.setBeanNameGenerator(this.beanNameGenerator);
}
if (this.resourceLoader != null) {
//设置loader的ResourceLoader属性
loader.setResourceLoader(this.resourceLoader);
}
if (this.environment != null) {
//设置loader的Environment属性
loader.setEnvironment(this.environment);
}
//<3>执行加载操作
loader.load();
}
我们可以看到的是在<1>处,首先是通过方法#getBeanDefinitionRegistry(...)来构建BeanDefinitionRegistry对象,我们来看代码:
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");
}
代码简单就不多扯了,接着看:
- 同样是在<1>处,等创建完BeanDefinitionRegistry对象时,作为构建BeanDefinitionLoader对象的参数进行创建过程,其中调用的是#createBeanDefinitionLoader(...)方法,额,大家都懂,由于篇幅太长我们后续来说吧.
- 在<2>处,通过条件的判断对BeanDefinitionLoader对象分别进行属性BeanNameGenerator 和ResourceLoader以及Environment的设置过程.
- 在<3>处才是执行加载的过程,感兴趣的可以去看看,到这里我就为止了...
- 在7处,调用 SpringApplicationRunListeners#contextLoaded(ConfigurableApplicationContext context) 方法,通知 SpringApplicationRunListener 的数组,Spring 容器加载完成
接着我们来看9处遗留的关于方法#refreshContext(...)启动spring容器的过程,直接看代码:
//是否开启对ShutdownHook的注册
private boolean registerShutdownHook = true;
private void refreshContext(ConfigurableApplicationContext context) {
//1.开启spring容器
refresh(context);
if (this.registerShutdownHook) {
try {
//2.向context中注册ShutdownHook
context.registerShutdownHook();
}
catch (AccessControlException ex) {
// Not allowed in some environments.
}
}
}
代码结构清晰简单,其主要的逻辑处理还是通过调用别的方法来完成,简单的来看一下:
- 在1处,通过调用#refresh(ApplicationContext applicationContext)方法来开启spring容器.代码如下:
/**
* Refresh the underlying {@link ApplicationContext}.
* @param applicationContext the application context to refresh
*/
protected void refresh(ApplicationContext applicationContext) {
Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
((AbstractApplicationContext) applicationContext).refresh();
}
- 在2.处,调用 ConfigurableApplicationContext#registerShutdownHook() 方法,注册 ShutdownHook ,该类主要用于 Spring 应用的关闭时,销毁相应的 Bean 们
看完了该方法的作用我们来看看在14.处我们遗留的callRunners(...)的详解.
callRunners
该方法主要的作用是通过调用ApplicationRunners或者是CommandLineRunner方法,我们来看看代码:
private void callRunners(ApplicationContext context, ApplicationArguments args) {
//<1>.用来保存所有的runners
List
方法简单明了,我们简单的总结一下:
- 在<1>处,创建一个用来保存runners类型bean的数组.
- 在<1.4>处,遍历runners数组,执行逻辑.我们来看代码:
private void callRunner(ApplicationRunner runner, ApplicationArguments args) {
try {
(runner).run(args);
}
catch (Exception ex) {
throw new IllegalStateException("Failed to execute ApplicationRunner", ex);
}
}
private void callRunner(CommandLineRunner runner, ApplicationArguments args) {
try {
(runner).run(args.getSourceArgs());
}
catch (Exception ex) {
throw new IllegalStateException("Failed to execute CommandLineRunner", ex);
}
}
在上述代码中,用到了ApplicationRunner接口和CommandLineRunner接口,这里就不多说了.
SpringApplicationRunListeners
该类位于org.springframework.boot.SpringApplicationRunListeners包下,是对SpringApplicationRunListener的封装,我们来看代码:
''''''
/**
* A collection of {@link SpringApplicationRunListener}.
*
* @author Phillip Webb
*/
class SpringApplicationRunListeners {
private final Log log;
private final List listeners;
SpringApplicationRunListeners(Log log, Collection extends SpringApplicationRunListener> listeners) {
this.log = log;
this.listeners = new ArrayList<>(listeners);
}
public void starting() {
for (SpringApplicationRunListener listener : this.listeners) {
listener.starting();
}
}
public void environmentPrepared(ConfigurableEnvironment environment) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.environmentPrepared(environment);
}
}
public void contextPrepared(ConfigurableApplicationContext context) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.contextPrepared(context);
}
}
public void contextLoaded(ConfigurableApplicationContext context) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.contextLoaded(context);
}
}
public void started(ConfigurableApplicationContext context) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.started(context);
}
}
public void running(ConfigurableApplicationContext context) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.running(context);
}
}
public void failed(ConfigurableApplicationContext context, Throwable exception) {
for (SpringApplicationRunListener listener : this.listeners) {
callFailedListener(listener, context, exception);
}
}
private void callFailedListener(SpringApplicationRunListener listener, ConfigurableApplicationContext context,
Throwable exception) {
try {
listener.failed(context, exception);
}
catch (Throwable ex) {
if (exception == null) {
ReflectionUtils.rethrowRuntimeException(ex);
}
if (this.log.isDebugEnabled()) {
this.log.error("Error handling failed", ex);
}
else {
String message = ex.getMessage();
message = (message != null) ? message : "no error message";
this.log.warn("Error handling failed (" + message + ")");
}
}
}
我们来看一下该接口方法的定义:
''''
public interface SpringApplicationRunListener {
/**
* Called immediately when the run method has first started. Can be used for very
* early initialization.
*/
void starting();
/**
* Called once the environment has been prepared, but before the
* {@link ApplicationContext} has been created.
* @param environment the environment
*/
void environmentPrepared(ConfigurableEnvironment environment);
/**
* Called once the {@link ApplicationContext} has been created and prepared, but
* before sources have been loaded.
* @param context the application context
*/
void contextPrepared(ConfigurableApplicationContext context);
/**
* Called once the application context has been loaded but before it has been
* refreshed.
* @param context the application context
*/
void contextLoaded(ConfigurableApplicationContext context);
/**
* The context has been refreshed and the application has started but
* {@link CommandLineRunner CommandLineRunners} and {@link ApplicationRunner
* ApplicationRunners} have not been called.
* @param context the application context.
* @since 2.0.0
*/
void started(ConfigurableApplicationContext context);
/**
* Called immediately before the run method finishes, when the application context has
* been refreshed and all {@link CommandLineRunner CommandLineRunners} and
* {@link ApplicationRunner ApplicationRunners} have been called.
* @param context the application context.
* @since 2.0.0
*/
void running(ConfigurableApplicationContext context);
/**
* Called when a failure occurs when running the application.
* @param context the application context or {@code null} if a failure occurred before
* the context was created
* @param exception the failure
* @since 2.0.0
*/
void failed(ConfigurableApplicationContext context, Throwable exception);
}
该接口里面的方法都有详细的解释,可以自己去看看,我们发现该接口唯一的实现类是EventPublishingRunListener
EventPublishingRunListener类
public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {
/**
* 当前的spring应用
*/
private final SpringApplication application;
/***
* 保存所有的参数的集合
*/
private final String[] args;
/**
* 事件广播器
*/
private final SimpleApplicationEventMulticaster initialMulticaster;
public EventPublishingRunListener(SpringApplication application, String[] args) {
this.application = application;
this.args = args;
this.initialMulticaster = new SimpleApplicationEventMulticaster();
for (ApplicationListener> listener : application.getListeners()) {
this.initialMulticaster.addApplicationListener(listener);
}
}
@Override
public int getOrder() {
return 0;
}
@Override
public void starting() {
this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
}
@Override
public void environmentPrepared(ConfigurableEnvironment environment) {
this.initialMulticaster
.multicastEvent(new ApplicationEnvironmentPreparedEvent(this.application, this.args, environment));
}
@Override
public void contextPrepared(ConfigurableApplicationContext context) {
this.initialMulticaster
.multicastEvent(new ApplicationContextInitializedEvent(this.application, this.args, context));
}
@Override
public void contextLoaded(ConfigurableApplicationContext context) {
for (ApplicationListener> listener : this.application.getListeners()) {
if (listener instanceof ApplicationContextAware) {
((ApplicationContextAware) listener).setApplicationContext(context);
}
context.addApplicationListener(listener);
}
this.initialMulticaster.multicastEvent(new ApplicationPreparedEvent(this.application, this.args, context));
}
@Override
public void started(ConfigurableApplicationContext context) {
context.publishEvent(new ApplicationStartedEvent(this.application, this.args, context));
}
@Override
public void running(ConfigurableApplicationContext context) {
context.publishEvent(new ApplicationReadyEvent(this.application, this.args, context));
}
@Override
public void failed(ConfigurableApplicationContext context, Throwable exception) {
ApplicationFailedEvent event = new ApplicationFailedEvent(this.application, this.args, context, exception);
if (context != null && context.isActive()) {
// Listeners have been registered to the application context so we should
// use it at this point if we can
context.publishEvent(event);
}
else {
// An inactive context may not have a multicaster so we use our multicaster to
// call all of the context's listeners instead
if (context instanceof AbstractApplicationContext) {
for (ApplicationListener> listener : ((AbstractApplicationContext) context)
.getApplicationListeners()) {
this.initialMulticaster.addApplicationListener(listener);
}
}
this.initialMulticaster.setErrorHandler(new LoggingErrorHandler());
this.initialMulticaster.multicastEvent(event);
}
}
private static class LoggingErrorHandler implements ErrorHandler {
private static Log logger = LogFactory.getLog(EventPublishingRunListener.class);
@Override
public void handleError(Throwable throwable) {
logger.warn("Error calling ApplicationEventListener", throwable);
}
}
代码简单,可以自己看看,这里我们可以将EventPublishingRunListener认为是springApplication的一个事件转换器,关于它的详解我们后面来看....