您好,登錄后才能下訂單哦!
小編給大家分享一下spring boot中servlet啟動過程與原理的示例分析,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!
啟動過程與原理:
1 spring boot 應用啟動運行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); //創建一個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():創建IOC容器,如果是web應用則創建AnnotationConfigEmbeddedWebApplacation的IOC容器,如果不是,則創建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 { //根據應用環境,創建不同的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容器(創建容器對象,并初始化容器,創建容器每一個組件)
private void refreshContext(ConfigurableApplicationContext context) { refresh(context); if (this.registerShutdownHook) { try { context.registerShutdownHook(); } catch (AccessControlException ex) { // Not allowed in some environments. } } }
4 refresh(context);刷新剛才創建的IOC容器
protected void refresh(ApplicationContext applicationContext) { Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext); ((AbstractApplicationContext) applicationContext).refresh(); }
5 調用父類的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放啊發中會獲取嵌入式Servlet容器工廠,由容器工廠創建Servlet
private void createEmbeddedServletContainer() { EmbeddedServletContainer localContainer = this.embeddedServletContainer; ServletContext localServletContext = getServletContext(); if (localContainer == null && localServletContext == null) { //獲取嵌入式Servlet容器工廠 EmbeddedServletContainerFactory containerFactory = getEmbeddedServletContainerFactory(); //根據容器工廠獲取對應嵌入式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容器,具體使用哪一個容器工廠看配置環境依賴
this.embeddedServletContainer = containerFactory .getEmbeddedServletContainer(getSelfInitializer());
10 上述創建過程 首先啟動IOC容器,接著啟動嵌入式Servlet容器,接著將IOC容器中剩下沒有創建的對象獲取出來,比如自己創建的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); } List<String> beanNames = 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<Object>() { public Object run() { smartSingleton.afterSingletonsInstantiated(); return null; } }, this.getAccessControlContext()); } else { smartSingleton.afterSingletonsInstantiated(); } } } return; } beanName = (String)var2.next(); bd = this.getMergedLocalBeanDefinition(beanName); } while(bd.isAbstract()); } while(!bd.isSingleton()); } while(bd.isLazyInit()); if (this.isFactoryBean(beanName)) { final FactoryBean<?> factory = (FactoryBean)this.getBean("&" + beanName); boolean isEagerInit; if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) { isEagerInit = ((Boolean)AccessController.doPrivileged(new PrivilegedAction<Boolean>() { public Boolean run() { return ((SmartFactoryBean)factory).isEagerInit(); } }, this.getAccessControlContext())).booleanValue(); } else { isEagerInit = factory instanceof SmartFactoryBean && ((SmartFactoryBean)factory).isEagerInit(); } if (isEagerInit) { this.getBean(beanName); } } else { //注冊bean this.getBean(beanName); } } } }
是使用getBean方法來通過反射將所有未創建的實例創建出來
使用嵌入式Servlet容器:
優點: 簡單,便攜
缺點: 默認不支持jsp,優化定制比較復雜
使用外置Servlet容器的步驟:
1 必須創建war項目,需要劍豪web項目的目錄結構
2 嵌入式Tomcat依賴scope指定provided
3 編寫SpringBootServletInitializer類子類,并重寫configure方法
public class ServletInitializer extends SpringBootServletInitializer { @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { return application.sources(SpringBoot04WebJspApplication.class); } }
4 啟動服務器
jar包和war包啟動區別
jar包:執行SpringBootApplication的run方法,啟動IOC容器,然后創建嵌入式Servlet容器
war包: 先是啟動Servlet服務器,服務器啟動Springboot應用(springBootServletInitizer),然后啟動IOC容器
Servlet 3.0+規則
1 服務器啟動(web應用啟動),會創建當前web應用里面所有jar包里面的ServletContainerlnitializer實例
2 ServletContainerInitializer的實現放在jar包的META-INF/services文件夾下
3 還可以使用@HandlesTypes注解,在應用啟動的時候加載指定的類。
外部Tomcat流程以及原理
① 啟動Tomcat
② 根據上述描述的Servlet3.0+規則,可以在Spring的web模塊里面找到有個文件名為javax.servlet.ServletContainerInitializer的文件,而文件的內容為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. * <p>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. * <p>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. * <p>Assuming that one or more {@code WebApplicationInitializer} types are detected, * they will be instantiated (and <em>sorted</em> 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<Class<?>> webAppInitializerClasses, ServletContext servletContext) throws ServletException { List<WebApplicationInitializer> initializers = new LinkedList<WebApplicationInitializer>(); 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類型創建實例,并加入集合中 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); //調用每一個WebApplicationInitializer實例的onstartup方法 for (WebApplicationInitializer initializer : initializers) { initializer.onStartup(servletContext); } } }
在上面一段長長的注釋中可以看到,SpringServletContainerInitializer將@HandlesTypes(WebApplicationInitializer.class)標注的所有WebApplicationInitializer這個類型的類都傳入到onStartup方法的Set參數中,并通過反射為這些WebApplicationInitializer類型的類創建實例;
④ 方法最后,每一個WebApplicationInitilizer實現調用自己onstartup方法
⑤ 而WebApplicationInitializer有個抽象實現類SpringBootServletInitializer(記住我們繼承了該抽象類),則會調用每一個WebApplicationInitializer實例(包括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()); //創建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) { //創建Spring應用構建器,并進行相關屬性設置 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); //調用configure方法,創建war類型的web項目后,由于編寫SpringBootServletInitializer的子類重寫configure方法,所以此處調用的是我們定義的子類重寫的configure方法 builder = configure(builder); //通過構建器構建了一個Spring應用 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); } //啟動Spring應用 return run(application); } //Spring應用啟動,創建并返回IOC容器 protected WebApplicationContext run(SpringApplication application) { return (WebApplicationContext) application.run(); } }
SpringBootServletInitializer實例執行onStartup方法的時候會通過createRootApplicationContext方法來執行run方法,接下來的過程就同以jar包形式啟動的應用的run過程一樣了,在內部會創建IOC容器并返回,只是以war包形式的應用在創建IOC容器過程中,不再創建Servlet容器了。
以上是“spring boot中servlet啟動過程與原理的示例分析”這篇文章的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注億速云行業資訊頻道!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。