1.Tomcat预留的接口
1.继承ServletContainerInitializer
这个是启动会被调用的接口
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机制加载,参考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自己创建容器
如果是jetty或者tomcat启动,会有个配置
@Configuration(proxyBeanMethods = false)
class ServletWebServerFactoryConfiguration {
// 判断是否由tomcat,有则自动注入tomcat相关的创建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,有则自动注入jetty相关的创建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,有则自动注入jetty相关的创建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);
}
}
}
这个类里面,根据类的加载情况,会自动注入容器的的创建工厂。
启动流程:
1.springboot启动时候,判断上下文
switch (webApplicationType) {
case SERVLET:
return new AnnotationConfigServletWebServerApplicationContext();
case REACTIVE:
return new AnnotationConfigReactiveWebServerApplicationContext();
default:
return new AnnotationConfigApplicationContext();
}
2.看类的继承
3.我们可以在父类ServletWebServerApplicationContext
中找到,这样一个函数
@Override
protected void onRefresh() {
super.onRefresh();
try {
// 创建web容器
createWebServer();
}
catch (Throwable ex) {
throw new ApplicationContextException("Unable to start web server", ex);
}
}
这里就是创建的web容器,而这个函数会在spring刷新的时候被调用,这也是spring留给子类的拓展点。
4.进入这个函数
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());
// 创建web的服务器
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 {
// 如果已经创建了,就直接启动
getSelfInitializer().onStartup(servletContext);
}
catch (ServletException ex) {
throw new ApplicationContextException("Cannot initialize servlet context", ex);
}
}
initPropertySources();
}
4.这里的ServletWebServerFactory
你会发现就是用于创建之前ServletWebServerFactoryConfiguration
里面配置的tomcat、jetty
这些的创建工厂,所以我们先看一下tomcat是如何实现getWebServer的
5.创建tomcat
@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {
if (this.disableMBeanRegistry) {
Registry.disableRegistry();
}
// 1.new tomcat对象
Tomcat tomcat = new Tomcat();
File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
tomcat.setBaseDir(baseDir.getAbsolutePath());
// 2.设置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);
// 创建Tomcat服务
return getTomcatWebServer(tomcat);
}
这里再getTomcatWebServer后面就是调用tomcat的start,真正的把tomcat启动了。
总结:springboot对于内置的tomcat或者jetty,其实是通过@Configuration,判断是否有jetty或者tomcat的类,找到后会注入一个创建tomcat或者jetty的工厂。在后续的spring创建的时候,会在OnRefresh的时候,创建web容器。
4.WebMvcAutoConfiguration
springboot对于springmvc启动的配置类,梦开始的地方..,通过里面的EnableWebMvcConfiguration
,继承了最终的WebMvcConfigurationSupport
。这个Support类,就是会把SpringMVC需要的Bean注入到Spring中,例如:
RequestMappingHandlerAdapter
(对参数的解析,返回值解析,处理请求的类),
RequestMappingHandlerMapping
(请求的处理器匹配器,负责为请求找到合适的 HandlerExecutionChain 处理器执行链,包含处理器(handler)和拦截器们(interceptors))
WebMvcAutoConfiguration这个类里面有一个EnableWebMvcConfiguration
,这个类又继承了DelegatingWebMvcConfiguration
下图是这些类的继承关系:
然后我们看看DelegatingWebMvcConfiguration
有什么东西,可以看到下面其实基本上都是回调WebMvcConfigurer
的接口,而WebMvcConfigurer
就是给我们去自定义比如拦截器,等等的地方。
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();
// 设置配置器
@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);
}
// .....
// 添加参数解析器
@Override
protected void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
this.configurers.addArgumentResolvers(argumentResolvers);
}
// 添加消息转换器
@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.写在最后
通过上面的代码分析,我们知道了SpringMVC在SpringBoot中是如何启动,同时还有一个重要的地方,就是如何自定义WebMvcConfigurer
。以后再实现就不要去继承WebMvcConfigurationSupport
。