深入讲解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{ List initializers=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容器了。
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对毛票票的支持。