软件编程
位置:首页>> 软件编程>> java编程>> 深入讲解spring boot中servlet的启动过程与原理

深入讲解spring boot中servlet的启动过程与原理

作者:期待华丽转身  发布时间:2022-08-19 00:18:40 

标签:springboot,servlet,启动

前言

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

来源:https://www.cnblogs.com/developerxiaofeng/p/9081689.html

0
投稿

猜你喜欢

手机版 软件编程 asp之家 www.aspxhome.com