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)的繼承
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)系:
然后我們看看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
麸粮。