注:本系列源码分析基于springboot 2.2.2.RELEASE,对应的spring版本为5.2.2.RELEASE,源码的gitee仓库仓库地址:gitee.com/funcy/sprin….
上一篇文章总结springboot启动流程如下:
接上文,我们继续分析接下来的步骤。
3.10 刷新ioc容器
接下来我们来看看SpringApplication#refreshContext方法:
private void refreshContext(ConfigurableApplicationContext context) {
// 启动spring容器
refresh(context);
if (this.registerShutdownHook) {
try {
// 注册 ShutdownHook
context.registerShutdownHook();
}
catch (AccessControlException ex) {
// Not allowed in some environments.
}
}
}
这个方法操作就两个:
refresh(context):启动spring容器,也不是调用AbstractApplicationContext#refresh方法;context.registerShutdownHook():注册ShutdownHook,可以在jvm进程关闭时处理一些特定的操作。
3.10.1 启动spring容器
进入SpringApplication#refresh:
protected void refresh(ApplicationContext applicationContext) {
Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
// spring 容器的启动操作了
((AbstractApplicationContext) applicationContext).refresh();
}
这个方法很简单,先判断applicationContext的类型是否为AbstractApplicationContext,然后再调用AbstractApplicationContext#refresh()。
关于AbstractApplicationContext#refresh(),那可是大名鼎鼎啊,该方法涵盖了spring容器的启动流程。由于本文不是分析spring的文章,因此这块就不展开分析了,想要了解的启动流程的小伙伴可以参考以下文章:
- 【spring源码分析】spring启动流程(四):启动前的准备工作
- 【spring源码分析】spring启动流程(五):执行BeanFactoryPostProcessor
- 【spring源码分析】spring启动流程(六):注册BeanPostProcessor
- 【spring源码分析】spring启动流程(七):国际化与事件处理
- 【spring源码分析】spring启动流程(八):完成BeanFactory的初始化
- 【spring源码分析】spring启动流程(九):单例bean的创建
- 【spring源码分析】spring启动流程(十):启动完成的处理
在AbstractApplicationContext#refresh()中,spring提供了几个扩展点:
我们当前使用的applicationContext为AnnotationConfigServletWebServerApplicationContext,其中也使用了这些扩展点,我们主要关注这些扩展点的应用。
1. 启动前准备:prepareRefresh()
经过调试发现,initPropertySources() 方法会运行到,调用链如下:
AbstractApplicationContext#refresh
|- AnnotationConfigServletWebServerApplicationContext#prepareRefresh
|- AbstractApplicationContext#prepareRefresh
|- GenericWebApplicationContext#initPropertySources
最终调用的是GenericWebApplicationContext#initPropertySources:
protected void initPropertySources() {
ConfigurableEnvironment env = getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment) env).initPropertySources(this.servletContext, null);
}
}
这个方法里先获取Environment,然后判断是否为ConfigurableWebEnvironment的实例,在前面分析准备运行时环境时,我们得到的Environment为StandardServletEnvironment,是ConfigurableWebEnvironment的符合,然后调用ConfigurableWebEnvironment#initPropertySources方法,结果到了StandardServletEnvironment#initPropertySources:
public void initPropertySources(@Nullable ServletContext servletContext,
@Nullable ServletConfigservletConfig) {
// 替换上面设置的 servletContextInitParams 为 servletContext
// 替换上面设置的 servletConfigInitParams 为 servletConfig
WebApplicationContextUtils.initServletPropertySources(getPropertySources(),
servletContext, servletConfig);
}
这个方法还是很简单,只是将servletContext 与 servletConfig 设置到了Environment中。
2. 获取 beanFactory: obtainFreshBeanFactory()
当前applicationContext对该方法无扩展,不分析。
3. 准备 beanFactory: prepareBeanFactory(beanFactory)
当前applicationContext对该方法无扩展,不分析。
4. 扩展点:postProcessBeanFactory(beanFactory)
AnnotationConfigServletWebServerApplicationContext 重写了这个方法:
@Override
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
// 调用父类的方法
super.postProcessBeanFactory(beanFactory);
// 进行包扫描,这里的包并不存在
if (this.basePackages != null && this.basePackages.length > 0) {
this.scanner.scan(this.basePackages);
}
// 注册bean,为空
if (!this.annotatedClasses.isEmpty()) {
this.reader.register(ClassUtils.toClassArray(this.annotatedClasses));
}
}
这个方法的执行过程如下:
- 调用了父类的方法
super.postProcessBeanFactory(beanFactory) - 进行包扫描,通过调试发现,这里的
basePackages为nul - 注册
annotatedClasses,这里的annotatedClasses为空
我们主要来看看super.postProcessBeanFactory(beanFactory),该方法在ServletWebServerApplicationContext中:
@Override
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
// 添加一个 BeanPostProcessor
beanFactory.addBeanPostProcessor(
new WebApplicationContextServletContextAwareProcessor(this));
// 忽略 ServletContextAware 的自动注入
beanFactory.ignoreDependencyInterface(ServletContextAware.class);
// 注册 web bean 的范围,这里会注册request、session、globalSession的作用域
registerWebApplicationScopes();
}
这个方法内容比较简单,主要是注册BeanPostProcessor以及注册web bean的作用范围。这里我们主要看下WebApplicationContextServletContextAwareProcessor的作用,代码如下:
public class WebApplicationContextServletContextAwareProcessor
extends ServletContextAwareProcessor {
private final ConfigurableWebApplicationContext webApplicationContext;
public WebApplicationContextServletContextAwareProcessor(
ConfigurableWebApplicationContext webApplicationContext) {
Assert.notNull(webApplicationContext, "WebApplicationContext must not be null");
this.webApplicationContext = webApplicationContext;
}
/**
* 获取 ServletContext
*/
@Override
protected ServletContext getServletContext() {
ServletContext servletContext = this.webApplicationContext.getServletContext();
return (servletContext != null) ? servletContext : super.getServletContext();
}
/**
* 获取 ServletConfig
*/
@Override
protected ServletConfig getServletConfig() {
ServletConfig servletConfig = this.webApplicationContext.getServletConfig();
return (servletConfig != null) ? servletConfig : super.getServletConfig();
}
}
这个类似乎并没有做什么,我们再跟进父类,由于它是个BeanPostProcessor,我们主要关注它的postProcessBeforeInitialization()与postProcessAfterInitialization()两个方法:
public class ServletContextAwareProcessor implements BeanPostProcessor {
...
public Object postProcessBeforeInitialization(Object bean,
String beanName) throws BeansException {
// 设置 ServletContext
if (getServletContext() != null && bean instanceof ServletContextAware) {
((ServletContextAware) bean).setServletContext(getServletContext());
}
// 设置 ServletConfig
if (getServletConfig() != null && bean instanceof ServletConfigAware) {
((ServletConfigAware) bean).setServletConfig(getServletConfig());
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
return bean;
}
}
可以看到,这个BeanPostProcessor是用来处理ServletContextAware 与 ServletConfigAware 两个 Aware 接口的,套路同处理ApplicationAware、BeanFactoryAware等一样。
5. 执行 BeanFactoryPostProcessors: invokeBeanFactoryPostProcessors(beanFactory)
当前applicationContext对该方法无扩展,不分析。
值得一提的是,在这个方法中,有个重的BeanFactoryPostProcessor会被执行:ConfigurationClassPostProcessor,springboot的自动装配的启用注解@EnableAutoConfiguration 会在这里处理,自动装配类的加载、条件注解也是在ConfigurationClassPostProcessor中。
6. 注册 BeanPostProcessor: registerBeanPostProcessors(beanFactory)
当前applicationContext对该方法无扩展,不分析。
7. 初始化 MessageSource(用于国际化操作): initMessageSource()
当前applicationContext对该方法无扩展,不分析。
8. 初始化事件广播器:initApplicationEventMulticaster()
当前applicationContext对该方法无扩展,不分析。
9. 扩展点:onRefresh()
当前applicationContext对该方法的扩展为 ServletWebServerApplicationContext#onRefresh方法,代码如下:
@Override
protected void onRefresh() {
// 调用父类方法
super.onRefresh();
try {
// 创建web服务器,如tomcat,jetty等
createWebServer();
}
catch (Throwable ex) {
throw new ApplicationContextException(...);
}
}
可以 看到,web服务器是在这个方法中创建的。不过web服务器的创建并不简单,需要经过多种条件判断,关于这点我们后面再详细说明。
10. 注册事件监听器:registerListeners()
当前applicationContext对该方法无扩展,不分析。
11. 初始化单例bean: finishBeanFactoryInitialization(beanFactory)
当前applicationContext对该方法无扩展,不分析。
12. 完成启动操作: finishRefresh()
当前applicationContext对该方法的扩展为 ServletWebServerApplicationContext#finishRefresh方法,代码如下:
@Override
protected void finishRefresh() {
super.finishRefresh();
// 启动web容器
WebServer webServer = startWebServer();
if (webServer != null) {
// 发布 ServletWebServerInitializedEvent 事件
publishEvent(new ServletWebServerInitializedEvent(webServer, this));
}
}
/**
* 启动web容器
*/
private WebServer startWebServer() {
WebServer webServer = this.webServer;
if (webServer != null) {
webServer.start();
}
return webServer;
}
可以看到,这里才是真正启动web容器。
13. 清除缓存: resetCommonCaches()
当前applicationContext对该方法无扩展,不分析。
3.10.2 注册ShutdownHook
我们再来看context.registerShutdownHook(),该方法由AbstractApplicationContext#registerShutdownHook提供:
public abstract class AbstractApplicationContext extends DefaultResourceLoader
implements ConfigurableApplicationContext {
...
@Override
public void registerShutdownHook() {
if (this.shutdownHook == null) {
// 指定线程的名字
this.shutdownHook = new Thread(SHUTDOWN_HOOK_THREAD_NAME) {
@Override
public void run() {
synchronized (startupShutdownMonitor) {
// 这里就是 ShutdownHook 的内容
doClose();
}
}
};
Runtime.getRuntime().addShutdownHook(this.shutdownHook);
}
}
/**
* 处理容器的关闭操作
*/
protected void doClose() {
// Check whether an actual close attempt is necessary...
if (this.active.get() && this.closed.compareAndSet(false, true)) {
LiveBeansView.unregisterApplicationContext(this);
try {
// 发布关闭事件
publishEvent(new ContextClosedEvent(this));
}
catch (Throwable ex) {
logger.warn(...);
}
// 调用 lifecycle 的 onClose() 方法
if (this.lifecycleProcessor != null) {
try {
this.lifecycleProcessor.onClose();
}
catch (Throwable ex) {
logger.warn(...);
}
}
// 销毁 bean
destroyBeans();
// 关闭容器
closeBeanFactory();
// 扩展点,待子类实现
onClose();
// 清除监听器
if (this.earlyApplicationListeners != null) {
this.applicationListeners.clear();
this.applicationListeners.addAll(this.earlyApplicationListeners);
}
// 设置 active 标识
this.active.set(false);
}
}
...
}
可以看到,context.registerShutdownHook()实际上是运行了doClose()方法,用来处理容器的关闭操作。关闭spring容器的关闭,注释已经相当清楚了,这里就不深入了。
好了,容器的启动就分析到这里了,从流程上来讲,与spring容器启动的最大扩展在于onRefresh()与finishRefresh(),前者创建了webServer容器,后者启动了webServer容器。
本文原文链接:my.oschina.net/funcy/blog/… ,限于作者个人水平,文中难免有错误之处,欢迎指正!原创不易,商业转载请联系作者获得授权,非商业转载请注明出处。