zoukankan      html  css  js  c++  java
  • SpringBoot 源码解析 (六)----- Spring Boot的核心能力

    Spring Boot默认使用Tomcat作为嵌入式的Servlet容器,只要引入了spring-boot-start-web依赖,则默认是用Tomcat作为Servlet容器:

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    Servlet容器的使用

    默认servlet容器

    我们看看spring-boot-starter-web这个starter中有什么

    核心就是引入了tomcat和SpringMvc,我们先来看tomcat

    Spring Boot默认支持Tomcat,Jetty,和Undertow作为底层容器。如图:

    而Spring Boot默认使用Tomcat,一旦引入spring-boot-starter-web模块,就默认使用Tomcat容器。

    切换servlet容器

    那如果我么想切换其他Servlet容器呢,只需如下两步:

    • 将tomcat依赖移除掉
    • 引入其他Servlet容器依赖

    引入jetty:

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <exclusions>
            <exclusion>
                <!--移除spring-boot-starter-web中的tomcat-->
                <artifactId>spring-boot-starter-tomcat</artifactId>
                <groupId>org.springframework.boot</groupId>
            </exclusion>
        </exclusions>
    </dependency>
    
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <!--引入jetty-->
        <artifactId>spring-boot-starter-jetty</artifactId>
    </dependency>

    Servlet容器自动配置原理

    EmbeddedServletContainerAutoConfiguration

    其中EmbeddedServletContainerAutoConfiguration是嵌入式Servlet容器的自动配置类,该类在spring-boot-autoconfigure.jar中的web模块可以找到。

    我们可以看到EmbeddedServletContainerAutoConfiguration被配置在spring.factories中,看过我前面文章的朋友应该知道SpringBoot自动配置的原理,这里将EmbeddedServletContainerAutoConfiguration配置类加入到IOC容器中,接着我们来具体看看这个配置类:

    @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
    @Configuration
    @ConditionalOnWebApplication// 在Web环境下才会起作用
    @Import(BeanPostProcessorsRegistrar.class)// 会Import一个内部类BeanPostProcessorsRegistrar
    public class EmbeddedServletContainerAutoConfiguration {
    
        @Configuration
        // Tomcat类和Servlet类必须在classloader中存在
        // 文章开头我们已经导入了web的starter,其中包含tomcat和SpringMvc
        // 那么classPath下会存在Tomcat.class和Servlet.class
        @ConditionalOnClass({ Servlet.class, Tomcat.class })
        // 当前Spring容器中不存在EmbeddedServletContainerFactory类型的实例
        @ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT)
        public static class EmbeddedTomcat {
    
            @Bean
            public TomcatEmbeddedServletContainerFactory tomcatEmbeddedServletContainerFactory() {
                // 上述条件注解成立的话就会构造TomcatEmbeddedServletContainerFactory这个EmbeddedServletContainerFactory
                return new TomcatEmbeddedServletContainerFactory();
            }
        }
        
        @Configuration
        @ConditionalOnClass({ Servlet.class, Server.class, Loader.class,
                WebAppContext.class })
        @ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT)
        public static class EmbeddedJetty {
    
            @Bean
            public JettyEmbeddedServletContainerFactory jettyEmbeddedServletContainerFactory() {
                return new JettyEmbeddedServletContainerFactory();
            }
    
        }
        
        @Configuration
        @ConditionalOnClass({ Servlet.class, Undertow.class, SslClientAuthMode.class })
        @ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT)
        public static class EmbeddedUndertow {
    
            @Bean
            public UndertowEmbeddedServletContainerFactory undertowEmbeddedServletContainerFactory() {
                return new UndertowEmbeddedServletContainerFactory();
            }
    
        }
        
        //other code...
    }

    在这个自动配置类中配置了三个容器工厂的Bean,分别是:

    • TomcatEmbeddedServletContainerFactory

    • JettyEmbeddedServletContainerFactory

    • UndertowEmbeddedServletContainerFactory

    这里以大家熟悉的Tomcat为例,首先Spring Boot会判断当前环境中是否引入了Servlet和Tomcat依赖,并且当前容器中没有自定义的EmbeddedServletContainerFactory的情况下,则创建Tomcat容器工厂。其他Servlet容器工厂也是同样的道理。

    EmbeddedServletContainerFactory

    • 嵌入式Servlet容器工厂
    public interface EmbeddedServletContainerFactory {
    
        EmbeddedServletContainer getEmbeddedServletContainer( ServletContextInitializer... initializers);
    }

    内部只有一个方法,用于获取嵌入式的Servlet容器。

    该工厂接口主要有三个实现类,分别对应三种嵌入式Servlet容器的工厂类,如图所示:

    TomcatEmbeddedServletContainerFactory

    以Tomcat容器工厂TomcatEmbeddedServletContainerFactory类为例:

    public class TomcatEmbeddedServletContainerFactory extends AbstractEmbeddedServletContainerFactory implements ResourceLoaderAware {
        
        //other code...
        
        @Override
        public EmbeddedServletContainer getEmbeddedServletContainer( ServletContextInitializer... initializers) {
            //创建一个Tomcat
            Tomcat tomcat = new Tomcat();
            
           //配置Tomcat的基本环节
            File baseDir = (this.baseDirectory != null ? this.baseDirectory: createTempDir("tomcat"));
            tomcat.setBaseDir(baseDir.getAbsolutePath());
            Connector connector = new Connector(this.protocol);
            tomcat.getService().addConnector(connector);
            customizeConnector(connector);
            tomcat.setConnector(connector);
            tomcat.getHost().setAutoDeploy(false);
            configureEngine(tomcat.getEngine());
            for (Connector additionalConnector : this.additionalTomcatConnectors) {
                tomcat.getService().addConnector(additionalConnector);
            }
            prepareContext(tomcat.getHost(), initializers);
            
            //包装tomcat对象,返回一个嵌入式Tomcat容器,内部会启动该tomcat容器
            return getTomcatEmbeddedServletContainer(tomcat);
        }
    }

    首先会创建一个Tomcat的对象,并设置一些属性配置,最后调用getTomcatEmbeddedServletContainer(tomcat)方法,内部会启动tomcat,我们来看看:

    protected TomcatEmbeddedServletContainer getTomcatEmbeddedServletContainer(
        Tomcat tomcat) {
        return new TomcatEmbeddedServletContainer(tomcat, getPort() >= 0);
    }

    该函数很简单,就是来创建Tomcat容器并返回。看看TomcatEmbeddedServletContainer类:

    public class TomcatEmbeddedServletContainer implements EmbeddedServletContainer {
    
        public TomcatEmbeddedServletContainer(Tomcat tomcat, boolean autoStart) {
            Assert.notNull(tomcat, "Tomcat Server must not be null");
            this.tomcat = tomcat;
            this.autoStart = autoStart;
            
            //初始化嵌入式Tomcat容器,并启动Tomcat
            initialize();
        }
        
        private void initialize() throws EmbeddedServletContainerException {
            TomcatEmbeddedServletContainer.logger
                    .info("Tomcat initialized with port(s): " + getPortsDescription(false));
            synchronized (this.monitor) {
                try {
                    addInstanceIdToEngineName();
                    try {
                        final Context context = findContext();
                        context.addLifecycleListener(new LifecycleListener() {
    
                            @Override
                            public void lifecycleEvent(LifecycleEvent event) {
                                if (context.equals(event.getSource())
                                        && Lifecycle.START_EVENT.equals(event.getType())) {
                                    // Remove service connectors so that protocol
                                    // binding doesn't happen when the service is
                                    // started.
                                    removeServiceConnectors();
                                }
                            }
    
                        });
    
                        // Start the server to trigger initialization listeners
                        //启动tomcat
                        this.tomcat.start();
    
                        // We can re-throw failure exception directly in the main thread
                        rethrowDeferredStartupExceptions();
    
                        try {
                            ContextBindings.bindClassLoader(context, getNamingToken(context),
                                    getClass().getClassLoader());
                        }
                        catch (NamingException ex) {
                            // Naming is not enabled. Continue
                        }
    
                        // Unlike Jetty, all Tomcat threads are daemon threads. We create a
                        // blocking non-daemon to stop immediate shutdown
                        startDaemonAwaitThread();
                    }
                    catch (Exception ex) {
                        containerCounter.decrementAndGet();
                        throw ex;
                    }
                }
                catch (Exception ex) {
                    stopSilently();
                    throw new EmbeddedServletContainerException(
                            "Unable to start embedded Tomcat", ex);
                }
            }
        }
    }

    到这里就启动了嵌入式的Servlet容器,其他容器类似。

    Servlet容器启动原理

    SpringBoot启动过程

    我们回顾一下前面讲解的SpringBoot启动过程,也就是run方法:

    public ConfigurableApplicationContext run(String... args) {
        // 计时工具
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
    
        ConfigurableApplicationContext context = null;
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
    
        configureHeadlessProperty();
    
        // 第一步:获取并启动监听器
        SpringApplicationRunListeners listeners = getRunListeners(args);
        listeners.starting();
        
        try {
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
    
            // 第二步:根据SpringApplicationRunListeners以及参数来准备环境
            ConfigurableEnvironment environment = prepareEnvironment(listeners,applicationArguments);
            configureIgnoreBeanInfo(environment);
    
            // 准备Banner打印器 - 就是启动Spring Boot的时候打印在console上的ASCII艺术字体
            Banner printedBanner = printBanner(environment);
    
            // 第三步:创建Spring容器
            context = createApplicationContext();
    
            exceptionReporters = getSpringFactoriesInstances(
                    SpringBootExceptionReporter.class,
                    new Class[] { ConfigurableApplicationContext.class }, context);
    
            // 第四步:Spring容器前置处理
            prepareContext(context, environment, listeners, applicationArguments,printedBanner);
    
            // 第五步:刷新容器
            refreshContext(context);
    
         // 第六步:Spring容器后置处理
            afterRefresh(context, applicationArguments);
    
          // 第七步:发出结束执行的事件
            listeners.started(context);
            // 第八步:执行Runners
            this.callRunners(context, applicationArguments);
            stopWatch.stop();
            // 返回容器
            return context;
        }
        catch (Throwable ex) {
            handleRunFailure(context, listeners, exceptionReporters, ex);
            throw new IllegalStateException(ex);
        }
    }

    我们回顾一下第三步:创建Spring容器

    public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context."
                + "annotation.AnnotationConfigApplicationContext";
    
    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);
            }
        }
        return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass);
    }
    创建IOC容器,如果是web应用,则创建AnnotationConfigEmbeddedWebApplicationContext的IOC容器;如果不是,则创建AnnotationConfigApplicationContext的IOC容器;很明显我们创建的容器是AnnotationConfigEmbeddedWebApplicationContext接着我们来看看第五步,刷新容器refreshContext(context);
    private void refreshContext(ConfigurableApplicationContext context) {
        refresh(context);
    }
    
    protected void refresh(ApplicationContext applicationContext) {
        Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
        //调用容器的refresh()方法刷新容器
        ((AbstractApplicationContext) applicationContext).refresh();
    }

    容器刷新过程

    调用抽象父类AbstractApplicationContext的refresh()方法;

    AbstractApplicationContext

     1 public void refresh() throws BeansException, IllegalStateException {
     2     synchronized (this.startupShutdownMonitor) {
     3         /**
     4          * 刷新上下文环境
     5          */
     6         prepareRefresh();
     7 
     8         /**
     9          * 初始化BeanFactory,解析XML,相当于之前的XmlBeanFactory的操作,
    10          */
    11         ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
    12 
    13         /**
    14          * 为上下文准备BeanFactory,即对BeanFactory的各种功能进行填充,如常用的注解@Autowired @Qualifier等
    15          * 添加ApplicationContextAwareProcessor处理器
    16          * 在依赖注入忽略实现*Aware的接口,如EnvironmentAware、ApplicationEventPublisherAware等
    17          * 注册依赖,如一个bean的属性中含有ApplicationEventPublisher(beanFactory),则会将beanFactory的实例注入进去
    18          */
    19         prepareBeanFactory(beanFactory);
    20 
    21         try {
    22             /**
    23              * 提供子类覆盖的额外处理,即子类处理自定义的BeanFactoryPostProcess
    24              */
    25             postProcessBeanFactory(beanFactory);
    26 
    27             /**
    28              * 激活各种BeanFactory处理器,包括BeanDefinitionRegistryBeanFactoryPostProcessor和普通的BeanFactoryPostProcessor
    29              * 执行对应的postProcessBeanDefinitionRegistry方法 和  postProcessBeanFactory方法
    30              */
    31             invokeBeanFactoryPostProcessors(beanFactory);
    32 
    33             /**
    34              * 注册拦截Bean创建的Bean处理器,即注册BeanPostProcessor,不是BeanFactoryPostProcessor,注意两者的区别
    35              * 注意,这里仅仅是注册,并不会执行对应的方法,将在bean的实例化时执行对应的方法
    36              */
    37             registerBeanPostProcessors(beanFactory);
    38 
    39             /**
    40              * 初始化上下文中的资源文件,如国际化文件的处理等
    41              */
    42             initMessageSource();
    43 
    44             /**
    45              * 初始化上下文事件广播器,并放入applicatioEventMulticaster,如ApplicationEventPublisher
    46              */
    47             initApplicationEventMulticaster();
    48 
    49             /**
    50              * 给子类扩展初始化其他Bean
    51              */
    52             onRefresh();
    53 
    54             /**
    55              * 在所有bean中查找listener bean,然后注册到广播器中
    56              */
    57             registerListeners();
    58 
    59             /**
    60              * 设置转换器
    61              * 注册一个默认的属性值解析器
    62              * 冻结所有的bean定义,说明注册的bean定义将不能被修改或进一步的处理
    63              * 初始化剩余的非惰性的bean,即初始化非延迟加载的bean
    64              */
    65             finishBeanFactoryInitialization(beanFactory);
    66 
    67             /**
    68              * 通过spring的事件发布机制发布ContextRefreshedEvent事件,以保证对应的监听器做进一步的处理
    69              * 即对那种在spring启动后需要处理的一些类,这些类实现了ApplicationListener<ContextRefreshedEvent>,
    70              * 这里就是要触发这些类的执行(执行onApplicationEvent方法)
    71              * spring的内置Event有ContextClosedEvent、ContextRefreshedEvent、ContextStartedEvent、ContextStoppedEvent、RequestHandleEvent
    72              * 完成初始化,通知生命周期处理器lifeCycleProcessor刷新过程,同时发出ContextRefreshEvent通知其他人
    73              */
    74             finishRefresh();
    75         }
    76 
    77         finally {
    78     
    79             resetCommonCaches();
    80         }
    81     }
    82 }

    我们看第52行的方法:

    protected void onRefresh() throws BeansException {
    
    }

    很明显抽象父类AbstractApplicationContext中的onRefresh是一个空方法,并且使用protected修饰,也就是其子类可以重写onRefresh方法,那我们看看其子类AnnotationConfigEmbeddedWebApplicationContext中的onRefresh方法是如何重写的,AnnotationConfigEmbeddedWebApplicationContext又继承EmbeddedWebApplicationContext,如下:

    public class AnnotationConfigEmbeddedWebApplicationContext extends EmbeddedWebApplicationContext {

    那我们看看其父类EmbeddedWebApplicationContext 是如何重写onRefresh方法的:

    EmbeddedWebApplicationContext

    @Override
    protected void onRefresh() {
        super.onRefresh();
        try {
            //核心方法:会获取嵌入式的Servlet容器工厂,并通过工厂来获取Servlet容器
            createEmbeddedServletContainer();
        }
        catch (Throwable ex) {
            throw new ApplicationContextException("Unable to start embedded container", ex);
        }
    }

    在createEmbeddedServletContainer方法中会获取嵌入式的Servlet容器工厂,并通过工厂来获取Servlet容器:

     1 private void createEmbeddedServletContainer() {
     2     EmbeddedServletContainer localContainer = this.embeddedServletContainer;
     3     ServletContext localServletContext = getServletContext();
     4     if (localContainer == null && localServletContext == null) {
     5         //先获取嵌入式Servlet容器工厂
     6         EmbeddedServletContainerFactory containerFactory = getEmbeddedServletContainerFactory();
     7         //根据容器工厂来获取对应的嵌入式Servlet容器
     8         this.embeddedServletContainer = containerFactory.getEmbeddedServletContainer(getSelfInitializer());
     9     }
    10     else if (localServletContext != null) {
    11         try {
    12             getSelfInitializer().onStartup(localServletContext);
    13         }
    14         catch (ServletException ex) {
    15             throw new ApplicationContextException("Cannot initialize servlet context",ex);
    16         }
    17     }
    18     initPropertySources();
    19 }

    关键代码在第6和第8行,先获取Servlet容器工厂,然后根据容器工厂来获取对应的嵌入式Servlet容器

    获取Servlet容器工厂

    protected EmbeddedServletContainerFactory getEmbeddedServletContainerFactory() {
        //从Spring的IOC容器中获取EmbeddedServletContainerFactory.class类型的Bean
        String[] beanNames = getBeanFactory().getBeanNamesForType(EmbeddedServletContainerFactory.class);
        //调用getBean实例化EmbeddedServletContainerFactory.class
        return getBeanFactory().getBean(beanNames[0], EmbeddedServletContainerFactory.class);
    }

    我们看到先从Spring的IOC容器中获取EmbeddedServletContainerFactory.class类型的Bean,然后调用getBean实例化EmbeddedServletContainerFactory.class,大家还记得我们第一节Servlet容器自动配置类EmbeddedServletContainerAutoConfiguration中注入Spring容器的对象是什么吗?当我们引入spring-boot-starter-web这个启动器后,会注入TomcatEmbeddedServletContainerFactory这个对象到Spring容器中,所以这里获取到的Servlet容器工厂是TomcatEmbeddedServletContainerFactory,然后调用

    TomcatEmbeddedServletContainerFactory的getEmbeddedServletContainer方法获取Servlet容器,并且启动Tomcat,大家可以看看文章开头的getEmbeddedServletContainer方法。

    大家看一下第8行代码获取Servlet容器方法的参数getSelfInitializer(),这是个啥?我们点进去看看

    private ServletContextInitializer getSelfInitializer() {
        //创建一个ServletContextInitializer对象,并重写onStartup方法,很明显是一个回调方法
        return new ServletContextInitializer() {
            public void onStartup(ServletContext servletContext) throws ServletException {
                EmbeddedWebApplicationContext.this.selfInitialize(servletContext);
            }
        };
    }

    创建一个ServletContextInitializer对象,并重写onStartup方法,很明显是一个回调方法,这里给大家留一点疑问:

    • ServletContextInitializer对象创建过程是怎样的?
    • onStartup是何时调用的?
    • onStartup方法的作用是什么?

    ServletContextInitializer是 Servlet 容器初始化的时候,提供的初始化接口。这里涉及到Servlet、Filter实例的注册,我们留在下一篇具体讲

  • 相关阅读:
    南邮NOJ整除的尾数
    【HDOJ】2844 Coins
    【HDOJ】2546 饭卡
    【HDOJ】1031 Design T-Shirt
    【HDOJ】1983 Kaitou Kid
    【HDOJ】2612 Find a way
    【原创】如何构建MIPS交叉编译工具链
    【HDOJ】1239 Calling Extraterrestrial Intelligence Again
    【Python Network】使用DOM生成XML
    【HDOJ】2602 Bone Collector
  • 原文地址:https://www.cnblogs.com/java-chen-hao/p/11837057.html
Copyright © 2011-2022 走看看