SpringBoot-SpringMVC的啟動(dòng)

1.Tomcat預(yù)留的接口

1.繼承ServletContainerInitializer
這個(gè)是啟動(dòng)會(huì)被調(diào)用的接口

public interface ServletContainerInitializer {

    /**
     * Receives notification during startup of a web application of the classes
     * within the web application that matched the criteria defined via the
     * {@link javax.servlet.annotation.HandlesTypes} annotation.
     *
     * @param c     The (possibly null) set of classes that met the specified
     *              criteria
     * @param ctx   The ServletContext of the web application in which the
     *              classes were discovered
     *
     * @throws ServletException If an error occurs
     */
    void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException;
}

2.在META-INF/services下增加
采用java的SPI機(jī)制加載邪意,參考org.springframework.spring-web.jar
javax.servlet.ServletContainerInitializer

org.springframework.web.SpringServletContainerInitializer

https://blog.csdn.net/weixin_38937840/article/details/107773717

tomcat和jetty切換原理
https://www.cnblogs.com/fanshuyao/p/8668059.html

2.SpringBoot自己創(chuàng)建容器

如果是jetty或者tomcat啟動(dòng),會(huì)有個(gè)配置

@Configuration(proxyBeanMethods = false)
class ServletWebServerFactoryConfiguration {
   
   // 判斷是否由tomcat儒飒,有則自動(dòng)注入tomcat相關(guān)的創(chuàng)建bean工廠
    @Configuration(proxyBeanMethods = false)
    @ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
    @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
    static class EmbeddedTomcat {
      
        @Bean
        TomcatServletWebServerFactory tomcatServletWebServerFactory(
                ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers,
                ObjectProvider<TomcatContextCustomizer> contextCustomizers,
                ObjectProvider<TomcatProtocolHandlerCustomizer<?>> protocolHandlerCustomizers) {
            TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
            return factory;
        }

    }

    // 判斷是否有jetty齿税,有則自動(dòng)注入jetty相關(guān)的創(chuàng)建bean工廠
    @Configuration(proxyBeanMethods = false)
    @ConditionalOnClass({ Servlet.class, Server.class, Loader.class, WebAppContext.class })
    @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
    static class EmbeddedJetty {

        @Bean
        JettyServletWebServerFactory JettyServletWebServerFactory(
                ObjectProvider<JettyServerCustomizer> serverCustomizers) {
            JettyServletWebServerFactory factory = new JettyServletWebServerFactory();
            factory.getServerCustomizers().addAll(serverCustomizers.orderedStream().collect(Collectors.toList()));
            return factory;
        }

    }

    // 判斷是否有Undertow,有則自動(dòng)注入jetty相關(guān)的創(chuàng)建bean工廠
    @Configuration(proxyBeanMethods = false)
    @ConditionalOnClass({ Servlet.class, Undertow.class, SslClientAuthMode.class })
    @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
    static class EmbeddedUndertow {

        @Bean
        UndertowServletWebServerFactory undertowServletWebServerFactory(
                ObjectProvider<UndertowDeploymentInfoCustomizer> deploymentInfoCustomizers,
                ObjectProvider<UndertowBuilderCustomizer> builderCustomizers) {
            UndertowServletWebServerFactory factory = new UndertowServletWebServerFactory();
            factory.getDeploymentInfoCustomizers()
                    .addAll(deploymentInfoCustomizers.orderedStream().collect(Collectors.toList()));
            factory.getBuilderCustomizers().addAll(builderCustomizers.orderedStream().collect(Collectors.toList()));
            return factory;
        }

        @Bean
        UndertowServletWebServerFactoryCustomizer undertowServletWebServerFactoryCustomizer(
                ServerProperties serverProperties) {
            return new UndertowServletWebServerFactoryCustomizer(serverProperties);
        }

    }

}

這個(gè)類(lèi)里面锻霎,根據(jù)類(lèi)的加載情況赤惊,會(huì)自動(dòng)注入容器的的創(chuàng)建工廠敲长。

啟動(dòng)流程:
1.springboot啟動(dòng)時(shí)候,判斷上下文


switch (webApplicationType) {
  case SERVLET:
    return new AnnotationConfigServletWebServerApplicationContext();
  case REACTIVE:
    return new AnnotationConfigReactiveWebServerApplicationContext();
  default:
       return new AnnotationConfigApplicationContext();
}

2.看類(lèi)的繼承


image.png

3.我們可以在父類(lèi)ServletWebServerApplicationContext中找到捡硅,這樣一個(gè)函數(shù)

    @Override
    protected void onRefresh() {
        super.onRefresh();
        try {
          // 創(chuàng)建web容器
            createWebServer();
        }
        catch (Throwable ex) {
            throw new ApplicationContextException("Unable to start web server", ex);
        }
    }

這里就是創(chuàng)建的web容器哮内,而這個(gè)函數(shù)會(huì)在spring刷新的時(shí)候被調(diào)用,這也是spring留給子類(lèi)的拓展點(diǎn)壮韭。

4.進(jìn)入這個(gè)函數(shù)

private void createWebServer() {
        WebServer webServer = this.webServer;
        ServletContext servletContext = getServletContext();
        if (webServer == null && servletContext == null) {
            // 獲取servletWebServerFactory
            ServletWebServerFactory factory = getWebServerFactory();
            createWebServer.tag("factory", factory.getClass().toString());
             // 創(chuàng)建web的服務(wù)器
            this.webServer = factory.getWebServer(getSelfInitializer());
            createWebServer.end();
            getBeanFactory().registerSingleton("webServerGracefulShutdown",
                    new WebServerGracefulShutdownLifecycle(this.webServer));
            getBeanFactory().registerSingleton("webServerStartStop",
                    new WebServerStartStopLifecycle(this, this.webServer));
        }
        else if (servletContext != null) {
            try {
              // 如果已經(jīng)創(chuàng)建了北发,就直接啟動(dòng)
                getSelfInitializer().onStartup(servletContext);
            }
            catch (ServletException ex) {
                throw new ApplicationContextException("Cannot initialize servlet context", ex);
            }
        }
        initPropertySources();
    }

4.這里的ServletWebServerFactory你會(huì)發(fā)現(xiàn)就是用于創(chuàng)建之前ServletWebServerFactoryConfiguration里面配置的tomcat、jetty這些的創(chuàng)建工廠喷屋,所以我們先看一下tomcat是如何實(shí)現(xiàn)getWebServer

5.創(chuàng)建tomcat

    @Override
    public WebServer getWebServer(ServletContextInitializer... initializers) {
        if (this.disableMBeanRegistry) {
            Registry.disableRegistry();
        }
        // 1.new tomcat對(duì)象
        Tomcat tomcat = new Tomcat();
        File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
        tomcat.setBaseDir(baseDir.getAbsolutePath());
        
       // 2.設(shè)置Connector
       Connector connector = new Connector(this.protocol);
        connector.setThrowOnFailure(true);
        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);
       
       // 創(chuàng)建Tomcat服務(wù)
        return getTomcatWebServer(tomcat);
    }

這里再getTomcatWebServer后面就是調(diào)用tomcat的start琳拨,真正的把tomcat啟動(dòng)了。

總結(jié):springboot對(duì)于內(nèi)置的tomcat或者jetty屯曹,其實(shí)是通過(guò)@Configuration狱庇,判斷是否有jetty或者tomcat的類(lèi)惊畏,找到后會(huì)注入一個(gè)創(chuàng)建tomcat或者jetty的工廠。在后續(xù)的spring創(chuàng)建的時(shí)候密任,會(huì)在OnRefresh的時(shí)候颜启,創(chuàng)建web容器。

4.WebMvcAutoConfiguration

springboot對(duì)于springmvc啟動(dòng)的配置類(lèi)浪讳,夢(mèng)開(kāi)始的地方..缰盏,通過(guò)里面的EnableWebMvcConfiguration,繼承了最終的WebMvcConfigurationSupport淹遵。這個(gè)Support類(lèi)乳规,就是會(huì)把SpringMVC需要的Bean注入到Spring中,例如:
RequestMappingHandlerAdapter(對(duì)參數(shù)的解析合呐,返回值解析,處理請(qǐng)求的類(lèi))笙以,

RequestMappingHandlerMapping(請(qǐng)求的處理器匹配器淌实,負(fù)責(zé)為請(qǐng)求找到合適的 HandlerExecutionChain 處理器執(zhí)行鏈,包含處理器(handler)和攔截器們(interceptors))

WebMvcAutoConfiguration這個(gè)類(lèi)里面有一個(gè)EnableWebMvcConfiguration猖腕,這個(gè)類(lèi)又繼承了DelegatingWebMvcConfiguration

下圖是這些類(lèi)的繼承關(guān)系:


image.png

然后我們看看DelegatingWebMvcConfiguration有什么東西拆祈,可以看到下面其實(shí)基本上都是回調(diào)WebMvcConfigurer的接口,而WebMvcConfigurer就是給我們?nèi)プ远x比如攔截器倘感,等等的地方放坏。

public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {

    private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();

   // 設(shè)置配置器
    @Autowired(required = false)
    public void setConfigurers(List<WebMvcConfigurer> configurers) {
        if (!CollectionUtils.isEmpty(configurers)) {
            this.configurers.addWebMvcConfigurers(configurers);
        }
    }

    @Override
    protected void addFormatters(FormatterRegistry registry) {
        this.configurers.addFormatters(registry);
    }
  
   // 添加攔截器
    @Override
    protected void addInterceptors(InterceptorRegistry registry) {
        this.configurers.addInterceptors(registry);
    }
   
   // .....
    
   // 添加參數(shù)解析器
    @Override
    protected void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        this.configurers.addArgumentResolvers(argumentResolvers);
    }
  

   //  添加消息轉(zhuǎn)換器
    @Override
    protected void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        this.configurers.configureMessageConverters(converters);
    }

}

所以如果我們想自定義一些攔截器就像下面這樣
自定義WebMvcConfigurer的例子:

@Slf4j
@Configuration
public class BootWebFilterConfiguration {

    @Bean
    WebMvcConfigurer webMvcConfigurer() {
        return new WebMvcConfigurer() {
            @Override
            public void addInterceptors(InterceptorRegistry registry) {、
                registry.addInterceptor(handlerInterceptor());
            }
        };
    }

    HandlerInterceptor handlerInterceptor() {
        return new HandlerInterceptor() {
            @Override
            public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
                log.info("jiang~~ preHandle");
                return true;
            }
        };
    }
}

5.寫(xiě)在最后

通過(guò)上面的代碼分析老玛,我們知道了SpringMVC在SpringBoot中是如何啟動(dòng)淤年,同時(shí)還有一個(gè)重要的地方,就是如何自定義WebMvcConfigurer蜡豹。以后再實(shí)現(xiàn)就不要去繼承WebMvcConfigurationSupport麸粮。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市镜廉,隨后出現(xiàn)的幾起案子弄诲,更是在濱河造成了極大的恐慌,老刑警劉巖娇唯,帶你破解...
    沈念sama閱讀 206,602評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件齐遵,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡塔插,警方通過(guò)查閱死者的電腦和手機(jī)梗摇,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)佑淀,“玉大人留美,你說(shuō)我怎么就攤上這事彰檬。” “怎么了谎砾?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,878評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵逢倍,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我景图,道長(zhǎng)较雕,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,306評(píng)論 1 279
  • 正文 為了忘掉前任挚币,我火速辦了婚禮亮蒋,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘妆毕。我一直安慰自己慎玖,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,330評(píng)論 5 373
  • 文/花漫 我一把揭開(kāi)白布笛粘。 她就那樣靜靜地躺著趁怔,像睡著了一般。 火紅的嫁衣襯著肌膚如雪薪前。 梳的紋絲不亂的頭發(fā)上润努,一...
    開(kāi)封第一講書(shū)人閱讀 49,071評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音示括,去河邊找鬼铺浇。 笑死,一個(gè)胖子當(dāng)著我的面吹牛垛膝,可吹牛的內(nèi)容都是我干的鳍侣。 我是一名探鬼主播,決...
    沈念sama閱讀 38,382評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼吼拥,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼拱她!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起扔罪,我...
    開(kāi)封第一講書(shū)人閱讀 37,006評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤秉沼,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后矿酵,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體唬复,經(jīng)...
    沈念sama閱讀 43,512評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,965評(píng)論 2 325
  • 正文 我和宋清朗相戀三年全肮,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了敞咧。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,094評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡辜腺,死狀恐怖休建,靈堂內(nèi)的尸體忽然破棺而出乍恐,到底是詐尸還是另有隱情,我是刑警寧澤测砂,帶...
    沈念sama閱讀 33,732評(píng)論 4 323
  • 正文 年R本政府宣布茵烈,位于F島的核電站,受9級(jí)特大地震影響砌些,放射性物質(zhì)發(fā)生泄漏呜投。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,283評(píng)論 3 307
  • 文/蒙蒙 一存璃、第九天 我趴在偏房一處隱蔽的房頂上張望仑荐。 院中可真熱鬧,春花似錦纵东、人聲如沸粘招。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,286評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)男图。三九已至,卻和暖如春甜橱,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背栈戳。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,512評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工岂傲, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人子檀。 一個(gè)月前我還...
    沈念sama閱讀 45,536評(píng)論 2 354
  • 正文 我出身青樓镊掖,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親褂痰。 傳聞我的和親對(duì)象是個(gè)殘疾皇子亩进,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,828評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容