SpringBoot启动流程简析(四)

来源:互联网 发布:免费hifi播放软件 编辑:程序博客网 时间:2024/06/03 11:53

在我们之前的web开发中,通常都是将应用打成war包或者将编译之后的应用放到tomcat的webapps目录下(其他的web服务器放到相应的目录下),但是我们在用SpringBoot进行web开发的时候,只是启动了一个main类,然后就会神奇的发现tomcat竟然也被启动了(SpringBoot也内置了Jetty),SpringBoot是怎么做到的呢?下面我将慢慢揭开它的神秘面纱:
我们之前说过在SpringBoot中web的上下文是AnnotationConfigEmbeddedWebApplicationContext这个类,我们先看简单的一下这个类的UML图:
UML
AnnotationConfigEmbeddedWebApplicationContext这个类继承了EmbeddedWebApplicationContext类,GenericWebApplicationContext类(这个要注意),它还实现了BeanDefinitionRegistry这个接口,还实现了ResourceLoader这个接口,这个类可以说是一个全能类了,这个个类我们先不多说,主要是看它的父类EmbeddedWebApplicationContext这个类。在这个类中重写了refresh方法、onRefresh方法、onClose方法、finishRefresh方法。这里我们先看onRefresh这个方法,

@Overrideprotected void onRefresh() {    //调用父类的onRefresh方法(GenericWebApplicationContext)    super.onRefresh();    try {        //创建嵌入式的Servlet容器        createEmbeddedServletContainer();    }    catch (Throwable ex) {        throw new ApplicationContextException("Unable to start embedded container",                ex);    }}

这里我们要重点分析的就是createEmbeddedServletContainer这个方法。

private void createEmbeddedServletContainer() {    //先获取embeddedServletContainer    EmbeddedServletContainer localContainer = this.embeddedServletContainer;    //获取Servlet上下文    ServletContext localServletContext = getServletContext();    //EmbeddedWebApplicationContext没有为EmbeddedServletContainer和ServletContext赋初值,也之前也没有调用set方法,所以这里都是为null    if (localContainer == null && localServletContext == null) {        //获取EmbeddedServletContainerFactory的实例 1)        EmbeddedServletContainerFactory containerFactory = getEmbeddedServletContainerFactory();        //获取EmbeddedServletContainer 2)        this.embeddedServletContainer = containerFactory                .getEmbeddedServletContainer(getSelfInitializer());        }    else if (localServletContext != null) {        try {            getSelfInitializer().onStartup(localServletContext);        }        catch (ServletException ex) {            throw new ApplicationContextException("Cannot initialize servlet context",                    ex);        }    }    //初始化属性信息3)    initPropertySources();}

我们先看1)处的代码:

    protected EmbeddedServletContainerFactory getEmbeddedServletContainerFactory() {        // Use bean names so that we don't consider the hierarchy        //从BeanFactory中获取EmbeddedServletContainerFactory类型的Bean,这里没有考虑父BeanFactory        String[] beanNames = getBeanFactory()                .getBeanNamesForType(EmbeddedServletContainerFactory.class);        //如果没有获取到EmbeddedServletContainerFactory类型的Bean,则抛出异常        if (beanNames.length == 0) {            throw new ApplicationContextException(                    "Unable to start EmbeddedWebApplicationContext due to missing "                            + "EmbeddedServletContainerFactory bean.");        }        //如果有一个以上的EmbeddedServletContainerFactory类型的Bean,则抛出异常        if (beanNames.length > 1) {            throw new ApplicationContextException(                    "Unable to start EmbeddedWebApplicationContext due to multiple "                            + "EmbeddedServletContainerFactory beans : "                            + StringUtils.arrayToCommaDelimitedString(beanNames));        }        //从BeanFactory中获取EmbeddedServletContainerFactory类型的Bean        return getBeanFactory().getBean(beanNames[0],                EmbeddedServletContainerFactory.class);    }

上面的这一段代码,干了这样三件事,查看Spring容器中是否有EmbeddedServletContainerFactory类型的Bean,EmbeddedServletContainerFactory类型的Bean是否多于一个,从Spring容器中获取EmbeddedServletContainerFactory类型的Bean,那么EmbeddedServletContainerFactory的Bean是什么时候被注入到Spring容器中的呢?我们先看一下EmbeddedServletContainerFactory的层次结构:
Web服务器
从上图中我们可以看到,在SpringBoot中为我们内置了三种Web服务器的实现类,TomCat、Jetty、Undertow(没接触过)。我们之前说SpringBoot的四大神器,其中之一是自动配置的功能,关于自动配置的内容我们不做更多的展开,在SpringBoot中有这样一个类EmbeddedServletContainerAutoConfiguration
这个类配置在spring.factories中,它的key为org.springframework.boot.autoconfigure.EnableAutoConfiguration,这里你需要知道,在SpringBoot启动的时候会加载这个类,并且这个类上带有Configuration这个注解,所以这个类会被注入到Spring容器中,然后这个类要生效还要有一个条件,即当前环境是web环境!在EmbeddedServletContainerAutoConfiguration中有这样的一段代码:

@Configuration@ConditionalOnClass({ Servlet.class, Tomcat.class })@ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT)public static class EmbeddedTomcat {    @Bean    public TomcatEmbeddedServletContainerFactory tomcatEmbeddedServletContainerFactory() {        return new TomcatEmbeddedServletContainerFactory();    }}

如果当前类路径下有Servlet和Tomcat这两个类,且在当前上下文中没有EmbeddedServletContainerFactory类型的Bean存在,则创建TomcatEmbeddedServletContainerFactory对象并注入到Spring容器中。SpringBoot中内置了TomCat相关的jar(spring-boot-starter-tomcat)。所以这里注入到Spring容器中的EmbeddedServletContainerFactory类型的Bean是TomcatEmbeddedServletContainerFactory。下面我们接着看2)处的代码:

this.embeddedServletContainer = containerFactory                    .getEmbeddedServletContainer(getSelfInitializer());

这里的containerFactory是TomcatEmbeddedServletContainerFactory的实例,所以这里调用的也是TomcatEmbeddedServletContainerFactory中的getEmbeddedServletContainer方法,

    @Override    public EmbeddedServletContainer getEmbeddedServletContainer(            ServletContextInitializer... initializers) {        //创建一个TomCat对象        Tomcat tomcat = new Tomcat();        //创建web容器base目录        File baseDir = (this.baseDirectory != null ? this.baseDirectory                : createTempDir("tomcat"));        tomcat.setBaseDir(baseDir.getAbsolutePath());        //创建一个连接器        Connector connector = new Connector(this.protocol);        //向tomcat的service中添加连接器        tomcat.getService().addConnector(connector);        //定制化Connector        customizeConnector(connector);        tomcat.setConnector(connector);        //自动部署设置为false        tomcat.getHost().setAutoDeploy(false);        //配置Engine        configureEngine(tomcat.getEngine());        //想tomcat中添加其他的Connector        for (Connector additionalConnector : this.additionalTomcatConnectors) {            tomcat.getService().addConnector(additionalConnector);        }        //准备ServletContext的上下文        prepareContext(tomcat.getHost(), initializers);        //创建TomcatEmbeddedServletContainer        return getTomcatEmbeddedServletContainer(tomcat);    }

上面的代码是不是看到很晕乎?准确的说上面的内容是TomCat的核心结构部分了。我们先从createTempDir这个简单的方法来开始:

protected File createTempDir(String prefix) {    try {        //创建临时文件夹        File tempDir = File.createTempFile(prefix + ".", "." + getPort());        //如果之前这个文件夹存在的话,则删除这个文件        tempDir.delete();        //创建临时文件夹        tempDir.mkdir();        //当虚拟机退出的时候删除这个临时文件夹        tempDir.deleteOnExit();        return tempDir;    }}

大家第一次看到TomCat这个类的时候是不是有点奇怪,怎么会有一个类叫做TomCat呢?既然敢叫TomCat,那肯定不一般,这个类也确实不一般,它整合了TomCat的整个生命周期,串联了TomCat的各个组件。我们接下来再继续说。

阅读全文
'); })();
0 0
原创粉丝点击
热门IT博客
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 气质男人霸气图片 气质名词解释 培养气质的书籍 怎样提高气质 气质怎么培养 有气质的女人 如何培养气质 个人魅力的养成与增强 一个简短而不失气质自我介绍 英文名字女生简单气质 如何提升自己的气质 气不足 阳气不足 肺气不足 中气不足怎么调理 中气不足吃什么药 理足气壮 中气不足什么药 肝气不足 肺气不足吃什么补得快 说话中气不足 胃气不足 肺气不足锻炼方法 气不足怎么补 说话气不足 肺气不足如何调理 肺气不足怎么调理 心气不足 阳气不足吃什么药 肺气不足有哪些症状 肝气不足症状 女性肺气不足吃食物 肾阳气不足的症状 无敌气运 快穿之气运宠儿 气运 万界之气运掠夺 气运掠夺系统 三国神话之气运之争 无限之气运主宰 混沌气运系统