一、从熟知的启动类开始
下面这个启动类是我们熟悉的springboot的启动类:代码是自己写的一个简单的springboot的demo: https://gitee.com/leijisong/springcloud-demo
@SpringBootApplication(scanBasePackages = {"com.mall"})
@EntityScan(basePackages = {"com.mall.domain.dataobject"})
@EnableJpaRepositories(basePackages = "com.mall.domain")
@EnableDiscoveryClient
@EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true)
@EnableRabbit
public class MallApplication {
public static void main(String[] args) {
SpringApplication.run(MallApplication.class, args);
}
}
1. 调用SpringApplication.run(MallApplication.class, args); 启动了我们的SpringBoot应用:
2. 查看run方法:使用自定义SpringApplication进行启动
public static ConfigurableApplicationContext run(Class<?>[] primarySources,
String[] args) {
return new SpringApplication(primarySources).run(args);
}
2.1 创建SpringApplication --> new SpringApplication(primarySources)
源码如下:
/**
* Create a new {@link SpringApplication} instance. The application context will load
* beans from the specified primary sources (see {@link SpringApplication class-level}
* documentation for details. The instance can be customized before calling
* {@link #run(String...)}.
* @param resourceLoader the resource loader to use
* @param primarySources the primary bean sources
* @see #run(Class, String[])
* @see #setSources(Set)
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
// 将启动类放入到primarySources
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 根据classpath下的类,感知当前web应用类型,如WebApplicationType.SERVLET,WebApplicationType.REACTIVE
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 从spring.factories中获取所有的key为:org.springframework.context.ApplicationContextInitializer
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
// 从spring.factories中获取所有的key为:org.springframework.context.ApplicationListener
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 根据main方法得到mainApplicationClass
this.mainApplicationClass = deduceMainApplicationClass();
}
我们看下spring.factories这个文件(这个文件通过第一个专题应该很熟悉了)。看看ApplicationContextInitializer和ApplicationListener都定义了哪些?
实际上,远不止这些,通过扩展的还有:
ApplicationContextInitializer:
ApplicationListener:
总结:这里主要是做了一些初始化动作
- 获取启动类加载ioc容器
- 获取web应用类型
- 通过spring.factories读取对外扩展的ApplicationContextInitializer ,ApplicationListener
- 根据main推算出所在类
2.2 启动
启动的核心逻辑源码如下:
/**
* Run the Spring application, creating and refreshing a new
* {@link ApplicationContext}.
* @param args the application arguments (usually passed from a Java main method)
* @return a running {@link ApplicationContext}
*/
public ConfigurableApplicationContext run(String... args) {
// StopWatch主要用来记录Springboot的启动耗时
StopWatch stopWatch = new StopWatch();
// 启动的开始时间
stopWatch.start();
// Spring上下文接口,继承ApplicationContext
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
// 开启了headless模式
configureHeadlessProperty();
// 从spring.factories读取SpringApplicationRunListener组件,发布事件/运行监听
SpringApplicationRunListeners listeners = getRunListeners(args);
// 发布事件:ApplicationStartingEvent
listeners.starting();
try {
// 根据命令行参数,实例化ApplicationArguments
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
// 基于监听器与初始化环境:包括环境变量和配置文件的信息
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
// 忽略beaninfo的Bean
configureIgnoreBeanInfo(environment);
// 打印Bananer横幅
Banner printedBanner = printBanner(environment);
// 根据webApplicationType创建上下文
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
// 初始化Spring上下文
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
// 关键:加载IOC容器;加载自动配置类
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;
}
2.2.1 prepareEnvironment(listeners,applicationArguments);
这个主要做的是基于监听器的环境初始化
源码:
private ConfigurableEnvironment prepareEnvironment(
SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// 根据webApplicationType创建环境,创建时就会读取java环境变量和系统变量
ConfigurableEnvironment environment = getOrCreateEnvironment();
// 将命令行参数读取环境变量中
configureEnvironment(environment, applicationArguments.getSourceArgs());
listeners.environmentPrepared(environment);
// 将spring.main开头的信息都绑定SpringApplication
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader())
.convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
}
// 更新PropertySource
ConfigurationPropertySources.attach(environment);
return environment;
}
private ConfigurableEnvironment getOrCreateEnvironment() {
if (this.environment != null) {
return this.environment;
}
switch (this.webApplicationType) {
case SERVLET:
return new StandardServletEnvironment();
case REACTIVE:
return new StandardReactiveWebEnvironment();
default:
return new StandardEnvironment();
}
}
protected void configureEnvironment(ConfigurableEnvironment environment,
String[] args) {
if (this.addConversionService) {
ConversionService conversionService = ApplicationConversionService
.getSharedInstance();
environment.setConversionService(
(ConfigurableConversionService) conversionService);
}
configurePropertySources(environment, args);
configureProfiles(environment, args);
}
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);
}
}
2.2.2 prepareContext
源码:
private void prepareContext(ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
// 上下文后置处理
postProcessApplicationContext(context);
// 将之前拿到的所有ApplicationContextInitializer,遍历执行initialize方法
applyInitializers(context);
// 发布ApplicationContextInitializedEvent
listeners.contextPrepared(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// 获取Spring上下文的Bean工厂
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
// 如果出现两个同名bean。直接抛异常
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory) beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
// 加载资源
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
// 读取主启动类,注册成Bean
load(context, sources.toArray(new Object[0]));
// 读取完配置类之后,发送ApplicationPreparedEvent
listeners.contextLoaded(context);
}
/**
* 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) {
for (ApplicationContextInitializer initializer : getInitializers()) {
Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(
initializer.getClass(), ApplicationContextInitializer.class);
Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
initializer.initialize(context);
}
}
public void contextPrepared(ConfigurableApplicationContext context) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.contextPrepared(context);
}
}
/**
* Return an immutable set of all the sources that will be added to an
* ApplicationContext when {@link #run(String...)} is called. This method combines any
* primary sources specified in the constructor with any additional ones that have
* been {@link #setSources(Set) explicitly set}.
* @return an immutable set of all sources
*/
public Set<Object> getAllSources() {
Set<Object> allSources = new LinkedHashSet<>();
if (!CollectionUtils.isEmpty(this.primarySources)) {
allSources.addAll(this.primarySources);
}
if (!CollectionUtils.isEmpty(this.sources)) {
allSources.addAll(this.sources);
}
return Collections.unmodifiableSet(allSources);
}
public void started(ConfigurableApplicationContext context) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.started(context);
}
}
@Override
public void started(ConfigurableApplicationContext context) {
context.publishEvent(
new ApplicationStartedEvent(this.application, this.args, context));
}
2.2.3 refreshContext
源码:
private void refreshContext(ConfigurableApplicationContext context) {
refresh(context);
if (this.registerShutdownHook) {
try {
context.registerShutdownHook();
}
catch (AccessControlException ex) {
// Not allowed in some environments.
}
}
}
@Override
public void refresh() throws BeansException, IllegalStateException {synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
// 设置Spring的启动事件,开启活跃状态;初始化属性源信息并验证必要属性
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
// 从spring容器获取BeanFactory并进行相关设置为后续使用做准备
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
// 继上一步beanfactory设置之后进行后续操作,不同spring容器进行不同操作
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
// 使用了PostProcessorRegistrationDelegate类的registerBeanPostProcessors方法执行
// 1)先找出实现了PriorityOrdered接口的BeanPostProcessor并排序后加到BeanFactory的BeanPostProcessor集合中
// 2)找出实现了Ordered接口的BeanPostProcessor并排序后加到BeanFactory的BeanPostProcessor集合中
// 3)没有实现PriorityOrdered和Ordered接口的BeanPostProcessor加到BeanFactory的BeanPostProcessor集合中
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
// 初始化国际化属性
initMessageSource();
// Initialize event multicaster for this context.
// 初始化事件广播器,用于发布事件。 EventPublishingRunlistener会监听事件,在run函数之前contextPrepared时候已经注入了。
// 这个时候不需要注册,只要拿到BeanFactory的广播器直接设置到spring容器,如果没有再自己初始化
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
// 不同容器各自实现,比如ConfigEmbeddedWebApplicationContext中会调用
// createEmbeddedServletContainer方法去创建内置的Servlet容器,
// 目前支持 tomcat,jetty,undertow。
onRefresh();
// Check for listener beans and register them.
// 把spring容器内的listener和beanfactory的listener都添加到广播器中
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
// 实例化BeanFactory 中已经被注册但是没被实例化的所有实例,懒加载除外。
// 比如invokeBeanFactoryPostProcessors方法中根据各种注解解析出来的类,都会初始化。
// 初始化的过程中各种BeanPostProcessor开始起作用
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
// 初始化生命周期处理器LifecycleProcessor并调用其onrefresh方法,
// 找到SmartLifecycle接口的所有实现类并调用start方法,发布事件告知listener,
// 如果设置了JMX相关属性,还会调用LiveBeansView的registerApplicationContext方法
finishRefresh();
}
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.
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();
}
}
}
总结:
- 初始化SpringApplication 从spring.factories 读取 listener ApplicationContextInitializer 。
- 运行run方法
- 读取 环境变量 配置信息
- 创建springApplication上下文:ServletWebServerApplicationContext
- 预初始化上下文 : 读取启动类
- 调用refresh 加载ioc容器
- 加载所有的自动配置类
- 创建servlet容器
- 在这个过程中springboot会调用很多监听器对外进行扩展