上一篇文章我们介绍了TomCat启动流程并如何讲一个socket连接,一步步解析转化成servlet规范的request和Response的,这篇文章将继续介绍TomCat启动流程,如何将request根据URL转发到业务处理流程中的,以及,TomCat如何集成在springboot中。
从TomCat的架构可以看到主要分为连接器和容器两部分。连机器connector又分为EndPoint、processor、adapter三个组成。
EndPoint:主要负责网络通信,建立网络连接并检测IO事件
processor:主要负责应用层的协议解析(HTTP、AJP等)。
adapter:将processor处理完的内部的
Request
、Response
转换为标准的ServletRequest
/ServletResponse
可以看出上一篇文章主要介绍了连接器connector的工作。
容器(container)中的四个部分的作用分别是:
- Engine即为全局引擎容器,它的标准实现是StandardEngine。
- Host在整个Servlet引擎中抽象出Host容器用于表示虚拟主机,它是根据URL地址中的主机部分抽象的,一个Servlet引擎可以包含若干个Host容器,而一个Host容器可以包含若干个Context容器。在Tomcat中Host的标准实现是StandardHost,它从虚拟主机级别对请求和响应进行处理。
- Context对应一个Web应用程序,但Web项目的组成比较复杂,它包含很多组件。对于Web容器,需要将Web应用程序包含的组件转换成容器的组件。
- Wrapper属于Tomcat中4个级别容器中最小级别的容器,与之相对应的是Servlet。
在调用的时候他们通过链式顺序调用,最终找到对应的servlet进行业务处理。
现在让我们从springboot启动开始一步步分析TomCat是如何启动的,并如何将springBoot中的URL业务处理方法注册入TomCat中的。
SpringApplication.run()方法是项目启动的地址,在创建context的时候会根据服务的类型创建对应的context
我们是servlet项目,所有创建AnnotationConfigServletWebServerApplicationContext实现对象,
SpringApplication.refreshContext(context)--->AbstractApplicationContext.refresh()--->ServletWebServerApplicationContext.createWebServer()
其中AbstractApplicationContext和ServletWebServerApplicationContext都是AnnotationConfigServletWebServerApplicationContext的父类
这里会发现factory的类型是TomcatServletWebServerFactory,spring中对于factory的实现还有其他几个如Jetty,Undertow,这里为啥是TomcatServletWebServerFactory类型呢,要想弄清这个,需要查看spring-boot-autoconfigure.jar下的/META-INF/spring.factories文件中的一个名为ServletWebServerFactoryAutoConfiguration的自动配置类
可以看到自动配置类会导入几个Embedded类,这些类在导入的时候,会判断依赖中是否有相关web服务器的类(@ConditionalOnClass注解),如果有则注入相关factory类。对于修改TomCat参数的TomcatWebServerFactoryCustomizer导入方式与上面类似。因为spring项目默认导入了TomCat的服务器,如果我们需要替换容器时需要将TomCat的依赖去除掉,具体参考这里,spring启动时提供了很多配置类,这些自动配置可以很好的协助程序员的工作。
继续回到createWebServer方法中,TomcatServletWebServerFactory.getWebServer()
这里将spring中的类信息传入到了Tomcat中很关键,这些参数是一系列实现了初始化接口的类,调用后这里实现了SpringBoot中写的Servlet三大组件(Servlet、Filter、Listener)传入到Tomcat中
prepareContext()--->configureContext()
这里的Context是org.apache.catalina下的,是TomcatStarter是javax.servlet.ServletContainerInitializer的实现类,是符合Servlet编程规范的,至此,spring将参数传入到了Tomcat中。
getTomcatWebServer(tomcat)--->创建TomcatWebServer()--->调用initialize()方法
这里会调用上面的实现了ServletContextInitializer接口的方法,实现注册Servlet、Filter、Listener这些部件。Tomcat也完成了启动。 Springboot中默认的Servlet是DispatcherServlet,对于URL和映射函数的保存可以参考这篇文章:spring boot中controller的URL存储匹配方式
具体的解析过程是在spring的bean注册的时候调用初始化方法进行解构保存。
pipeline会在Tomcat启动的时候创建,实现了链式调用。
当一个请求进来时,通过Tomcat的层层转发以及过滤器这些操作,最后进入到StandardWrapperValve中
ApplicationFilterChain调用完过滤器后,会去调用注册进来的Servlet方法去执行最终的业务代码。