一、spring boot嵌入式servlet容器运行原理:
1)SpringBoot应用启动时运行主程序的run方法。
2)调用SpringApplication的run()方法,其中调用refreshContext(context);
SpringBoot刷新IOC容器【创建IOC容器对象,并初始化容器,创建容器中的每一个组件】,如果是web应用创建AnnotationConfigEmbeddedWebApplicationContext,否则创建AnnotationConfigApplicationContext;
StopWatch stopWatch = new StopWatch ( ) ;
stopWatch. start ( ) ;
ConfigurableApplicationContext context = null ;
FailureAnalyzers analyzers = null ;
configureHeadlessProperty ( ) ;
SpringApplicationRunListeners listeners = getRunListeners ( args) ;
listeners. starting ( ) ;
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments (
args) ;
ConfigurableEnvironment environment = prepareEnvironment ( listeners,
applicationArguments) ;
Banner printedBanner = printBanner ( environment) ;
context = createApplicationContext ( ) ;
analyzers = new FailureAnalyzers ( context) ;
prepareContext ( context, environment, listeners, applicationArguments,
printedBanner) ;
refreshContext ( context) ;
afterRefresh ( context, applicationArguments) ;
listeners. finished ( context, null ) ;
stopWatch. stop ( ) ;
if ( this . logStartupInfo) {
new StartupInfoLogger ( this . mainApplicationClass)
. logStarted ( getApplicationLog ( ) , stopWatch) ;
}
return context;
}
3)在refreshContext(ConfigurableApplicationContext context)方法中调用refresh(ApplicationContext applicationContext)方法,在refresh(ApplicationContext applicationContext)方法中调用AbstractApplicationContext的refresh()方法刷新刚才创建好的ioc容器;
4)在refresh()方法中,调用onRefresh()方法,子类的EmbeddedWebApplicationContext重写了父类的onRefresh()方法;
synchronized ( this . startupShutdownMonitor) {
prepareRefresh ( ) ;
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory ( ) ;
prepareBeanFactory ( beanFactory) ;
try {
postProcessBeanFactory ( beanFactory) ;
invokeBeanFactoryPostProcessors ( beanFactory) ;
registerBeanPostProcessors ( beanFactory) ;
initMessageSource ( ) ;
initApplicationEventMulticaster ( ) ;
onRefresh ( ) ;
registerListeners ( ) ;
finishBeanFactoryInitialization ( beanFactory) ;
finishRefresh ( ) ;
}
EmbeddedWebApplicationContext 的onRefresh ( ) 方法
@Override
protected void onRefresh ( ) {
super . onRefresh ( ) ;
try {
createEmbeddedServletContainer ( ) ;
}
catch ( Throwable ex) {
throw new ApplicationContextException ( "Unable to start embedded container" ,
ex) ;
}
}
5)在子类的onRefresh方法中调用createEmbeddedServletContainer()方法,创建嵌入式servlet容器EmbeddedServletContainer
6)在createEmbeddedServletContainer()方法中获取嵌入式的Servlet容器工厂:
EmbeddedServletContainerFactory containerFactory = getEmbeddedServletContainerFactory ( ) ;
从ioc容器中获取EmbeddedServletContainerFactory组件,并创建子类的TomcatEmbeddedServletContainerFactory创建对象,后置处理器一看是这个对象,就获取所有的定制器来先定制Servlet容器的相关配置;
7)使用容器工厂获取嵌入式的Servlet容器:this.embeddedServletContainer = containerFactory.getEmbeddedServletContainer(getSelfInitializer());
private void createEmbeddedServletContainer ( ) {
EmbeddedServletContainer localContainer = this . embeddedServletContainer;
ServletContext localServletContext = getServletContext ( ) ;
if ( localContainer == null && localServletContext == null ) {
EmbeddedServletContainerFactory containerFactory = getEmbeddedServletContainerFactory ( ) ;
this . embeddedServletContainer = containerFactory
. getEmbeddedServletContainer ( getSelfInitializer ( ) ) ;
}
else if ( localServletContext != null ) {
try {
getSelfInitializer ( ) . onStartup ( localServletContext) ;
}
catch ( ServletException ex) {
throw new ApplicationContextException ( "Cannot initialize servlet context" ,
ex) ;
}
}
initPropertySources ( ) ;
}
8)子类TomcatEmbeddedServletContainerFactory通过getEmbeddedServletContainerFactory()创建Tomcat对象,通过run()方法启动Servlet容器;先启动嵌入式的Servlet容器,再将ioc容器中剩下没有创建出的对象获取出来;
先创建IOC容器,同时创建嵌入式的Servlet容器,之后在去创建IOC容器的剩下的步骤,将自己写的类扫到容器中
二、spring boot使用外部servlet容器运行原理:
1>servlet3.0之后增加了如下的规则:
1)服务器启动(web应用启动)时会找在当前web应用里面每一个jar包里面找到ServletContainerInitializer实例:
2)ServletContainerInitializer的实现放在jar包的META-INF/services文件夹下,有一个名为javax.servlet.ServletContainerInitializer的文件,内容就是ServletContainerInitializer的实现类的全类名使用@HandlesTypes,在应用启动的时候加载我们感兴趣的类;
2>原理:
1)首先启动tomcat容器
2)在Spring的web模块中有这个文件:
org\springframework\spring- web\4.3 .14 . RELEASE \spring- web- 4.3 .14 . RELEASE . jar! \METAINF \services\javax. servlet. ServletContainerInitializer :
其中这个文件中有org. springframework. web. SpringServletContainerInitializer 这个类
3)SpringServletContainerInitializer将@HandlesTypes(WebApplicationInitializer.class)标注的所有这个类型的类都传入到onStartup方法的Set>;为这些WebApplicationInitializer类型的类创建实例;而WebApplicationInitializer的实现类如下图所示:
4)每一个WebApplicationInitializer都调用自己的onStartup;等同于创建一个SpringBootServletInitializer类的对象,并执行onStartup方法
5)SpringBootServletInitializer实例执行onStartup的时候会调用createRootApplicationContext方法创建容器
protected WebApplicationContext createRootApplicationContext (
ServletContext servletContext) {
SpringApplicationBuilder builder = createSpringApplicationBuilder ( ) ;
builder. main ( getClass ( ) ) ;
ApplicationContext parent = getExistingRootWebApplicationContext ( servletContext) ;
if ( parent != null ) {
this . logger. info ( "Root context already created (using as parent)." ) ;
servletContext. setAttribute (
WebApplicationContext . ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE , null ) ;
builder. initializers ( new ParentContextApplicationContextInitializer ( parent) ) ;
}
builder. initializers (
new ServletContextApplicationContextInitializer ( servletContext) ) ;
builder. contextClass ( AnnotationConfigEmbeddedWebApplicationContext . class ) ;
builder = configure ( builder) ;
builder. listeners ( new WebEnvironmentPropertySourceInitializer ( servletContext) ) ;
SpringApplication application = builder. build ( ) ;
if ( application. getSources ( ) . isEmpty ( ) && AnnotationUtils
. findAnnotation ( getClass ( ) , Configuration . class ) != null ) {
application. getSources ( ) . add ( getClass ( ) ) ;
}
Assert . state ( ! application. getSources ( ) . isEmpty ( ) ,
"No SpringApplication sources have been defined. Either override the "
+ "configure method or add an @Configuration annotation" ) ;
if ( this . registerErrorPageFilter) {
application. getSources ( ) . add ( ErrorPageFilterConfiguration . class ) ;
}
return run ( application) ;
}
6)上段代码最后一行调用run()方法,在此方法中再次调用了SpringApplication的run()方法,后续流程,除了不去创建嵌入式tomcat容器外,会创建ioc容器,具体步骤参考上面嵌入式servlet容器运行原理的(2)-(4),创建过程中不会在调用(4)的onrefresh()方法了。
总结:先启动Servlet容器,再启动SpringBoot应用