博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
spring boot中servlet启动原理
阅读量:5317 次
发布时间:2019-06-14

本文共 21163 字,大约阅读时间需要 70 分钟。

启动过程及原理

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
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
() { 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
() { 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.     * 

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. *

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. *

Assuming that one or more {@code WebApplicationInitializer} types are detected, * they will be instantiated (and sorted 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

> webAppInitializerClasses, ServletContext servletContext) throws ServletException { List
initializers = new LinkedList
(); 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

你可能感兴趣的文章
Linux设备驱动--块设备(一)之概念和框架
查看>>
男性健身全攻略 男人4大方法快速塑形
查看>>
Qt5.4中遇到找不到头文件<QApplication>等。
查看>>
英特尔 QSV 在 FFMPEG 中的使用(Linux)
查看>>
爱上OpenCL的十个理由
查看>>
时序分析/约束(三)——Xilinx时钟资源 & ISE时序分析器
查看>>
从Git和SVN看版本控制
查看>>
Linux system log avahi-daemon[3733]: Invalid query packet
查看>>
python学习第十三节(sys,logging,logger,json)
查看>>
201303014001 张敏 计科高职13-1 github使用心得
查看>>
《面向对象程序设计》c++第四次作业___calculator plus
查看>>
adb Monkey用法
查看>>
Java EE 运用DBHelper连接数据库做一个简单的登录测试
查看>>
QTP零基础实战(二)
查看>>
python sys.argv[ ]
查看>>
DS博客作业01-日期抽象数据类型设计和实现
查看>>
HTML Jquery
查看>>
2018/2/20 Springretry,Feign,以及用通俗的语言(自认为)教会你关于Hystrix的复杂概念...
查看>>
数据结构实验8
查看>>
字体精讲
查看>>