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

  • 相关阅读:
    Swagger介绍
    mybatis-plus常用操作
    [比赛][蓝桥杯] 第十一届蓝桥杯第二次省赛C++A组
    [题目][蓝桥杯PREV-14] 高僧斗法
    [题目][蓝桥杯PREV] 大合集
    [题目][APIO2009][蓝桥杯ALGO-44] 采油区域
    [题目][蓝桥杯ALGO] 大合集
    [题目][NOIP2001][蓝桥杯ALGO-25] Car 的旅行路线
    [题目][蓝桥杯ALGO-22] 数的划分
    [知识点] 1.5.3 运算符重载
  • 原文地址:https://www.cnblogs.com/developerxiaofeng/p/9081689.html
Copyright © 2020-2023  润新知