基本實(shí)現(xiàn)說(shuō)明
spring-boot-starter-tomcat默認(rèn)會(huì)被spring-boot-starter所依賴(lài)褐啡,而spring-boot實(shí)現(xiàn)的巧妙方式在于,通過(guò)依賴(lài)判定當(dāng)前classpath下是否存在Tomcat類(lèi)來(lái)斷定锣杂,當(dāng)前web容器類(lèi)型也颤。
這種方式也是spring-boot實(shí)現(xiàn)通過(guò)配置來(lái)決定啟用何種服務(wù)的根本原理。
代碼分析
spring-boot容器的默認(rèn)超類(lèi)是EmbeddedWebApplicationContext
炭玫,而真正初始化容器的過(guò)程是調(diào)用createEmbeddedServletContainer
方法卵凑。
EmbeddedWebApplicationContext
private void createEmbeddedServletContainer() {
EmbeddedServletContainer localContainer = this.embeddedServletContainer;
ServletContext localServletContext = getServletContext();
if (localContainer == null && localServletContext == null) {
EmbeddedServletContainerFactory containerFactory = getEmbeddedServletContainerFactory();
// 從容器中查找實(shí)例,方法如下
this.embeddedServletContainer = containerFactory
.getEmbeddedServletContainer(getSelfInitializer());
}
else if (localServletContext != null) {
try {
getSelfInitializer().onStartup(localServletContext);
}
catch (ServletException ex) {
throw new ApplicationContextException("Cannot initialize servlet context",
ex);
}
}
initPropertySources();
}
protected EmbeddedServletContainerFactory getEmbeddedServletContainerFactory() {
// Use bean names so that we don't consider the hierarchy
String[] beanNames = getBeanFactory()
.getBeanNamesForType(EmbeddedServletContainerFactory.class);
....
....
}
即查找類(lèi)型為EmbeddedServletContainerFactory.class
的對(duì)象庆聘,而此對(duì)象的BeanDefinition
放入的時(shí)機(jī)則是通過(guò)配置來(lái)實(shí)現(xiàn)的。
其中有一個(gè)過(guò)程是通過(guò)EnableAutoConfigurationImportSelector
查找在在spring.factories中配置的所有類(lèi)型為org.springframework.boot.autoconfigure.EnableAutoConfiguration
的配置類(lèi)勺卢,而這些默認(rèn)實(shí)現(xiàn)類(lèi)在spring-boot-starter.autoconfigure
中伙判,截取部分如下
...
...
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration,\
org.springframework.boot.autoconfigure.elasticsearch.jest.JestAutoConfiguration,\org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration,\
org.springframework.boot.autoconfigure.jooq.JooqAutoConfiguration,\
org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration,\
org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration,\
org.springframework.boot.autoconfigure.mail.MailSenderValidatorAutoConfiguration,\
...
org.springframework.boot.autoconfigure.mobile.DeviceResolverAutoConfiguration,\
org.springframework.boot.autoconfigure.mobile.DeviceDelegatingViewResolverAutoConfiguration,\
org.springframework.boot.autoconfigure.mobile.SitePreferenceAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration,\
\
....
org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration,\
org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration,\
org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration,\
org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.web.HttpEncodingAutoConfiguration,\
org.springframework.boot.autoconfigure.web.HttpMessageConvertersAutoConfiguration,\
org.springframework.boot.autoconfigure.web.MultipartAutoConfiguration,\
org.springframework.boot.autoconfigure.web.ServerPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.web.WebClientAutoConfiguration,\
org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.WebSocketAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.WebSocketMessagingAutoConfiguration,\
org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration
...
查看其中的org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration
@ConditionalOnWebApplication
@Import(BeanPostProcessorsRegistrar.class)
public class 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();
}
}
@Configuration
@ConditionalOnClass({ Servlet.class, Server.class, Loader.class,
WebAppContext.class })
@ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT)
public static class EmbeddedJetty {
@Bean
public JettyEmbeddedServletContainerFactory jettyEmbeddedServletContainerFactory() {
return new JettyEmbeddedServletContainerFactory();
}
}
...
...
}
這個(gè)提供了默認(rèn)三種web容器,Tomcat,Jetty,Undertow黑忱,而@ConditionalOnClass
注解的配置決定了在什么情況下使用這個(gè)類(lèi)宴抚,類(lèi)似Profile
的功能。此條件判斷依據(jù)為當(dāng)前classpath容器中存在是否存在配置的class甫煞,以EmbeddedServletContainerAutoConfiguration$EmbeddedTomcat
來(lái)說(shuō)菇曲,它存在的依據(jù)在于當(dāng)前classpath存在Servlet類(lèi)和Tomcat類(lèi),而spring-boot默認(rèn)依賴(lài)Tomcat抚吠,所以EmbeddedTomcat
將被激活常潮,而EmbeddedServletContainerAutoConfiguration$EmbeddedJetty
將不會(huì)被采用。
所以楷力,如果你想要使用jetty容器喊式,僅僅需要將Tomcat依賴(lài)排除,并添加Jetty依賴(lài)即可萧朝,其他容器同理岔留。
此時(shí)容器中已經(jīng)確定了web容器,初始化的代碼就在TomcatEmbeddedServletContainerFactory#getEmbeddedServletContainer
中
public EmbeddedServletContainer getEmbeddedServletContainer(
ServletContextInitializer... initializers) {
Tomcat tomcat = new Tomcat();
File baseDir = (this.baseDirectory != null ? this.baseDirectory
: createTempDir("tomcat"));
tomcat.setBaseDir(baseDir.getAbsolutePath());
Connector connector = new Connector(this.protocol);
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);
return getTomcatEmbeddedServletContainer(tomcat);
}