小編給大家分享一下spring boot中servlet啟動(dòng)過(guò)程與原理的示例分析,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!
創(chuàng)新互聯(lián)2013年開創(chuàng)至今,是專業(yè)互聯(lián)網(wǎng)技術(shù)服務(wù)公司,擁有項(xiàng)目網(wǎng)站設(shè)計(jì)制作、網(wǎng)站制作網(wǎng)站策劃,項(xiàng)目實(shí)施與項(xiàng)目整合能力。我們以讓每一個(gè)夢(mèng)想脫穎而出為使命,1280元普洱做網(wǎng)站,已為上家服務(wù),為普洱各地企業(yè)和個(gè)人服務(wù),聯(lián)系電話:18980820575
啟動(dòng)過(guò)程與原理:
1 spring boot 應(yīng)用啟動(dòng)運(yùn)行run方法
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); //創(chuàng)建一個(gè)ApplicationContext容器 context = createApplicationContext(); analyzers = new FailureAnalyzers(context); prepareContext(context, environment, listeners, applicationArguments, printedBanner); //刷新IOC容器 refreshContext(context); afterRefresh(context, applicationArguments); listeners.finished(context, null); stopWatch.stop(); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass) .logStarted(getApplicationLog(), stopWatch); } return context; } catch (Throwable ex) { handleRunFailure(context, listeners, analyzers, ex); throw new IllegalStateException(ex); }
2 createApplicationContext():創(chuàng)建IOC容器,如果是web應(yīng)用則創(chuàng)建AnnotationConfigEmbeddedWebApplacation的IOC容器,如果不是,則創(chuàng)建AnnotationConfigApplication的IOC容器
public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context." + "annotation.AnnotationConfigApplicationContext"; /** * The class name of application context that will be used by default for web * environments. */ public static final String DEFAULT_WEB_CONTEXT_CLASS = "org.springframework." + "boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext"; protected ConfigurableApplicationContext createApplicationContext() { Class> contextClass = this.applicationContextClass; if (contextClass == null) { try { //根據(jù)應(yīng)用環(huán)境,創(chuàng)建不同的IOC容器 contextClass = Class.forName(this.webEnvironment ? DEFAULT_WEB_CONTEXT_CLASS : DEFAULT_CONTEXT_CLASS); } catch (ClassNotFoundException ex) { throw new IllegalStateException( "Unable create a default ApplicationContext, " + "please specify an ApplicationContextClass", ex); } } return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass); }
3 refreshContext(context) spring boot刷新IOC容器(創(chuàng)建容器對(duì)象,并初始化容器,創(chuàng)建容器每一個(gè)組件)
private void refreshContext(ConfigurableApplicationContext context) { refresh(context); if (this.registerShutdownHook) { try { context.registerShutdownHook(); } catch (AccessControlException ex) { // Not allowed in some environments. } } }
4 refresh(context);刷新剛才創(chuàng)建的IOC容器
protected void refresh(ApplicationContext applicationContext) { Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext); ((AbstractApplicationContext) applicationContext).refresh(); }
5 調(diào)用父類的refresh()的方法
public void refresh() throws BeansException, IllegalStateException { Object var1 = this.startupShutdownMonitor; synchronized(this.startupShutdownMonitor) { this.prepareRefresh(); ConfigurableListableBeanFactory beanFactory = 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 (BeansException var9) { if (this.logger.isWarnEnabled()) { this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9); } this.destroyBeans(); this.cancelRefresh(var9); throw var9; } finally { this.resetCommonCaches(); } } }
6 抽象父類AbstractApplicationContext類的子類EmbeddedWebApplicationContext的onRefresh方法
@Override protected void onRefresh() { super.onRefresh(); try { createEmbeddedServletContainer(); } catch (Throwable ex) { throw new ApplicationContextException("Unable to start embedded container", ex); } }
7 在createEmbeddedServletContainer放啊發(fā)中會(huì)獲取嵌入式Servlet容器工廠,由容器工廠創(chuàng)建Servlet
private void createEmbeddedServletContainer() { EmbeddedServletContainer localContainer = this.embeddedServletContainer; ServletContext localServletContext = getServletContext(); if (localContainer == null && localServletContext == null) { //獲取嵌入式Servlet容器工廠 EmbeddedServletContainerFactory containerFactory = getEmbeddedServletContainerFactory(); //根據(jù)容器工廠獲取對(duì)應(yīng)嵌入式Servlet容器 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 從IOC容器中獲取Servlet容器工廠
//EmbeddedWebApplicationContext#getEmbeddedServletContainerFactory protected EmbeddedServletContainerFactory getEmbeddedServletContainerFactory() { // Use bean names so that we don't consider the hierarchy String[] beanNames = getBeanFactory() .getBeanNamesForType(EmbeddedServletContainerFactory.class); if (beanNames.length == 0) { throw new ApplicationContextException( "Unable to start EmbeddedWebApplicationContext due to missing " + "EmbeddedServletContainerFactory bean."); } if (beanNames.length > 1) { throw new ApplicationContextException( "Unable to start EmbeddedWebApplicationContext due to multiple " + "EmbeddedServletContainerFactory beans : " + StringUtils.arrayToCommaDelimitedString(beanNames)); } return getBeanFactory().getBean(beanNames[0], EmbeddedServletContainerFactory.class); }
9 使用Servlet容器工廠獲取嵌入式Servlet容器,具體使用哪一個(gè)容器工廠看配置環(huán)境依賴
this.embeddedServletContainer = containerFactory .getEmbeddedServletContainer(getSelfInitializer());
10 上述創(chuàng)建過(guò)程 首先啟動(dòng)IOC容器,接著啟動(dòng)嵌入式Servlet容器,接著將IOC容器中剩下沒(méi)有創(chuàng)建的對(duì)象獲取出來(lái),比如自己創(chuàng)建的controller
// Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory);
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { // Initialize conversion service for this context. 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)); } // Register a default embedded value resolver if no bean post-processor // (such as a PropertyPlaceholderConfigurer bean) registered any before: // at this point, primarily for resolution in annotation attribute values. if (!beanFactory.hasEmbeddedValueResolver()) { beanFactory.addEmbeddedValueResolver(new StringValueResolver() { @Override public String resolveStringValue(String strVal) { return getEnvironment().resolvePlaceholders(strVal); } }); } // Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early. String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false); for (String weaverAwareName : weaverAwareNames) { getBean(weaverAwareName); } // Stop using the temporary ClassLoader for type matching. beanFactory.setTempClassLoader(null); // Allow for caching all bean definition metadata, not expecting further changes. beanFactory.freezeConfiguration(); // Instantiate all remaining (non-lazy-init) singletons. beanFactory.preInstantiateSingletons(); }
看看 preInstantiateSingletons方法
public void preInstantiateSingletons() throws BeansException { if (this.logger.isDebugEnabled()) { this.logger.debug("Pre-instantiating singletons in " + this); } ListbeanNames = new ArrayList(this.beanDefinitionNames); Iterator var2 = beanNames.iterator(); while(true) { while(true) { String beanName; RootBeanDefinition bd; do { do { do { if (!var2.hasNext()) { var2 = beanNames.iterator(); while(var2.hasNext()) { beanName = (String)var2.next(); Object singletonInstance = this.getSingleton(beanName); if (singletonInstance instanceof SmartInitializingSingleton) { final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton)singletonInstance; if (System.getSecurityManager() != null) { AccessController.doPrivileged(new PrivilegedAction
是使用getBean方法來(lái)通過(guò)反射將所有未創(chuàng)建的實(shí)例創(chuàng)建出來(lái)
使用嵌入式Servlet容器:
優(yōu)點(diǎn): 簡(jiǎn)單,便攜
缺點(diǎn): 默認(rèn)不支持jsp,優(yōu)化定制比較復(fù)雜
使用外置Servlet容器的步驟:
1 必須創(chuàng)建war項(xiàng)目,需要?jiǎng)纖eb項(xiàng)目的目錄結(jié)構(gòu)
2 嵌入式Tomcat依賴scope指定provided
3 編寫SpringBootServletInitializer類子類,并重寫configure方法
public class ServletInitializer extends SpringBootServletInitializer { @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { return application.sources(SpringBoot04WebJspApplication.class); } }
4 啟動(dòng)服務(wù)器
jar包和war包啟動(dòng)區(qū)別
jar包:執(zhí)行SpringBootApplication的run方法,啟動(dòng)IOC容器,然后創(chuàng)建嵌入式Servlet容器
war包: 先是啟動(dòng)Servlet服務(wù)器,服務(wù)器啟動(dòng)Springboot應(yīng)用(springBootServletInitizer),然后啟動(dòng)IOC容器
Servlet 3.0+規(guī)則
1 服務(wù)器啟動(dòng)(web應(yīng)用啟動(dòng)),會(huì)創(chuàng)建當(dāng)前web應(yīng)用里面所有jar包里面的ServletContainerlnitializer實(shí)例
2 ServletContainerInitializer的實(shí)現(xiàn)放在jar包的META-INF/services文件夾下
3 還可以使用@HandlesTypes注解,在應(yīng)用啟動(dòng)的時(shí)候加載指定的類。
外部Tomcat流程以及原理
① 啟動(dòng)Tomcat
② 根據(jù)上述描述的Servlet3.0+規(guī)則,可以在Spring的web模塊里面找到有個(gè)文件名為javax.servlet.ServletContainerInitializer的文件,而文件的內(nèi)容為org.springframework.web.SpringServletContainerInitializer,用于加載SpringServletContainerInitializer類
③看看SpringServletContainerInitializer定義
@HandlesTypes(WebApplicationInitializer.class) public class SpringServletContainerInitializer implements ServletContainerInitializer { /** * Delegate the {@code ServletContext} to any {@link WebApplicationInitializer} * implementations present on the application classpath. *Because this class declares @{@code HandlesTypes(WebApplicationInitializer.class)}, * Servlet 3.0+ containers will automatically scan the classpath for implementations * of Spring's {@code WebApplicationInitializer} interface and provide the set of all * such types to the {@code webAppInitializerClasses} parameter of this method. *
If no {@code WebApplicationInitializer} implementations are found on the classpath, * this method is effectively a no-op. An INFO-level log message will be issued notifying * the user that the {@code ServletContainerInitializer} has indeed been invoked but that * no {@code WebApplicationInitializer} implementations were found. *
Assuming that one or more {@code WebApplicationInitializer} types are detected, * they will be instantiated (and sorted if the @{@link * org.springframework.core.annotation.Order @Order} annotation is present or * the {@link org.springframework.core.Ordered Ordered} interface has been * implemented). Then the {@link WebApplicationInitializer#onStartup(ServletContext)} * method will be invoked on each instance, delegating the {@code ServletContext} such * that each instance may register and configure servlets such as Spring's * {@code DispatcherServlet}, listeners such as Spring's {@code ContextLoaderListener}, * or any other Servlet API componentry such as filters. * @param webAppInitializerClasses all implementations of * {@link WebApplicationInitializer} found on the application classpath * @param servletContext the servlet context to be initialized * @see WebApplicationInitializer#onStartup(ServletContext) * @see AnnotationAwareOrderComparator */ @Override public void onStartup(Set
> webAppInitializerClasses, ServletContext servletContext) throws ServletException { List initializers = new LinkedList (); if (webAppInitializerClasses != null) { for (Class> waiClass : webAppInitializerClasses) { // Be defensive: Some servlet containers provide us with invalid classes, // no matter what @HandlesTypes says... if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) && WebApplicationInitializer.class.isAssignableFrom(waiClass)) { try { //為所有的WebApplicationInitializer類型創(chuàng)建實(shí)例,并加入集合中 initializers.add((WebApplicationInitializer) waiClass.newInstance()); } catch (Throwable ex) { throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex); } } } } if (initializers.isEmpty()) { servletContext.log("No Spring WebApplicationInitializer types detected on classpath"); return; } servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath"); AnnotationAwareOrderComparator.sort(initializers); //調(diào)用每一個(gè)WebApplicationInitializer實(shí)例的onstartup方法 for (WebApplicationInitializer initializer : initializers) { initializer.onStartup(servletContext); } } }
在上面一段長(zhǎng)長(zhǎng)的注釋中可以看到,SpringServletContainerInitializer將@HandlesTypes(WebApplicationInitializer.class)標(biāo)注的所有WebApplicationInitializer這個(gè)類型的類都傳入到onStartup方法的Set參數(shù)中,并通過(guò)反射為這些WebApplicationInitializer類型的類創(chuàng)建實(shí)例;
④ 方法最后,每一個(gè)WebApplicationInitilizer實(shí)現(xiàn)調(diào)用自己onstartup方法
⑤ 而WebApplicationInitializer有個(gè)抽象實(shí)現(xiàn)類SpringBootServletInitializer(記住我們繼承了該抽象類),則會(huì)調(diào)用每一個(gè)WebApplicationInitializer實(shí)例(包括SpringBootServletInitializer)的onStartup方法:
public abstract class SpringBootServletInitializer implements WebApplicationInitializer { //other code... @Override public void onStartup(ServletContext servletContext) throws ServletException { // Logger initialization is deferred in case a ordered // LogServletContextInitializer is being used this.logger = LogFactory.getLog(getClass()); //創(chuàng)建IOC容器 WebApplicationContext rootAppContext = createRootApplicationContext( servletContext); if (rootAppContext != null) { servletContext.addListener(new ContextLoaderListener(rootAppContext) { @Override public void contextInitialized(ServletContextEvent event) { // no-op because the application context is already initialized } }); } else { this.logger.debug("No ContextLoaderListener registered, as " + "createRootApplicationContext() did not " + "return an application context"); } } protected WebApplicationContext createRootApplicationContext( ServletContext servletContext) { //創(chuàng)建Spring應(yīng)用構(gòu)建器,并進(jìn)行相關(guān)屬性設(shè)置 SpringApplicationBuilder builder = createSpringApplicationBuilder(); StandardServletEnvironment environment = new StandardServletEnvironment(); environment.initPropertySources(servletContext, null); builder.environment(environment); 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); //調(diào)用configure方法,創(chuàng)建war類型的web項(xiàng)目后,由于編寫SpringBootServletInitializer的子類重寫configure方法,所以此處調(diào)用的是我們定義的子類重寫的configure方法 builder = configure(builder); //通過(guò)構(gòu)建器構(gòu)建了一個(gè)Spring應(yīng)用 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"); // Ensure error pages are registered if (this.registerErrorPageFilter) { application.getSources().add(ErrorPageFilterConfiguration.class); } //啟動(dòng)Spring應(yīng)用 return run(application); } //Spring應(yīng)用啟動(dòng),創(chuàng)建并返回IOC容器 protected WebApplicationContext run(SpringApplication application) { return (WebApplicationContext) application.run(); } }
SpringBootServletInitializer實(shí)例執(zhí)行onStartup方法的時(shí)候會(huì)通過(guò)createRootApplicationContext方法來(lái)執(zhí)行run方法,接下來(lái)的過(guò)程就同以jar包形式啟動(dòng)的應(yīng)用的run過(guò)程一樣了,在內(nèi)部會(huì)創(chuàng)建IOC容器并返回,只是以war包形式的應(yīng)用在創(chuàng)建IOC容器過(guò)程中,不再創(chuàng)建Servlet容器了。
以上是“spring boot中servlet啟動(dòng)過(guò)程與原理的示例分析”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對(duì)大家有所幫助,如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道!