文章目录
- 一、基本机制
- 二 、Spring MVC装配tomcat 分析
- 2.1 pom 分析
- 2.2 tomcat 的自动装配&mvc自动装配
- 2.2.1 tomcat的装配逻辑: `TomcatWebServerFactoryCustomizer`
- 2.2.2 回调函数加载 tomcat :`WebServerFactoryCustomizerBeanPostProcessor`
- 2.2.3 加载WebServerFactoryCustomizerBeanPostProcessor:`ServletWebServerFactoryAutoConfiguration `
- 2.2.4 `ServletWebServerFactoryConfiguration`
- 2.2.5 准备DispatcherServlet
- 2.2.6 WebMvcAutoConfiguration :mvc的最终装配
- 三、一个请求的调用
一、基本机制
starter官方文档
编写starter 中文
spring boot 的starter是spring 的核心模型。一个spring boot 工程是由很多个starter组成。新建spring boot 工程时,starter提供装配的逻辑和依赖。比如放入
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
其中包含tomcat starter,这个依赖包含tomcat jar包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<version>2.3.1.RELEASE</version>
<scope>compile</scope>
</dependency>
那么自动装配就会实例化tomcatServer
。
1.1 starter 的结构
starter的结构是有两个模块组成:autoconfigure
和dependency
autoconfigure
模块负责按照一定的条件实例化bean
,放入spring context
dependency
模块就是依赖的配置
1.2 autoconfigure
自动装配模块
- 对于一个starter,
autoconfigure
自动装配 不是必须的。如果确实需要实例化Bean
,那么需要编写自动装配逻辑
+配置文件
(spring.factories)。下面是spring-boot-starter
对spring官方starter
自动装配设置。
自定义的
starter
,它的autoconfigure
和dependency
都在一个项目下。spring-boot-starter
比较庞大,使用专门的jar,来保留自动装配逻辑。官方starter
- 结构
- 自动装配逻辑
对于spring mvc 的自动装配逻辑
图片中
bean
的注解比较好理解,就是来实例化的。一堆Condition
就是用来判断,装配的条件是否满足。比如@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
,意思就是类的路径下只要有DispatcherServlet.class
,等就实例化该bean。
- 配置文件 spring.factories
位于META_INF/spring.factories
。将自动装配类逻辑配置到如下属性,会被扫描装载
1.3 dependency
依赖模块
依赖是必须的,starter可以只是单纯提供依赖的jar,是一个空的jar。比如spring.boot.starter.tomcat
这里是tomcat starter 的依赖,可以看到这个starter,把必要的依赖都引入了。
<dependencies>
<dependency>
<groupId>jakarta.annotation</groupId>
<artifactId>jakarta.annotation-api</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<scope>compile</scope>
<exclusions>
<exclusion>
<artifactId>tomcat-annotations-api</artifactId>
<groupId>org.apache.tomcat</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>jakarta.el</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-websocket</artifactId>
<scope>compile</scope>
<exclusions>
<exclusion>
<artifactId>tomcat-annotations-api</artifactId>
<groupId>org.apache.tomcat</groupId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
二 、Spring MVC装配tomcat 分析
pom引入了mvc的依赖和tomcat的依赖。那么根据装配逻辑,tomcat会被实例化,注入到mvc的类中。
2.1 pom 分析
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>demomvc</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demomvc</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
2.2.1 jar包的依赖
spring-boot-starter-web
这个pom
配置了mvc jar的依赖和tomcat jar依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<version>2.3.1.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<scope>compile</scope>
</dependency>
</dependencies>
2.2.1 自动装配逻辑
spring-boot-starter
中的spring-boot-autoconfigure放置了所有的自动装配逻辑。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
<version>2.3.1.RELEASE</version>
<scope>compile</scope>
</dependency>
2.2 tomcat 的自动装配&mvc自动装配
2.2.1 tomcat的装配逻辑: TomcatWebServerFactoryCustomizer
embedded中提供了四种servlet 容器
。tomcat 只是一种
@ConditionalOnClass({ Tomcat.class, UpgradeProtocol.class })
,表明只要存在这个类,那么这个实例化逻辑就会触发。
TomcatWebServerFactoryCustomizer
实现了WebServerFactoryCustomizer
public class TomcatWebServerFactoryCustomizer
implements WebServerFactoryCustomizer<ConfigurableTomcatWebServerFactory>, Ordered {
private final Environment environment;
private final ServerProperties serverProperties;
public TomcatWebServerFactoryCustomizer(Environment environment, ServerProperties serverProperties) {
this.environment = environment;
this.serverProperties = serverProperties;
}
2.2.2 回调函数加载 tomcat :WebServerFactoryCustomizerBeanPostProcessor
PostProcessor,是每个bean被初始化前调用的一个逻辑。
这里用来
public class WebServerFactoryCustomizerBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware {
private ListableBeanFactory beanFactory;
private List<WebServerFactoryCustomizer<?>> customizers;
@Override
public void setBeanFactory(BeanFactory beanFactory) {
Assert.isInstanceOf(ListableBeanFactory.class, beanFactory,
"WebServerCustomizerBeanPostProcessor can only be used with a ListableBeanFactory");
this.beanFactory = (ListableBeanFactory) beanFactory;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof WebServerFactory) {
postProcessBeforeInitialization((WebServerFactory) bean);
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@SuppressWarnings("unchecked")
private void postProcessBeforeInitialization(WebServerFactory webServerFactory) {
LambdaSafe.callbacks(WebServerFactoryCustomizer.class, getCustomizers(), webServerFactory)
.withLogger(WebServerFactoryCustomizerBeanPostProcessor.class)
.invoke((customizer) -> customizer.customize(webServerFactory));
}
private Collection<WebServerFactoryCustomizer<?>> getCustomizers() {
if (this.customizers == null) {
// Look up does not include the parent context
this.customizers = new ArrayList<>(getWebServerFactoryCustomizerBeans());
this.customizers.sort(AnnotationAwareOrderComparator.INSTANCE);
this.customizers = Collections.unmodifiableList(this.customizers);
}
return this.customizers;
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private Collection<WebServerFactoryCustomizer<?>> getWebServerFactoryCustomizerBeans() {
return (Collection) this.beanFactory.getBeansOfType(WebServerFactoryCustomizer.class, false, false).values();
}
}
在加载WebServerFactory
bean时,会加载WebServerFactoryCustomizer
。可见如果没有WebServerFactory
,就不会启动tomcat。
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof WebServerFactory) {
postProcessBeforeInitialization((WebServerFactory) bean);
}
return bean;
}
获取所有的WebServerFactoryCustomizer
, 其中包含TomcatWebServerFactoryCustomizer
private Collection<WebServerFactoryCustomizer<?>> getWebServerFactoryCustomizerBeans() {
return (Collection) this.beanFactory.getBeansOfType(WebServerFactoryCustomizer.class, false, false).values();
}
2.2.3 加载WebServerFactoryCustomizerBeanPostProcessor:ServletWebServerFactoryAutoConfiguration
这个类会注册上面的postproessor,从名称可以看出,就是准备webserver的装配逻辑。
2.2.4 ServletWebServerFactoryConfiguration
ServletWebServerFactoryAutoConfiguration
中还包含一个类ServletWebServerFactoryConfiguration
。
这个类中演示了如何使用API来启动tomcat
TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
这里Tomcat tomcat = new Tomcat();
就是开启一个tomcat
@Override
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);
}
2.2.5 准备DispatcherServlet
DispatcherServlet是mvc的核心类。在上一个ServletWebServerFactoryAutoConfiguration之后
2.2.6 WebMvcAutoConfiguration :mvc的最终装配
简单看下,mvc需要的bean都会在这里初始化化完成。
三、一个请求的调用
tomcat的原理
发给端口的请求最终被tomcat转给了 Servlet的服务。
想自定义的请求分发和处理逻辑实际上是实现Servlet接口
菜鸟教程
Spring mvc的核心是DispatcherServlet,就是实现了一个Servlet接口。
一个请求会会经过org.apache.catalina.core.StandardHostValve
的invoke(Request request, Response response)
,最终会调用到DispatcherServlet
。
Spring mvc 执行DispatcherServlet原理。
3.1 一个简单的演示
tomcat启动类
public class AppTomcat {
public static void main(String[] args) throws LifecycleException {
// 创建Tomcat应用对象
Tomcat tomcat = new Tomcat();
// 设置Tomcat的端口号
tomcat.setPort(8080);
// 是否设置Tomcat自动部署
tomcat.getHost().setAutoDeploy(false);
// 创建上下文
StandardContext standardContext = new StandardContext();
// 设置项目名
standardContext.setPath("/sb");
// 监听上下文
standardContext.addLifecycleListener(new FixContextListener());
// 向tomcat容器对象添加上下文配置
tomcat.getHost().addChild(standardContext);
// 创建Servlet
tomcat.addServlet("/sb", "helloword", new HelloServlet());
// Servlet映射
standardContext.addServletMappingDecoded("/hello", "helloword");
//启动tomcat容器
tomcat.start();
//等待
tomcat.getServer().await();
}
}
servlet类
public class HelloServlet extends HttpServlet{
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().write("hellowrld");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}