spring boot内置tomcat启动

今天简单的学习一下spring boot内置tomcat的启动,以及相关bean的注入。今天主要看下比较重要的几个方法,因为这个三个方法有一定的关联性,所以就一起说一下。

this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
this.refreshContext(context);
this.afterRefresh(context, applicationArguments);

一、prepareContext

根据方法名称应该就知道这个方法主要是为应用context做一些准备工作

private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
        context.setEnvironment(environment);
        this.postProcessApplicationContext(context);
        this.applyInitializers(context);
        listeners.contextPrepared(context);
        if (this.logStartupInfo) {
            this.logStartupInfo(context.getParent() == null);
            this.logStartupProfileInfo(context);
        }

        context.getBeanFactory().registerSingleton("springApplicationArguments", applicationArguments);
        if (printedBanner != null) {
            context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
        }

        Set<Object> sources = this.getAllSources();
        Assert.notEmpty(sources, "Sources must not be empty");
        this.load(context, sources.toArray(new Object[0]));
        listeners.contextLoaded(context);
    }

这个方法中首先就是将环境添加到应用context容器,然后向context容器中的beanFactory(一个DefaultListableBeanFactory实例)注入一个"internalConfigurationBeanNameGenerator",即内部可配置的bean名称生成器,另外ResourceLoader和ClassLoader也会被注入到context中。此外一些initializer也会执行它们的initialize方法。另外还有一个就是发布ApplicationContextInitializedEvent和ApplicationPreparedEvent事件,由监听这些事件的监听器执行其onApplicationEvent方法。关于initialize和listener的作用在上次的spring boot启动分析(二)中有专门提到,这里就不再去分析这部分代码了。

二、refreshContext

这个方法会执行AbstractApplicationContext类的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();
            }

        }
    }

下面简单介绍一下上面几个方法的作用,代码分析是我在项目启动时通过debug模式跟踪出来的,可能不是特别的详细,有些方法也没有很深入的往下跟踪。

this.prepareRefresh();

这个方法是做的也是一些准备性质的工作,主要作用有几点:1、清楚classpath下的一些bean缓存(这个方法我没有很深入的去看,只是自己推测);2、设置context的启动和关闭状态;3、一些需要初始化的参数,在这之前会先获取到应用的配置文件,主要是下面这些:

[ConfigurationPropertySourcesPropertySource {name='configurationProperties'}, 
StubPropertySource {name='servletConfigInitParams'}, 
StubPropertySource {name='servletContextInitParams'}, 
MapPropertySource {name='systemProperties'}, 
OriginAwareSystemEnvironmentPropertySource {name='systemEnvironment'}, RandomValuePropertySource {name='random'}, 
OriginTrackedMapPropertySource {name='applicationConfig: [classpath:/application.properties]'}]

其实就是我们熟悉的Servlet的一些init参数配置,即servletContextInitParams和servletConfigInitParams这两个,如果发现有自定义的配置,则会用新的文件替换原有默认配置。

ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
this.prepareBeanFactory(beanFactory);

protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        beanFactory.setBeanClassLoader(this.getClassLoader());
        beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
        beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, this.getEnvironment()));
        beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
        beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
        beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
        beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
        beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
        beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
        beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
        beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
        beanFactory.registerResolvableDependency(ResourceLoader.class, this);
        beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
        beanFactory.registerResolvableDependency(ApplicationContext.class, this);
        beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));
        if (beanFactory.containsBean("loadTimeWeaver")) {
            beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
            beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
        }

        if (!beanFactory.containsLocalBean("environment")) {
            beanFactory.registerSingleton("environment", this.getEnvironment());
        }

        if (!beanFactory.containsLocalBean("systemProperties")) {
            beanFactory.registerSingleton("systemProperties", this.getEnvironment().getSystemProperties());
        }

        if (!beanFactory.containsLocalBean("systemEnvironment")) {
            beanFactory.registerSingleton("systemEnvironment", this.getEnvironment().getSystemEnvironment());
        }

    }

这两个方法中,第一个是获取最新的beanFactory,而prepareBeanFactory方法则是处理beanFctory的一些准备工作,主要包括:1、添加bean的类加载器和bean表达式解析器;2、忽略一些依赖接口(我也不明白指 的是什么);3、注册相关的可解析依赖;4、添加一些BeanPostProcessor;5、注册一些单例bean。以上应该是为后面beanFactory创建bean做的准备工作。

this.invokeBeanFactoryPostProcessors(beanFactory);
this.registerBeanPostProcessors(beanFactory);

主要是获取应用定义的bean的名称的集合然后将这些bean的名称放到beanFactory中的变量beanDefinitionNames中,另一个作用是向beanFactory的manualSingletonNames变量,添加一些bean。manualSingletonNames这个变量
保存的是手动单列对象名称(手动的意思即它不在spring的容器内)。其实这两个方法也是非常重要的,后面再继续深挖吧。


接下来就是刷新了,这里就会涉及到内置tomcat的初始化和执行,先往下看

this.onRefresh();
public WebServer getWebServer(ServletContextInitializer... initializers) {
      Tomcat tomcat = new Tomcat();
      File baseDir = this.baseDirectory != null ? this.baseDirectory : this.createTempDir("tomcat");
      tomcat.setBaseDir(baseDir.getAbsolutePath());
      Connector connector = new Connector(this.protocol);
      tomcat.getService().addConnector(connector);
      this.customizeConnector(connector);
      tomcat.setConnector(connector);
      tomcat.getHost().setAutoDeploy(false);
      this.configureEngine(tomcat.getEngine());
      Iterator var5 = this.additionalTomcatConnectors.iterator();
      while(var5.hasNext()) {
          Connector additionalConnector = (Connector)var5.next();
          tomcat.getService().addConnector(additionalConnector);
      }
      this.prepareContext(tomcat.getHost(), initializers);
      return this.getTomcatWebServer(tomcat);
}

这个方法先判断context的webServer是否为空,如果为空则会先创建TomcatServletWebServerFactory,然后调用其getWebServer方法,先创建Tomcat实例,并进行Tomcat的一些设置,最后将Tomcat实例和一个booleab值作为参数,调用TomcatWebServer的构造方法,而构造方法中又会调用TomcatWebServer的initialize方法。在initialize这个方法中context会添加关于生命周期的监听器,并启动tomcat,还有就是daemonAwaitThread创建和启动等等(这个方法应该是让tomcat处于等待状态)。之后会再次执行上面refreshContext方法中的servletContextInitParams和servletConfigInitParams两个配置更新,如果发现有自定义的配置,则会用新的文件替换原有默认配置,到这里这个方法就执行完成了。另外还会涉及到
ServletWebServerApplicationContext的selfInitialize方法,也就是context自身的初始化,这个方法应该是注入一些过滤器和disparcherServlet,比如characterEncodingFilter等,自己代码跟踪的不深,所以对这段代码还有点疑问。


this.finishBeanFactoryInitialization(beanFactory);
this.finishRefresh();

第一个方法是表示完成beanFactory的初始化,主要是beanFactory一些设置,比如ConversionService,还有冻结beanFactory配置,预装载单例bean等。这个方法的代码需要看DefaultListableBeanFactory方法,但是有些自己也不太清楚是什么意思,感觉还是有点吃力。
第二个方法表示完成刷新,对应的上面的onRefresh,这个方法先调用AbstractApplicationContext的finishRefresh方法,主要是清除resource的缓存信息,初始化LifecycleProcessor,即判断beanFactory中有没有LifecycleProcessor实例,没有则新建,然后作为单例注入beanFactory,并刷新context容器中的LifecycleProcessor,还有是发布ContextRefreshedEvent事件,然后启动监听该事件的监听器。之后会启动WebServer,最后发布ServletWebServerInitializedEvent事件,也会启动监听该事件的监听器。


三、afterRefresh

这个有点迷啊,这个方法内部没有任何代码,所以这个代码的意义在哪里呢???汗......

四、总结

prepareContext和refreshContext应该是整个spring boot启动的核心,自己只能简单的跟踪一下,了解一下大概的情况,很多更深层的代码还是需要一层一层的往下去看,很多地方自己还是一知半解,也可能自己看源码的方法不太正确,再加上对spring理解也不够,所以导致整个过程都不太顺利。不知道是不是先去看下别人源码学习之后再来学习源码比较好一点,还有一点就是自己看的源码没有注释,这一点影响也是蛮大的,好在自己已经下载了spring boot源码,以后学习起来希望能有帮助。好了,关于spring boot的源码学习到这里就暂时告一段落,自己也需要再去好好的消化一下学的的内容,如果有错误之处希望大家指正。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,753评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,668评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,090评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,010评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,054评论 6 395
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,806评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,484评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,380评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,873评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,021评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,158评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,838评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,499评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,044评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,159评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,449评论 3 374
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,136评论 2 356

推荐阅读更多精彩内容