b Spring MVC 如何內嵌 tomcat & 流程分析

一、基本機制

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的結構是有兩個模塊組成:autoconfiguredependency

  • autoconfigure模塊負責按照一定的條件實例化bean,放入spring context
  • dependency模塊就是依賴的配置

1.2 autoconfigure 自動裝配模塊

  1. 對於一個starter,autoconfigure自動裝配 不是必須的。如果確實需要實例化Bean,那麼需要編寫自動裝配邏輯+ 配置文件(spring.factories)。下面是spring-boot-starter對spring官方starter自動裝配設置。

自定義的starter,它的autoconfiguredependency都在一個項目下。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();
	}

}

在加載WebServerFactorybean時,會加載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.StandardHostValveinvoke(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);
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章