1. 前言
在使用SpringBoot框架开发web应用程序,只是了解DispatcherServlet
作为web请求的入口,却不知道请求是如何转发给DispatcherServlet
以及请求在转发给DispatcherServlet
之前做了哪些处理,阅读本文,带你解开心中的疑惑。
2.创建web服务工厂
打开ServletWebServerFactoryAutoConfiguration
自动配置类,可以看到该自动配置类引入了4个配置类
2.1 注入Web服务工厂后置处理器
2.2 声明Tomcat Web服务工厂
默认情况下会使用Tomcat
作为Web容器,当然你也可以选择Jetty
和Undertow
,同时也会声明对应的工厂
2.3 声明服务工厂自定义配置
ServletWebServerFactoryCustomizer
通过ServerProperties
对Web服务工厂进行自定义配置,比如设置ip、端口、停机方式等
TomcatWebServerFactoryCustomizer
通过ServerProperties
来设置Tomcat
最小、最大线程数、最大连接数等
2.4 小结
该部分几个比较重要的信息:
- 声明了
WebServerFactoryCustomizerBeanPostProcessor
,用作Web服务工厂自定义配置 - 声明了
ServletWebServerFactoryCustomizer
自定义配置 - 声明了
TomcatServletWebServerFactoryCustomizer
自定义配置 - 声明
TomcatWebServerFactoryCustomizer
自定义配置
3.创建Web服务
3.1 获取Web服务工厂
private void createWebServer() {
ServletWebServerFactory factory = getWebServerFactory();
}
protected ServletWebServerFactory getWebServerFactory() {
String[] beanNames = getBeanFactory().getBeanNamesForType(ServletWebServerFactory.class);
return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);
}
在2.2
章节部分已经声明过Web服务工厂,因此此处获取到的是TomcatServletWebServerFactory
;在2.1
章节部分定义了WebServerFactoryCustomizerBeanPostProcessor
后置处理器,后置处理器中会回调WebServerFactoryCustomizer
的实现,从而完成对TomcatServletWebServerFactory
自定义配置
private void postProcessBeforeInitialization(WebServerFactory webServerFactory) {
LambdaSafe.callbacks(WebServerFactoryCustomizer.class, getCustomizers(), webServerFactory)
.withLogger(WebServerFactoryCustomizerBeanPostProcessor.class)
.invoke((customizer) -> customizer.customize(webServerFactory));
}
3.2 创建Web服务
根据Web服务工厂创建Web服务,并传入ServletContextInitializer
的实现
private void createWebServer() {
this.webServer = factory.getWebServer(getSelfInitializer());
}
3.2.1 创建Tomcat
@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {
Tomcat tomcat = new Tomcat();
}
3.2.2 创建Server和Service
@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {
Tomcat tomcat = new Tomcat();
tomcat.getService().addConnector(connector);
}
public Server getServer() {
if (server != null) {
return server;
}
System.setProperty("catalina.useNaming", "false");
server = new StandardServer();
initBaseDir();
// Set configuration source
ConfigFileLoader.setSource(new CatalinaBaseConfigurationSource(new File(basedir), null));
server.setPort( -1 );
Service service = new StandardService();
service.setName("Tomcat");
server.addService(service);
return server;
}
3.2.3 创建Connector
@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {
Connector connector = new Connector(this.protocol);
}
3.2.4 创建Engine和Host
@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {
tomcat.getHost().setAutoDeploy(false);
}
public Host getHost() {
Engine engine = getEngine();
if (engine.findChildren().length > 0) {
return (Host) engine.findChildren()[0];
}
Host host = new StandardHost();
host.setName(hostname);
getEngine().addChild(host);
return host;
}
public Engine getEngine() {
Service service = getServer().findServices()[0];
if (service.getContainer() != null) {
return service.getContainer();
}
Engine engine = new StandardEngine();
engine.setName( "Tomcat" );
engine.setDefaultHost(hostname);
engine.setRealm(createDefaultRealm());
service.setContainer(engine);
return engine;
}
3.2.5 创建Context
protected void prepareContext(Host host, ServletContextInitializer[] initializers) {
TomcatEmbeddedContext context = new TomcatEmbeddedContext();
host.addChild(context);
}
3.2.6 配置Context
protected void configureContext(Context context, ServletContextInitializer[] initializers) {
// 创建ServletContainerInitializer对象,并持有ServletContextInitializer数组
TomcatStarter starter = new TomcatStarter(initializers);
if (context instanceof TomcatEmbeddedContext) {
TomcatEmbeddedContext embeddedContext = (TomcatEmbeddedContext) context;
embeddedContext.setStarter(starter);
embeddedContext.setFailCtxIfServletStartFails(true);
}
// 设置容器初始化器为TomcatStarter
context.addServletContainerInitializer(starter, NO_CLASSES);
}
3.2.7 小结
该部分的逻辑正好与server.xml
中的配置一致
<?xml version="1.0" encoding="UTF-8"?>
<Server port="8005" shutdown="SHUTDOWN">
<Service name="Catalina">
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
<Engine name="Catalina" defaultHost="localhost">
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true">
<context>
</context>
</Host>
</Engine>
</Service>
</Server>
4.启动Web服务
通过Tomcat
的start()
方法来初始化、启动对应的组件
4.1 初始化过程
- StandardServer.initInternal()
- StandardService.initInternal()
- Connector..initInternal()
- StandardEngine.initInternal()
- StandardHost.initInternal()
- StandardContext.initInternal()
4.2 启动过程
- StandardServer.startInternal()
- StandardService.startInternal()
- Connector.startInternal()
- StandardEngine.startInternal()
- StandardHost.startInternal()
- StandardContext.startInternal()
4.3 回调ServletContainerInitializer
在StandardContext
启动过程中会回调在3.2.6
章节设置的ServletContainerInitializer
@Override
protected synchronized void startInternal() throws LifecycleException {
// Call ServletContainerInitializers
for (Map.Entry<ServletContainerInitializer, Set<Class<?>>> entry :
initializers.entrySet()) {
try {
// 创建ApplicationContext并回调ServletContainerInitializer
entry.getKey().onStartup(entry.getValue(),
getServletContext());
} catch (ServletException e) {
log.error(sm.getString("standardContext.sciFail"), e);
ok = false;
break;
}
}
}
4.4 回调ServletContextInitializer
ServletContainerInitializer
的实现 TomcatStarter
,遍历所有ServletContextInitializer
,并调用onStartup()
方法
@Override
public void onStartup(Set<Class<?>> classes, ServletContext servletContext) throws ServletException {
try {
for (ServletContextInitializer initializer : this.initializers) {
initializer.onStartup(servletContext);
}
}
}
ServletContextInitializer
的定义如下
private void selfInitialize(ServletContext servletContext) throws ServletException {
prepareWebApplicationContext(servletContext);
registerApplicationScope(servletContext);
WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
beans.onStartup(servletContext);
}
}
protected Collection<ServletContextInitializer> getServletContextInitializerBeans() {
return new ServletContextInitializerBeans(getBeanFactory());
}
4.4.1 设置ServletContextInitializer
public ServletContextInitializerBeans(ListableBeanFactory beanFactory,
Class<? extends ServletContextInitializer>... initializerTypes) {
this.initializers = new LinkedMultiValueMap<>();
// 指定类型为ServletContextInitializer
this.initializerTypes = (initializerTypes.length != 0) ? Arrays.asList(initializerTypes)
: Collections.singletonList(ServletContextInitializer.class);
// 从容器中获取类型为ServletContextInitializer的Bean并添加
addServletContextInitializerBeans(beanFactory);
// 从容器中获取类型为Servlet、Filter的Bean组装成对应的ServletRegistrationBean、FilterRegistrationBean并添加
addAdaptableBeans(beanFactory);
// 获取所有ServletContextInitializer实现
List<ServletContextInitializer> sortedInitializers = this.initializers.values().stream()
.flatMap((value) -> value.stream().sorted(AnnotationAwareOrderComparator.INSTANCE))
.collect(Collectors.toList());
this.sortedList = Collections.unmodifiableList(sortedInitializers);
logMappings(this.initializers);
}
4.5 以DispatcherServletRegistrationBean
为例
DispatcherServletRegistrationBean
实现了ServletContextInitializer
接口,从4.4.1
章节可以得知实现ServletContextInitializer
接口的Bean
会被添加到ServletContextInitializer
集合中
@Configuration(proxyBeanMethods = false)
@Conditional(DispatcherServletRegistrationCondition.class)
@ConditionalOnClass(ServletRegistration.class)
@EnableConfigurationProperties(WebMvcProperties.class)
@Import(DispatcherServletConfiguration.class)
protected static class DispatcherServletRegistrationConfiguration {
@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
@ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet,
WebMvcProperties webMvcProperties, ObjectProvider<MultipartConfigElement> multipartConfig) {
DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet,
webMvcProperties.getServlet().getPath());
registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
registration.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());
multipartConfig.ifAvailable(registration::setMultipartConfig);
return registration;
}
}
4.5.1 执行onStartup()方法
@Override
public final void onStartup(ServletContext servletContext) throws ServletException {
String description = getDescription();
if (!isEnabled()) {
logger.info(StringUtils.capitalize(description) + " was not registered (disabled)");
return;
}
register(description, servletContext);
}
4.5.2 添加Servlet到上下文
private ServletRegistration.Dynamic addServlet(String servletName, String servletClass,
Servlet servlet, Map<String,String> initParams) throws IllegalStateException {
// 是否存在名称为对应名称的Wrapper
Wrapper wrapper = (Wrapper) context.findChild(servletName);
if (wrapper == null) {
// 创建Wrapper
wrapper = context.createWrapper();
wrapper.setName(servletName);
// 将Wrapper添加到上下文中
context.addChild(wrapper);
} else {
if (wrapper.getName() != null &&
wrapper.getServletClass() != null) {
if (wrapper.isOverridable()) {
wrapper.setOverridable(false);
} else {
return null;
}
}
}
ServletSecurity annotation = null;
if (servlet == null) {
wrapper.setServletClass(servletClass);
Class<?> clazz = Introspection.loadClass(context, servletClass);
if (clazz != null) {
annotation = clazz.getAnnotation(ServletSecurity.class);
}
} else {
// 设置Servlet对应的Class
wrapper.setServletClass(servlet.getClass().getName());
// 设置Servlet实例
wrapper.setServlet(servlet);
if (context.wasCreatedDynamicServlet(servlet)) {
annotation = servlet.getClass().getAnnotation(ServletSecurity.class);
}
}
ServletRegistration.Dynamic registration =
new ApplicationServletRegistration(wrapper, context);
if (annotation != null) {
registration.setServletSecurity(new ServletSecurityElement(annotation));
}
return registration;
}
4.6 以CharacterEncodingFilter
为例
CharacterEncodingFilter
实现了Filter
接口,从4.4.1
章节可以得知实现Filter
接口的Bean会被组装成对应的FilterRegistrationBean
并添加到ServletContextInitializer
集合中
@Bean
@ConditionalOnMissingBean
public CharacterEncodingFilter characterEncodingFilter() {
CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
filter.setEncoding(this.properties.getCharset().name());
filter.setForceRequestEncoding(this.properties.shouldForce(Encoding.Type.REQUEST));
filter.setForceResponseEncoding(this.properties.shouldForce(Encoding.Type.RESPONSE));
return filter;
}
4.6.1 执行onStartup()方法
@Override
public final void onStartup(ServletContext servletContext) throws ServletException {
String description = getDescription();
if (!isEnabled()) {
logger.info(StringUtils.capitalize(description) + " was not registered (disabled)");
return;
}
register(description, servletContext);
}
4.6.2 添加Filter到上下文
private FilterRegistration.Dynamic addFilter(String filterName,
String filterClass, Filter filter) throws IllegalStateException {
// 是否存在对应名称的Filter
FilterDef filterDef = context.findFilterDef(filterName);
if (filterDef == null) {
// 创建FilterDef对象
filterDef = new FilterDef();
// 设置Filter名称
filterDef.setFilterName(filterName);
// 添加FilterDef到上下文
context.addFilterDef(filterDef);
} else {
if (filterDef.getFilterName() != null &&
filterDef.getFilterClass() != null) {
return null;
}
}
if (filter == null) {
filterDef.setFilterClass(filterClass);
} else {
// 设置Filter对应的Class
filterDef.setFilterClass(filter.getClass().getName());
// 设置Filter
filterDef.setFilter(filter);
}
return new ApplicationFilterRegistration(filterDef, context);
}
4.7 小结
程序中实现Filter
、Servlet
、ServletContextInitializer
接口的Bean
会被放入ServletContextInitializer
集合并被添加到上下文StandardContext
中
5.接收处理请求
5.1 接收请求
当我们在浏览器中输入要访问的请求时,由Connector
组件接收请求,最终交由CoyoteAdapter
处理
@Override
public void service(org.apache.coyote.Request req, org.apache.coyote.Response res)
throws Exception {
Request request = (Request) req.getNote(ADAPTER_NOTES);
Response response = (Response) res.getNote(ADAPTER_NOTES);
try {
postParseSuccess = postParseRequest(req, request, res, response);
if (postParseSuccess) {
//check valves if we support async
request.setAsyncSupported(
connector.getService().getContainer().getPipeline().isAsyncSupported());
// 请求交由容器来处理
connector.getService().getContainer().getPipeline().getFirst().invoke(
request, response);
}
}
}
5.2处理请求
基于前面的分析可以得知StandardEngine
包含StandardHost
,StandardHost
包含StandardContext
,StandardContext
包含StandardWrapper
,因此请求会交给StandardWrapperValve
处理
@Override
public final void invoke(Request request, Response response)
throws IOException, ServletException {
StandardWrapper wrapper = (StandardWrapper) getContainer();
Servlet servlet = null;
Context context = (Context) wrapper.getParent();
try {
if (!unavailable) {
// 1.获取Servlet,也就是DispatcherServlet
servlet = wrapper.allocate();
}
}
// 2.构建ApplicationFilterChain对象,设置Filter和Servlet
ApplicationFilterChain filterChain =
ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);
Container container = this.container;
try {
if ((servlet != null) && (filterChain != null)) {
// Swallow output if needed
if (context.getSwallowOutput()) {
//
} else {
if (request.isAsyncDispatching()) {
request.getAsyncContextInternal().doInternalDispatch();
} else {
// 3.执行Filter的doFilter()方法,Filter链执行完成后,执行Servlet的service()方法
filterChain.doFilter
(request.getRequest(), response.getResponse());
}
}
}
}
}