溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

spring boot中servlet啟動過程與原理的示例分析

發(fā)布時間:2021-08-23 09:38:51 來源:億速云 閱讀:221 作者:小新 欄目:編程語言

小編給大家分享一下spring boot中servlet啟動過程與原理的示例分析,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!

啟動過程與原理:

1 spring boot 應(yī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)建一個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)建容器對象,并初始化容器,創(chuàng)建容器每一個組件)

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ā)中會獲取嵌入式Servlet容器工廠,由容器工廠創(chuàng)建Servlet

private void createEmbeddedServletContainer() {
 EmbeddedServletContainer localContainer = this.embeddedServletContainer;
 ServletContext localServletContext = getServletContext();
 if (localContainer == null && localServletContext == null) {
                //獲取嵌入式Servlet容器工廠
  EmbeddedServletContainerFactory containerFactory = getEmbeddedServletContainerFactory();
          //根據(jù)容器工廠獲取對應(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容器,具體使用哪一個容器工廠看配置環(huán)境依賴

this.embeddedServletContainer = containerFactory 
  .getEmbeddedServletContainer(getSelfInitializer());

10  上述創(chuàng)建過程  首先啟動IOC容器,接著啟動嵌入式Servlet容器,接著將IOC容器中剩下沒有創(chuàng)建的對象獲取出來,比如自己創(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);
  }

  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方法來通過反射將所有未創(chuàng)建的實(shí)例創(chuàng)建出來

  使用嵌入式Servlet容器:

     優(yōu)點(diǎn):   簡單,便攜

     缺點(diǎn):   默認(rèn)不支持jsp,優(yōu)化定制比較復(fù)雜

使用外置Servlet容器的步驟:

  1  必須創(chuàng)建war項目,需要劍豪web項目的目錄結(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  啟動服務(wù)器

jar包和war包啟動區(qū)別

    jar包:執(zhí)行SpringBootApplication的run方法,啟動IOC容器,然后創(chuàng)建嵌入式Servlet容器

 war包:  先是啟動Servlet服務(wù)器,服務(wù)器啟動Springboot應(yīng)用(springBootServletInitizer),然后啟動IOC容器

Servlet 3.0+規(guī)則

    1  服務(wù)器啟動(web應(yīng)用啟動),會創(chuàng)建當(dāng)前web應(yīng)用里面所有jar包里面的ServletContainerlnitializer實(shí)例

     2 ServletContainerInitializer的實(shí)現(xiàn)放在jar包的META-INF/services文件夾下

   3  還可以使用@HandlesTypes注解,在應(yīng)用啟動的時候加載指定的類。

外部Tomcat流程以及原理

 ?、?nbsp; 啟動Tomcat

  ②  根據(jù)上述描述的Servlet3.0+規(guī)則,可以在Spring的web模塊里面找到有個文件名為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.
  * <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類型創(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)用每一個WebApplicationInitializer實(shí)例的onstartup方法
  for (WebApplicationInitializer initializer : initializers) {
   initializer.onStartup(servletContext);
  }
 }
}

 在上面一段長長的注釋中可以看到,SpringServletContainerInitializer將@HandlesTypes(WebApplicationInitializer.class)標(biāo)注的所有WebApplicationInitializer這個類型的類都傳入到onStartup方法的Set參數(shù)中,并通過反射為這些WebApplicationInitializer類型的類創(chuàng)建實(shí)例;

 ?、?nbsp; 方法最后,每一個WebApplicationInitilizer實(shí)現(xiàn)調(diào)用自己onstartup方法

 ?、?nbsp; 而WebApplicationInitializer有個抽象實(shí)現(xiàn)類SpringBootServletInitializer(記住我們繼承了該抽象類),則會調(diào)用每一個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項目后,由于編寫SpringBootServletInitializer的子類重寫configure方法,所以此處調(diào)用的是我們定義的子類重寫的configure方法 
    builder = configure(builder); 
     
    //通過構(gòu)建器構(gòu)建了一個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); 
    } 
    //啟動Spring應(yīng)用 
    return run(application); 
  } 
   
  //Spring應(yīng)用啟動,創(chuàng)建并返回IOC容器 
  protected WebApplicationContext run(SpringApplication application) { 
    return (WebApplicationContext) application.run(); 
  }   
}

SpringBootServletInitializer實(shí)例執(zhí)行onStartup方法的時候會通過createRootApplicationContext方法來執(zhí)行run方法,接下來的過程就同以jar包形式啟動的應(yīng)用的run過程一樣了,在內(nèi)部會創(chuàng)建IOC容器并返回,只是以war包形式的應(yīng)用在創(chuàng)建IOC容器過程中,不再創(chuàng)建Servlet容器了。

以上是“spring boot中servlet啟動過程與原理的示例分析”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對大家有所幫助,如果還想學(xué)習(xí)更多知識,歡迎關(guān)注億速云行業(yè)資訊頻道!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI