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