SpringMVC嵌入式容器原理分析

嵌入式Servlet容器原理

Spring Boot 默认支持的三种Servlet容器 Tomcat(默认) Jetty Undertow Netty

切换Servlet容器

将tomcat 改成 undertow

  • 排除默认的tomcat
  • 添加undertow依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </exclusion>
    </exclusions>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-undertow</artifactId>
</dependency>

image-20210208144417041

Servlet 容器的自动装配原理 以Tomcat为例

  • 以WebMvcAutoConfiguration 为入口 找到DispatcherServletAutoConfiguration 找到ServletWebServerFactoryAutoConfiguration 此类是容器自动装配核心

    image-20210208144803795

    image-20210208144821790

  • 该类@Import 导入了 各种Servlet容器 以Tomcat为例 首先判断一下环境中是否有Tomcat.class 保证是tomcat的环境 因为web-starter默认带的是tomcat依赖,此时这段配置生效

    image-20210208145018383

  • 分析配置 往IOC容器中添加一个TomcatServletWebServerFactory

    @Bean
    TomcatServletWebServerFactory tomcatServletWebServerFactory(
        ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers,
        ObjectProvider<TomcatContextCustomizer> contextCustomizers,
        ObjectProvider<TomcatProtocolHandlerCustomizer<?>> protocolHandlerCustomizers) {
        // 核心就是 new 了一个TomcatServlet web服务器工厂 添加了各种自定义配置Customizers
        TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
        factory.getTomcatConnectorCustomizers()
            .addAll(connectorCustomizers.orderedStream().collect(Collectors.toList()));
        factory.getTomcatContextCustomizers()
            .addAll(contextCustomizers.orderedStream().collect(Collectors.toList()));
        factory.getTomcatProtocolHandlerCustomizers()
            .addAll(protocolHandlerCustomizers.orderedStream().collect(Collectors.toList()));
        return factory;
    }
    
    

第二步

  • web应用会创建一个web的IOC容器ServletWebServerApplicationContext

    image-20210208145429185

  • 在该类中有一个onRefresh方法 调用了createWebServer方法创建容器

    	@Override
    	protected void onRefresh() {
    		super.onRefresh();
    		try {
                // 核心方法
    			createWebServer();
    		}
    		catch (Throwable ex) {
    			throw new ApplicationContextException("Unable to start web server", ex);
    		}
    	}
    
  • createWebServer方法分析 从IOC容器中拿到ServletWebServerFactory 调用工厂的getWebServer方法得到一个WebServer

    	private void createWebServer() {
    		WebServer webServer = this.webServer;
    		ServletContext servletContext = getServletContext();
    		if (webServer == null && servletContext == null) {
    			ServletWebServerFactory factory = getWebServerFactory();
    			this.webServer = factory.getWebServer(getSelfInitializer());
    			getBeanFactory().registerSingleton("webServerGracefulShutdown",
    					new WebServerGracefulShutdownLifecycle(this.webServer));
    			getBeanFactory().registerSingleton("webServerStartStop",
    					new WebServerStartStopLifecycle(this, this.webServer));
    		}
    		else if (servletContext != null) {
    			try {
    				getSelfInitializer().onStartup(servletContext);
    			}
    			catch (ServletException ex) {
    				throw new ApplicationContextException("Cannot initialize servlet context", ex);
    			}
    		}
    		initPropertySources();
    	}
    
    

    getWebServerFactory 定义了容器里面只能有一个 Web容器 如果出现多个容器会抛出异常,调用方法之后此时就会拿到之前装配进去的TomcatServletWebServerFactory然后调用他的getWebServer方法

    	protected ServletWebServerFactory getWebServerFactory() {
    		// Use bean names so that we don't consider the hierarchy
    		String[] beanNames = getBeanFactory().getBeanNamesForType(ServletWebServerFactory.class);
    		if (beanNames.length == 0) {
    			throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to missing "
    					+ "ServletWebServerFactory bean.");
    		}
    		if (beanNames.length > 1) {
    			throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to multiple "
    					+ "ServletWebServerFactory beans : " + StringUtils.arrayToCommaDelimitedString(beanNames));
    		}
    		return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);
    	}
    
  • getWebServer方法 new Tomcat() 设置一些配置参数 相当于把之前启动tomcat的逻辑用代码实现了

    @Override
    public WebServer getWebServer(ServletContextInitializer... initializers) {
        if (this.disableMBeanRegistry) {
            Registry.disableRegistry();
        }
        // 创建Tomcat实例
        Tomcat tomcat = new Tomcat();
        // 设置各种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);
        // 获取Tomcat WebServer
        return getTomcatWebServer(tomcat);
    }
    
  • getTomcatWebServer方法底层其实new TomcatWebServer

    // 构造器
    public TomcatWebServer(Tomcat tomcat, boolean autoStart, Shutdown shutdown) {
    		Assert.notNull(tomcat, "Tomcat Server must not be null");
    		this.tomcat = tomcat;
    		this.autoStart = autoStart;
    		this.gracefulShutdown = (shutdown == Shutdown.GRACEFUL) ? new GracefulShutdown(tomcat) : null;
        // 初始化方法
    		initialize();
    	}
    
    	private void initialize() throws WebServerException {
    		logger.info("Tomcat initialized with port(s): " + getPortsDescription(false));
    		synchronized (this.monitor) {
    			try {
    				// 各种添加监听器 省略
    				// tomcat启动 这一行执行 Tomcat Server才真正的启动完成
    				this.tomcat.start();
    			// 其他非核心逻辑省略
    			}
    			catch (Exception ex) {
    				stopSilently();
    				destroySilently();
    				throw new WebServerException("Unable to start embedded Tomcat", ex);
    			}
    		}
    	}
    

自定义Servlet容器的三种方式

  • 实现WebServerFactoryCustomizer可以修改一些底层的默认配置
    • 将配置文件与ServletWebServerFacotry绑定
  • 修改配置文件server.xxx属性 (推荐)
  • 直接自定义ConfigurableServletWebServerFactory
@Component
public class CustomizationBean implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> {

    @Override
    public void customize(ConfigurableServletWebServerFactory server) {
        server.setPort(9000);
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值