首页 > 开发 > 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教程频道。
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表