文章目錄
- 一、基本機制
- 二 、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);
}
}