SpringBoot-嵌入式Servlet容器原理

WebServerFactory自动配置

@Configuration
@AutoConfigureOrder(-2147483648)
@ConditionalOnClass({ServletRequest.class})
@ConditionalOnWebApplication(
    type = Type.SERVLET
)
@EnableConfigurationProperties({ServerProperties.class})
@Import({ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class, EmbeddedTomcat.class, EmbeddedJetty.class, EmbeddedUndertow.class})
public class ServletWebServerFactoryAutoConfiguration {

	// Servlet Web Server 通用配置,将ServerProperties注入到定制器中,实现application.properties中的配置信息配置 Servlet Web Server
	@Bean
	public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(ServerProperties serverProperties) {
	return new ServletWebServerFactoryCustomizer(serverProperties);
	}

	// Tomcat Servlet Web Server 配置信息
	@Bean
	@ConditionalOnClass(
	name = {"org.apache.catalina.startup.Tomcat"}
	)
	public TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer(ServerProperties serverProperties) {
	return new TomcatServletWebServerFactoryCustomizer(serverProperties);
	}
}

EmbeddedTomcat 嵌入式Tomcat

@Configuration
// 判断当前是否引入了 org.apache.catalina.startup.Tomcat 依赖
@ConditionalOnClass({Servlet.class, Tomcat.class, UpgradeProtocol.class})
// 当没有 ServletWebServerFactory 类型的Bean时才注入该组件
@ConditionalOnMissingBean(
	value = {ServletWebServerFactory.class},
	search = SearchStrategy.CURRENT
)
public static class EmbeddedTomcat {
	public EmbeddedTomcat() {
	}

  // 创建Tomcat容器工厂
	@Bean
	public TomcatServletWebServerFactory tomcatServletWebServerFactory() {
		return new TomcatServletWebServerFactory();
	}
}

  ServletWebServerFactory(Servlet容器工厂) 继承关系

@FunctionalInterface
public interface ServletWebServerFactory {
	// 创建WebServer即Servlet容器 
  WebServer getWebServer(ServletContextInitializer... initializers);
}

  WebServer (Servlet容器) 继承关系

public interface WebServer {
    void start() throws WebServerException;

    void stop() throws WebServerException;

    int getPort();
}

  TomcatServletWebServerFactory 获得Tomcat Servlet 容器(即org.apache.catalina.startup.Tomcat)

public WebServer getWebServer(ServletContextInitializer... initializers) {
	// 创建Tomcat容器	
	Tomcat tomcat = new Tomcat();
  // 配置Tomcat运行环境
	File baseDir = this.baseDirectory != null ? this.baseDirectory : this.createTempDir("tomcat");
	tomcat.setBaseDir(baseDir.getAbsolutePath());
	Connector connector = new Connector(this.protocol);
	tomcat.getService().addConnector(connector);
	this.customizeConnector(connector);
	tomcat.setConnector(connector);
	tomcat.getHost().setAutoDeploy(false);
	this.configureEngine(tomcat.getEngine());
	Iterator var5 = this.additionalTomcatConnectors.iterator();

	while(var5.hasNext()) {
		Connector additionalConnector = (Connector)var5.next();
		tomcat.getService().addConnector(additionalConnector);
	}

	this.prepareContext(tomcat.getHost(), initializers);
  // 调用 tomcat.start(); 启动容器
	return this.getTomcatWebServer(tomcat);
}

修改Servlet容器配置信息的方式 

  1)修改application.properties

server.port=8888

  2)  添加定制器

package com.wjz.config;

import org.springframework.boot.web.server.ConfigurableWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.stereotype.Component;

@Component
public class WebServerConfig implements WebServerFactoryCustomizer<ConfigurableWebServerFactory> {
    @Override
    public void customize(ConfigurableWebServerFactory factory) {
        factory.setPort(8888);
    }
}  

定制器后置处理器

public class WebServerFactoryCustomizerBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware {

	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		if (bean instanceof WebServerFactory) {
			this.postProcessBeforeInitialization((WebServerFactory)bean);
		}

		return bean;
	}

	private void postProcessBeforeInitialization(WebServerFactory webServerFactory) {
		((Callbacks)LambdaSafe.callbacks(WebServerFactoryCustomizer.class, this.getCustomizers(), webServerFactory, new Object[0]).withLogger(WebServerFactoryCustomizerBeanPostProcessor.class)).invoke((customizer) -> {
			customizer.customize(webServerFactory);
		});
	}

	private Collection<WebServerFactoryCustomizer<?>> getCustomizers() {
		if (this.customizers == null) {
			this.customizers = new ArrayList(this.getWebServerFactoryCustomizerBeans());
			this.customizers.sort(AnnotationAwareOrderComparator.INSTANCE);
			this.customizers = Collections.unmodifiableList(this.customizers);
		}

		return this.customizers;
	}

	private Collection<WebServerFactoryCustomizer<?>> getWebServerFactoryCustomizerBeans() {
		return this.beanFactory.getBeansOfType(WebServerFactoryCustomizer.class, false, false).values();
	}
}

Servlet容器启动流程

  什么时候创建ServletWebServerFactory(Servlet容器工厂),什么时候创建 WebServer (Servlet容器)并启动?

  1)EmbeddedTomcat.TomcatServletWebServerFactory处和TomcatServletWebServerFactory#getWebServer()处打断点,debug方式启动系统

  2)创建SpringApplication对象并执行run()方法

  3)context = this.createApplicationContext(); this.refreshContext(context); SpringBoot刷新IOC容器即创建IOC容器对象并初始化容器,创建容器中的每一个组件

如果是Web环境创建AnnotationConfigServletWebServerApplicationContext,否则创建默认的IOC容器AnnotationConfigApplicationContext

  4)this.refresh(context); 刷新刚创建好的IOC容器即执行IOC容器的refresh()方法

  5)this.onRefresh(); Web容器会创建嵌入式Servlet Web Server容器(参见ServletWebServerApplicationContext)

  6)ServletWebServerFactory factory = this.getWebServerFactory(); 创建ServletWebServerFactory(Servlet容器工厂),后置处理器工作获取所有的定制器定制Servlet容器的相关配置

  7)this.webServer = factory.getWebServer(new ServletContextInitializer[]{this.getSelfInitializer()});  创建 WebServer (Servlet容器)并启动

外部Servlet容器&支持JSP

  1)创建一个war项目

  2)嵌入式Tomcat依赖指定为provided

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-tomcat</artifactId>
    <scope>provided</scope>
</dependency>

  3)编写一个SpringBootServletInitializer的子类并复写configure方法

package com.wjz.springboot;

import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;

public class ServletInitializer extends SpringBootServletInitializer {

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        return application.sources(SpringBootJspApplication.class);
    }
}  

  4)启动外部Tomcat

外部Servlet容器启动流程

  1)Servlet3.0的一个规范,容器启动时会创建每一个应用实现了javax.servlet.ServletContainerInitializer的实例

  2)寻找各个应用每个jar包路径 META-INF/services/ 路径下的文件,文件名为 javax.servlet.ServletContainerInitializer,内容是实现类的全类名,参考 spring-web jar包下对应的路径文件

org.springframework.web.SpringServletContainerInitializer

  3)还可以使用@HandlesTypes注解,在容器启动时传入类

  4)SpringServletContainerInitializer将@HandlesTypes({WebApplicationInitializer.class})标注的这个类型的所有类传入到onStartup方法的Set<Class<?>>中,

将这些类型创建实例且调用各自的onStartup方法

  

   相当于SpringBootServletInitializer类会创建对象并调用onStartup方法,该方法中会创建IOC容器

public void onStartup(ServletContext servletContext) throws ServletException {
	this.logger = LogFactory.getLog(this.getClass());
	// 创建IOC容器
	WebApplicationContext rootAppContext = this.createRootApplicationContext(servletContext);
	if (rootAppContext != null) {
		servletContext.addListener(new ContextLoaderListener(rootAppContext) {
			public void contextInitialized(ServletContextEvent event) {
			}
		});
	} else {
		this.logger.debug("No ContextLoaderListener registered, as createRootApplicationContext() did not return an application context");
	}
}

  

原文地址:https://www.cnblogs.com/BINGJJFLY/p/11878064.html