国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 開發 > Java > 正文

深入講解spring boot中servlet的啟動過程與原理

2024-07-14 08:41:25
字體:
來源:轉載
供稿:網友

前言

本文主要介紹了關于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容器了。

總結

以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對VeVb武林網的支持。


注:相關教程知識閱讀請移步到JAVA教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 郎溪县| 崇明县| 崇州市| 固始县| 崇仁县| 长沙市| 综艺| 吉林市| 辉县市| 成都市| 同德县| 汝阳县| 吉首市| 桃园县| 临武县| 宣威市| 潮州市| 紫阳县| 达孜县| 泾川县| 宣恩县| 济阳县| 利辛县| 蒙城县| 延安市| 闻喜县| 蒙山县| 瓦房店市| 纳雍县| 沙河市| 东平县| 乌兰县| 太仆寺旗| 广饶县| 德州市| 滦南县| 嘉黎县| 余干县| 枝江市| 碌曲县| 集贤县|