有了对springMVC的理解:SpringMVC原理讲述
1、Tomact启动
2、解析web.xml文件
3、DispatcherServlet实例化
4、DispatcherServlet对象.init方法,创建spring容器
5、接收请求,处理请求
springBoot中也使用了tomcat,只是和springMVC顺序不一样而已
用springBoot是先创建spring容器,再启动tomcat。
在springBoot中,比如说默认情况下使用的是tomcat,也可以使用Jetty、Undertow
只需要修改pom.xml文档中的依赖就可以。
在springApplication的run方法中
创建完spring容器之后,会去启动对应的webServer
在这里就会创建对应的服务(tomcat/jetty)
protected ServletWebServerFactory getWebServerFactory() {
// 获取spring容器里面ServletWebServerFactory类型的bean的名字(注意这里拿的不是bean对象)
//可能执行到这里的时候,对应的bean对象还没有创建出来,这里只是根据bean的定义来找到符合这个bean类型的所有名字
String[] beanNames = getBeanFactory().getBeanNamesForType(ServletWebServerFactory.class);
//没有拿到ServletWebServerFactory类型的名字,报错
if (beanNames.length == 0) {
throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to missing "
+ "ServletWebServerFactory bean.");
}
//拿到多个ServletWebServerFactory的名字,报错
if (beanNames.length > 1) {
throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to multiple "
+ "ServletWebServerFactory beans : " + StringUtils.arrayToCommaDelimitedString(beanNames));
}
//getbean,真正把bean对象创建出来
//基于性能的考虑,并不会在第一行代码的时候就把所有ServletWebServerFactory类型的bean都创建出来,只是拿到名字。
return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);
}
1.2 getWebServer方法
通过getWebServerFactory方法返回ServletWebServerFactory 后,会去创建对应的服务。
以Tomcat为例
public WebServer getWebServer(ServletContextInitializer... initializers) {
if (this.disableMBeanRegistry) {
Registry.disableRegistry();
}
Tomcat tomcat = new Tomcat();
File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
tomcat.setBaseDir(baseDir.getAbsolutePath());
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);
return getTomcatWebServer(tomcat);
}
protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
return new TomcatWebServer(tomcat, getPort() >= 0, getShutdown());
}
public TomcatWebServer(Tomcat tomcat, boolean autoStart, Shutdown shutdown) {
Assert.notNull(tomcat, "Tomcat Server must not be null");
this.tomcat = tomcat;
this.autoStart = autoStart;
this.gracefulShutdown = (shutdown == Shutdown.GRACEFUL) ? new GracefulShutdown(tomcat) : null;
initialize();
}
private void initialize() throws WebServerException {
synchronized (this.monitor) {
try {
addInstanceIdToEngineName();
Context context = findContext();
context.addLifecycleListener((event) -> {
if (context.equals(event.getSource()) && Lifecycle.START_EVENT.equals(event.getType())) {
// Remove service connectors so that protocol binding doesn't
// happen when the service is started.
removeServiceConnectors();
}
});
// 启动Tomcat
this.tomcat.start();
rethrowDeferredStartupExceptions();
try {
ContextBindings.bindClassLoader(context, context.getNamingToken(), getClass().getClassLoader());
}catch (NamingException ex) {}
startDaemonAwaitThread();
}catch (Exception ex) {
stopSilently();
destroySilently();
throw new WebServerException("Unable to start embedded Tomcat", ex);
}
}
}
1.3 ServletWebServerFactory类型的bean定义
代码看到这里,我们认为在默认的情况下,在使用springBoot时就应该有一个ServletWebServerFactory类型的bean,不然在getWebServerFactory方法中会报错。
依然以Tomcat为例,在springBoot源码中是否有某个地方定义了对应类型的bean呢?
@Configuration(proxyBeanMethods = false)
class ServletWebServerFactoryConfiguration {
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
static class EmbeddedTomcat {
@Bean
TomcatServletWebServerFactory tomcatServletWebServerFactory(
ObjectProvider connectorCustomizers,
ObjectProvider contextCustomizers,
ObjectProvider> protocolHandlerCustomizers) {
TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
factory.getTomcatConnectorCustomizers()
.addAll(connectorCustomizers.orderedStream().collect(Collectors.toList()));
factory.getTomcatContextCustomizers()
.addAll(contextCustomizers.orderedStream().collect(Collectors.toList()));
factory.getTomcatProtocolHandlerCustomizers()
.addAll(protocolHandlerCustomizers.orderedStream().collect(Collectors.toList()));
return factory;
}
}
@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 serverCustomizers) {
JettyServletWebServerFactory factory = new JettyServletWebServerFactory();
factory.getServerCustomizers().addAll(serverCustomizers.orderedStream().collect(Collectors.toList()));
return factory;
}
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ Servlet.class, Undertow.class, SslClientAuthMode.class })
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
static class EmbeddedUndertow {
@Bean
UndertowServletWebServerFactory undertowServletWebServerFactory(
ObjectProvider deploymentInfoCustomizers,
ObjectProvider 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);
}
}
}
在ServletWebServerFactoryConfiguration 类中,3个类型的bean都定义了,不会报错么?
可以看到,每个类型的bean都是定义在EmbeddedXXX 这个内部类里面,每次只会解析一个内部类的内容。
怎么决定是否需要解析对应的内部类里面的内容呢?
首先需要判断对应的内部类是否符合条件(也就是ConditionalOnClass注解对应的内容)
每个对应的条件都是不一样的。
Tomcat:@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
jeety:@ConditionalOnClass({ Servlet.class, Server.class, Loader.class, WebAppContext.class })
Undertow:@ConditionalOnClass({ Servlet.class, Undertow.class, SslClientAuthMode.class })
1.4 @ConditionalOnClass注解底层原理到这里,可能会有个问题。
平时在使用springBoot的时候,在整个项目里面,可能没有jeety的依赖,也没有Undertow的依赖,执行到注解这里的时候不会报错么?
首先我们了解到的现状就是:虽然没有对应的jeety/Undertow依赖,springBoot也正常使用运行了,这是为什么呢?
报错分为两部分:编译报错和运行报错。
编译报错:指的是自己的项目编译,使用的springBoot.jar(jar包里面是class文件)是已经编译好的,在使用ServletWebServerFactoryConfiguration 这个类的时候就是个class,是不需要再次编译下这个类。
运行报错:运行的时候,先拿到对应的内部类,通过反射拿到类上面的注解,那就需要加载注解里面的class类,那这个时候就会报错?
可以先大概想一下:通常在spring中,我们都会指定一个扫描路径,这里路径下面会存在很多类,那么对应spring来说,扫描主要是做什么?



