继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

spring boot中servlet启动原理

烙印99
关注TA
已关注
手记 235
粉丝 92
获赞 446

启动过程及原理

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容器了。

原文出处

打开App,阅读手记
1人推荐
发表评论
随时随地看视频慕课网APP