深入讲解spring boot中servlet的启动过程与原理
前言
本文主要介绍了关于springboot中servlet启动过程与原理的相关内容,下面话不多说了,来一起看看详细的介绍吧
启动过程与原理:
1springboot应用启动运行run方法
StopWatchstopWatch=newStopWatch();
stopWatch.start();
ConfigurableApplicationContextcontext=null;
FailureAnalyzersanalyzers=null;
configureHeadlessProperty();
SpringApplicationRunListenerslisteners=getRunListeners(args);
listeners.starting();
try{
ApplicationArgumentsapplicationArguments=newDefaultApplicationArguments(
args);
ConfigurableEnvironmentenvironment=prepareEnvironment(listeners,
applicationArguments);
BannerprintedBanner=printBanner(environment);
//创建一个ApplicationContext容器
context=createApplicationContext();
analyzers=newFailureAnalyzers(context);
prepareContext(context,environment,listeners,applicationArguments,
printedBanner);
//刷新IOC容器
refreshContext(context);
afterRefresh(context,applicationArguments);
listeners.finished(context,null);
stopWatch.stop();
if(this.logStartupInfo){
newStartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(),stopWatch);
}
returncontext;
}
catch(Throwableex){
handleRunFailure(context,listeners,analyzers,ex);
thrownewIllegalStateException(ex);
}
2 createApplicationContext():创建IOC容器,如果是web应用则创建AnnotationConfigEmbeddedWebApplacation的IOC容器,如果不是,则创建AnnotationConfigApplication的IOC容器
publicstaticfinalStringDEFAULT_CONTEXT_CLASS="org.springframework.context."
+"annotation.AnnotationConfigApplicationContext";
/**
*Theclassnameofapplicationcontextthatwillbeusedbydefaultforweb
*environments.
*/
publicstaticfinalStringDEFAULT_WEB_CONTEXT_CLASS="org.springframework."
+"boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext";
protectedConfigurableApplicationContextcreateApplicationContext(){
Class>contextClass=this.applicationContextClass;
if(contextClass==null){
try{
//根据应用环境,创建不同的IOC容器
contextClass=Class.forName(this.webEnvironment
?DEFAULT_WEB_CONTEXT_CLASS:DEFAULT_CONTEXT_CLASS);
}
catch(ClassNotFoundExceptionex){
thrownewIllegalStateException(
"UnablecreateadefaultApplicationContext,"
+"pleasespecifyanApplicationContextClass",
ex);
}
}
return(ConfigurableApplicationContext)BeanUtils.instantiate(contextClass);
}
3 refreshContext(context)springboot刷新IOC容器(创建容器对象,并初始化容器,创建容器每一个组件)
privatevoidrefreshContext(ConfigurableApplicationContextcontext){
refresh(context);
if(this.registerShutdownHook){
try{
context.registerShutdownHook();
}
catch(AccessControlExceptionex){
//Notallowedinsomeenvironments.
}
}
}
4refresh(context);刷新刚才创建的IOC容器
protectedvoidrefresh(ApplicationContextapplicationContext){
Assert.isInstanceOf(AbstractApplicationContext.class,applicationContext);
((AbstractApplicationContext)applicationContext).refresh();
}
5调用父类的refresh()的方法
publicvoidrefresh()throwsBeansException,IllegalStateException{
Objectvar1=this.startupShutdownMonitor;
synchronized(this.startupShutdownMonitor){
this.prepareRefresh();
ConfigurableListableBeanFactorybeanFactory=this.obtainFreshBeanFactory();
this.prepareBeanFactory(beanFactory);
try{
this.postProcessBeanFactory(beanFactory);
this.invokeBeanFactoryPostProcessors(beanFactory);
this.registerBeanPostProcessors(beanFactory);
this.initMessageSource();
this.initApplicationEventMulticaster();
this.onRefresh();
this.registerListeners();
this.finishBeanFactoryInitialization(beanFactory);
this.finishRefresh();
}catch(BeansExceptionvar9){
if(this.logger.isWarnEnabled()){
this.logger.warn("Exceptionencounteredduringcontextinitialization-cancellingrefreshattempt:"+var9);
}
this.destroyBeans();
this.cancelRefresh(var9);
throwvar9;
}finally{
this.resetCommonCaches();
}
}
}
6 抽象父类AbstractApplicationContext类的子类EmbeddedWebApplicationContext的onRefresh方法
@Override
protectedvoidonRefresh(){
super.onRefresh();
try{
createEmbeddedServletContainer();
}
catch(Throwableex){
thrownewApplicationContextException("Unabletostartembeddedcontainer",
ex);
}
}
7 在createEmbeddedServletContainer放啊发中会获取嵌入式Servlet容器工厂,由容器工厂创建Servlet
privatevoidcreateEmbeddedServletContainer(){
EmbeddedServletContainerlocalContainer=this.embeddedServletContainer;
ServletContextlocalServletContext=getServletContext();
if(localContainer==null&&localServletContext==null){
//获取嵌入式Servlet容器工厂
EmbeddedServletContainerFactorycontainerFactory=getEmbeddedServletContainerFactory();
//根据容器工厂获取对应嵌入式Servlet容器
this.embeddedServletContainer=containerFactory
.getEmbeddedServletContainer(getSelfInitializer());
}
elseif(localServletContext!=null){
try{
getSelfInitializer().onStartup(localServletContext);
}
catch(ServletExceptionex){
thrownewApplicationContextException("Cannotinitializeservletcontext",
ex);
}
}
initPropertySources();
}
8 从IOC容器中获取Servlet容器工厂
//EmbeddedWebApplicationContext#getEmbeddedServletContainerFactory
protectedEmbeddedServletContainerFactorygetEmbeddedServletContainerFactory(){
//Usebeannamessothatwedon'tconsiderthehierarchy
String[]beanNames=getBeanFactory()
.getBeanNamesForType(EmbeddedServletContainerFactory.class);
if(beanNames.length==0){
thrownewApplicationContextException(
"UnabletostartEmbeddedWebApplicationContextduetomissing"
+"EmbeddedServletContainerFactorybean.");
}
if(beanNames.length>1){
thrownewApplicationContextException(
"UnabletostartEmbeddedWebApplicationContextduetomultiple"
+"EmbeddedServletContainerFactorybeans:"
+StringUtils.arrayToCommaDelimitedString(beanNames));
}
returngetBeanFactory().getBean(beanNames[0],
EmbeddedServletContainerFactory.class);
}
9 使用Servlet容器工厂获取嵌入式Servlet容器,具体使用哪一个容器工厂看配置环境依赖
this.embeddedServletContainer=containerFactory .getEmbeddedServletContainer(getSelfInitializer());
10 上述创建过程 首先启动IOC容器,接着启动嵌入式Servlet容器,接着将IOC容器中剩下没有创建的对象获取出来,比如自己创建的controller
//Instantiateallremaining(non-lazy-init)singletons. finishBeanFactoryInitialization(beanFactory);
protectedvoidfinishBeanFactoryInitialization(ConfigurableListableBeanFactorybeanFactory){
//Initializeconversionserviceforthiscontext.
if(beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME)&&
beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME,ConversionService.class)){
beanFactory.setConversionService(
beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME,ConversionService.class));
}
//Registeradefaultembeddedvalueresolverifnobeanpost-processor
//(suchasaPropertyPlaceholderConfigurerbean)registeredanybefore:
//atthispoint,primarilyforresolutioninannotationattributevalues.
if(!beanFactory.hasEmbeddedValueResolver()){
beanFactory.addEmbeddedValueResolver(newStringValueResolver(){
@Override
publicStringresolveStringValue(StringstrVal){
returngetEnvironment().resolvePlaceholders(strVal);
}
});
}
//InitializeLoadTimeWeaverAwarebeansearlytoallowforregisteringtheirtransformersearly.
String[]weaverAwareNames=beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class,false,false);
for(StringweaverAwareName:weaverAwareNames){
getBean(weaverAwareName);
}
//StopusingthetemporaryClassLoaderfortypematching.
beanFactory.setTempClassLoader(null);
//Allowforcachingallbeandefinitionmetadata,notexpectingfurtherchanges.
beanFactory.freezeConfiguration();
//Instantiateallremaining(non-lazy-init)singletons.
beanFactory.preInstantiateSingletons();
}
看看preInstantiateSingletons方法
publicvoidpreInstantiateSingletons()throwsBeansException{
if(this.logger.isDebugEnabled()){
this.logger.debug("Pre-instantiatingsingletonsin"+this);
}
ListbeanNames=newArrayList(this.beanDefinitionNames);
Iteratorvar2=beanNames.iterator();
while(true){
while(true){
StringbeanName;
RootBeanDefinitionbd;
do{
do{
do{
if(!var2.hasNext()){
var2=beanNames.iterator();
while(var2.hasNext()){
beanName=(String)var2.next();
ObjectsingletonInstance=this.getSingleton(beanName);
if(singletonInstanceinstanceofSmartInitializingSingleton){
finalSmartInitializingSingletonsmartSingleton=(SmartInitializingSingleton)singletonInstance;
if(System.getSecurityManager()!=null){
AccessController.doPrivileged(newPrivilegedAction
是使用getBean方法来通过反射将所有未创建的实例创建出来
使用嵌入式Servlet容器:
优点: 简单,便携
缺点: 默认不支持jsp,优化定制比较复杂
使用外置Servlet容器的步骤:
1 必须创建war项目,需要剑豪web项目的目录结构
2 嵌入式Tomcat依赖scope指定provided
3 编写SpringBootServletInitializer类子类,并重写configure方法
publicclassServletInitializerextendsSpringBootServletInitializer{
@Override
protectedSpringApplicationBuilderconfigure(SpringApplicationBuilderapplication){
returnapplication.sources(SpringBoot04WebJspApplication.class);
}
}
4 启动服务器
jar包和war包启动区别
jar包:执行SpringBootApplication的run方法,启动IOC容器,然后创建嵌入式Servlet容器
war包: 先是启动Servlet服务器,服务器启动Springboot应用(springBootServletInitizer),然后启动IOC容器
Servlet3.0+规则
1 服务器启动(web应用启动),会创建当前web应用里面所有jar包里面的ServletContainerlnitializer实例
2ServletContainerInitializer的实现放在jar包的META-INF/services文件夹下
3 还可以使用@HandlesTypes注解,在应用启动的时候加载指定的类。
外部Tomcat流程以及原理
① 启动Tomcat
② 根据上述描述的Servlet3.0+规则,可以在Spring的web模块里面找到有个文件名为javax.servlet.ServletContainerInitializer的文件,而文件的内容为org.springframework.web.SpringServletContainerInitializer,用于加载SpringServletContainerInitializer类
③看看SpringServletContainerInitializer定义
@HandlesTypes(WebApplicationInitializer.class)
publicclassSpringServletContainerInitializerimplementsServletContainerInitializer{
/**
*Delegatethe{@codeServletContext}toany{@linkWebApplicationInitializer}
*implementationspresentontheapplicationclasspath.
*Becausethisclassdeclares@{@codeHandlesTypes(WebApplicationInitializer.class)},
*Servlet3.0+containerswillautomaticallyscantheclasspathforimplementations
*ofSpring's{@codeWebApplicationInitializer}interfaceandprovidethesetofall
*suchtypestothe{@codewebAppInitializerClasses}parameterofthismethod.
*
Ifno{@codeWebApplicationInitializer}implementationsarefoundontheclasspath,
*thismethodiseffectivelyano-op.AnINFO-levellogmessagewillbeissuednotifying
*theuserthatthe{@codeServletContainerInitializer}hasindeedbeeninvokedbutthat
*no{@codeWebApplicationInitializer}implementationswerefound.
*
Assumingthatoneormore{@codeWebApplicationInitializer}typesaredetected,
*theywillbeinstantiated(andsortedifthe@{@link
*org.springframework.core.annotation.Order@Order}annotationispresentor
*the{@linkorg.springframework.core.OrderedOrdered}interfacehasbeen
*implemented).Thenthe{@linkWebApplicationInitializer#onStartup(ServletContext)}
*methodwillbeinvokedoneachinstance,delegatingthe{@codeServletContext}such
*thateachinstancemayregisterandconfigureservletssuchasSpring's
*{@codeDispatcherServlet},listenerssuchasSpring's{@codeContextLoaderListener},
*oranyotherServletAPIcomponentrysuchasfilters.
*@paramwebAppInitializerClassesallimplementationsof
*{@linkWebApplicationInitializer}foundontheapplicationclasspath
*@paramservletContexttheservletcontexttobeinitialized
*@seeWebApplicationInitializer#onStartup(ServletContext)
*@seeAnnotationAwareOrderComparator
*/
@Override
publicvoidonStartup(Set>webAppInitializerClasses,ServletContextservletContext)
throwsServletException{
Listinitializers=newLinkedList();
if(webAppInitializerClasses!=null){
for(Class>waiClass:webAppInitializerClasses){
//Bedefensive:Someservletcontainersprovideuswithinvalidclasses,
//nomatterwhat@HandlesTypessays...
if(!waiClass.isInterface()&&!Modifier.isAbstract(waiClass.getModifiers())&&
WebApplicationInitializer.class.isAssignableFrom(waiClass)){
try{
//为所有的WebApplicationInitializer类型创建实例,并加入集合中
initializers.add((WebApplicationInitializer)waiClass.newInstance());
}
catch(Throwableex){
thrownewServletException("FailedtoinstantiateWebApplicationInitializerclass",ex);
}
}
}
}
if(initializers.isEmpty()){
servletContext.log("NoSpringWebApplicationInitializertypesdetectedonclasspath");
return;
}
servletContext.log(initializers.size()+"SpringWebApplicationInitializersdetectedonclasspath");
AnnotationAwareOrderComparator.sort(initializers);
//调用每一个WebApplicationInitializer实例的onstartup方法
for(WebApplicationInitializerinitializer:initializers){
initializer.onStartup(servletContext);
}
}
}
在上面一段长长的注释中可以看到,SpringServletContainerInitializer将@HandlesTypes(WebApplicationInitializer.class)标注的所有WebApplicationInitializer这个类型的类都传入到onStartup方法的Set参数中,并通过反射为这些WebApplicationInitializer类型的类创建实例;
④ 方法最后,每一个WebApplicationInitilizer实现调用自己onstartup方法
⑤ 而WebApplicationInitializer有个抽象实现类SpringBootServletInitializer(记住我们继承了该抽象类),则会调用每一个WebApplicationInitializer实例(包括SpringBootServletInitializer)的onStartup方法:
publicabstractclassSpringBootServletInitializerimplementsWebApplicationInitializer{
//othercode...
@Override
publicvoidonStartup(ServletContextservletContext)throwsServletException{
//Loggerinitializationisdeferredincaseaordered
//LogServletContextInitializerisbeingused
this.logger=LogFactory.getLog(getClass());
//创建IOC容器
WebApplicationContextrootAppContext=createRootApplicationContext(
servletContext);
if(rootAppContext!=null){
servletContext.addListener(newContextLoaderListener(rootAppContext){
@Override
publicvoidcontextInitialized(ServletContextEventevent){
//no-opbecausetheapplicationcontextisalreadyinitialized
}
});
}
else{
this.logger.debug("NoContextLoaderListenerregistered,as"
+"createRootApplicationContext()didnot"
+"returnanapplicationcontext");
}
}
protectedWebApplicationContextcreateRootApplicationContext(
ServletContextservletContext){
//创建Spring应用构建器,并进行相关属性设置
SpringApplicationBuilderbuilder=createSpringApplicationBuilder();
StandardServletEnvironmentenvironment=newStandardServletEnvironment();
environment.initPropertySources(servletContext,null);
builder.environment(environment);
builder.main(getClass());
ApplicationContextparent=getExistingRootWebApplicationContext(servletContext);
if(parent!=null){
this.logger.info("Rootcontextalreadycreated(usingasparent).");
servletContext.setAttribute(
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,null);
builder.initializers(newParentContextApplicationContextInitializer(parent));
}
builder.initializers(
newServletContextApplicationContextInitializer(servletContext));
builder.contextClass(AnnotationConfigEmbeddedWebApplicationContext.class);
//调用configure方法,创建war类型的web项目后,由于编写SpringBootServletInitializer的子类重写configure方法,所以此处调用的是我们定义的子类重写的configure方法
builder=configure(builder);
//通过构建器构建了一个Spring应用
SpringApplicationapplication=builder.build();
if(application.getSources().isEmpty()&&AnnotationUtils
.findAnnotation(getClass(),Configuration.class)!=null){
application.getSources().add(getClass());
}
Assert.state(!application.getSources().isEmpty(),
"NoSpringApplicationsourceshavebeendefined.Eitheroverridethe"
+"configuremethodoraddan@Configurationannotation");
//Ensureerrorpagesareregistered
if(this.registerErrorPageFilter){
application.getSources().add(ErrorPageFilterConfiguration.class);
}
//启动Spring应用
returnrun(application);
}
//Spring应用启动,创建并返回IOC容器
protectedWebApplicationContextrun(SpringApplicationapplication){
return(WebApplicationContext)application.run();
}
}
SpringBootServletInitializer实例执行onStartup方法的时候会通过createRootApplicationContext方法来执行run方法,接下来的过程就同以jar包形式启动的应用的run过程一样了,在内部会创建IOC容器并返回,只是以war包形式的应用在创建IOC容器过程中,不再创建Servlet容器了。
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对毛票票的支持。