引言
根据对RefreshContext(...)的解析,我们发现对内嵌容器的实现主要在onRefresh()中,
而在onRefresh()之前,我们已经进行了自动装载,并根据装载成功将所需的类加载到spring IOC容器中。Spring Boot就是根据IOC中的内容进行内嵌容器的初始化的。
嵌入式容器的自动装载
@AutoConfiguration(after = SslAutoConfiguration.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@ConditionalOnClass(ServletRequest.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(ServerProperties.class)
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration {
@Bean
// 会在 postprocessors 时触发
public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(ServerProperties serverProperties,
ObjectProvider<WebListenerRegistrar> webListenerRegistrars,
ObjectProvider<CookieSameSiteSupplier> cookieSameSiteSuppliers, ObjectProvider<SslBundles> sslBundles) {
return new ServletWebServerFactoryCustomizer(serverProperties, webListenerRegistrars.orderedStream().toList(),
cookieSameSiteSuppliers.orderedStream().toList(), sslBundles.getIfAvailable());
}
@Bean
@ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat")
// 会在 postprocessors 时触发
public TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer(
ServerProperties serverProperties) {
return new TomcatServletWebServerFactoryCustomizer(serverProperties);
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(value = "server.forward-headers-strategy", havingValue = "framework")
@ConditionalOnMissingFilterBean(ForwardedHeaderFilter.class)
static class ForwardedHeaderFilterConfiguration {
@Bean
@ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat")
ForwardedHeaderFilterCustomizer tomcatForwardedHeaderFilterCustomizer(ServerProperties serverProperties) {
return (filter) -> filter.setRelativeRedirects(serverProperties.getTomcat().isUseRelativeRedirects());
}
@Bean
FilterRegistrationBean<ForwardedHeaderFilter> forwardedHeaderFilter(
ObjectProvider<ForwardedHeaderFilterCustomizer> customizerProvider) {
ForwardedHeaderFilter filter = new ForwardedHeaderFilter();
customizerProvider.ifAvailable((customizer) -> customizer.customize(filter));
FilterRegistrationBean<ForwardedHeaderFilter> registration = new FilterRegistrationBean<>(filter);
registration.setDispatcherTypes(DispatcherType.REQUEST, DispatcherType.ASYNC, DispatcherType.ERROR);
registration.setOrder(Ordered.HIGHEST_PRECEDENCE);
return registration;
}
}
interface ForwardedHeaderFilterCustomizer {
void customize(ForwardedHeaderFilter filter);
}
// ......
}
@AutoConfigureOrder这个注解是决定配置类的加载顺序的,当注解里的值越小越先加载,而Ordered.HIGHEST_PRECEDENCE的值是Integer.MIN_VALUE也就是说这个类肯定是最先加载的那一批@EnableConfigurationProperties开启ServerProperties类的属性值配置。而这个类里面包含的就是Web服务的配置@Import则又导入了对应的PostProcessorsRegistrar以及嵌入式容器
PostProcessorRegistrar
/**
* 注册 WebServerFactoryCustomizerBeanPostProcessor,
* 此类会在 early registration阶段通过 Import BeanDefinitionRegistrar注册
*/
public static class BeanPostProcessorsRegistrar implements ImportBeanDefinitionRegistrar, BeanFactoryAware {
private ConfigurableListableBeanFactory beanFactory;
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
if (beanFactory instanceof ConfigurableListableBeanFactory listableBeanFactory) {
this.beanFactory = listableBeanFactory;
}
}
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
BeanDefinitionRegistry registry) {
if (this.beanFactory == null) {
return;
}
registerSyntheticBeanIfMissing(registry, "webServerFactoryCustomizerBeanPostProcessor",
WebServerFactoryCustomizerBeanPostProcessor.class);
registerSyntheticBeanIfMissing(registry, "errorPageRegistrarBeanPostProcessor",
ErrorPageRegistrarBeanPostProcessor.class);
}
private <T> void registerSyntheticBeanIfMissing(BeanDefinitionRegistry registry, String name,
Class<T> beanClass) {
if (ObjectUtils.isEmpty(this.beanFactory.getBeanNamesForType(beanClass, true, false))) {
RootBeanDefinition beanDefinition = new RootBeanDefinition(beanClass);
beanDefinition.setSynthetic(true);
registry.registerBeanDefinition(name, beanDefinition);
}
}
}
这个类注册了两个bean:WebServerFactoryCustomizerBeanPostProcessor和ErrorPageRegistrarBeanPostProcessor。
/**
* 该 BeanPostProcessor 会应用将所有的 WebServerFactoryCustomizer,
* 应用于 WebServerFactory
*/
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 webServerFactory) {
// System.out.println("run: " + beanName);
postProcessBeforeInitialization(webServerFactory);
}
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();
}
}
run: tomcatServletWebServerFactory
这个 processor 的作用是获得所有定制器,然后执行定制器的方法, 在初始化阶段应用于 WebServerFactory,其中就包括 ServletWebServerFactoryAutoConfiguration中的ServletWebServerFactoryCustomizer,TomcatServletWebServerFactoryCustomizer
嵌入式Tomcat
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
static class EmbeddedTomcat {
@Bean
TomcatServletWebServerFactory tomcatServletWebServerFactory(
ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers,
ObjectProvider<TomcatContextCustomizer> contextCustomizers,
ObjectProvider<TomcatProtocolHandlerCustomizer<?>> protocolHandlerCustomizers) {
TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
factory.getTomcatConnectorCustomizers().addAll(connectorCustomizers.orderedStream().toList());
factory.getTomcatContextCustomizers().addAll(contextCustomizers.orderedStream().toList());
factory.getTomcatProtocolHandlerCustomizers().addAll(protocolHandlerCustomizers.orderedStream().toList());
return factory;
}
}
该类将TomcatServletWebServerFactory加入到了IOC容器。
@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
public class ServerProperties {
/**
* Server HTTP port.
*/
private Integer port;
/**
* Network address to which the server should bind.
*/
private InetAddress address;
// ......
}
这也是为什么我们平时使用server.port=80可以修改服务器的端口。spring boot在创建了服务器工厂之后,会通过postprocessors将属性写入工厂
createWebServer()
服务器的创建是在onRefresh()中进行的
下文是对此方法的解析。
@Override
protected void onRefresh() {
super.onRefresh();
try {
createWebServer();
}
catch (Throwable ex) {
throw new ApplicationContextException("Unable to start web server", ex);
}
}
super.onRefresh()
不重要
/**
* Initialize the theme capability.
*/
@Override
protected void onRefresh() {
this.themeSource = UiApplicationContextUtils.initThemeSource(this);
}
createWebServer()
ServletWebServerApplicationContext#createWebServer()
private void createWebServer() {
// 获取当前是否存在webServer
WebServer webServer = this.webServer;
ServletContext servletContext = getServletContext();
// 什么都不存在
if (webServer == null && servletContext == null) {
// 服务器开始创建
StartupStep createWebServer = getApplicationStartup().start("spring.boot.webserver.create");
// 获得 WebServer 的创建工厂
ServletWebServerFactory factory = getWebServerFactory();
// 记录创建过程使用的工厂
createWebServer.tag("factory", factory.getClass().toString());
// 创建 webServer
this.webServer = factory.getWebServer(getSelfInitializer());
// 服务器创建结束
createWebServer.end();
getBeanFactory().registerSingleton("webServerGracefulShutdown",
new WebServerGracefulShutdownLifecycle(this.webServer));
getBeanFactory().registerSingleton("webServerStartStop",
new WebServerStartStopLifecycle(this, this.webServer));
}
// 存在服务器,但不存在 servletContext
else if (servletContext != null) {
try {
getSelfInitializer().onStartup(servletContext);
}
catch (ServletException ex) {
throw new ApplicationContextException("Cannot initialize servlet context", ex);
}
}
// 初始化属性源
initPropertySources();
}
该方法中的主要语句为两句,1. 获得工厂,2. 工厂生产产品
// 获得 WebServer 的创建工厂
ServletWebServerFactory factory = getWebServerFactory();
// 创建 webServer
this.webServer = factory.getWebServer(getSelfInitializer());
getWebServerFactory()
ServletWebServerApplicationContext#getWebServerFactory()
/**
* 返回一个ServletWebServerFactory,该类用于创建一个嵌入式WebServer。默认情况下,该方法会
* 在context搜索一个合适的bean
* 返回:一个webserver工厂,不会为null
*/
protected ServletWebServerFactory getWebServerFactory() {
// Use bean names so that we don't consider the hierarchy
// 从 applicationContext 中获得 ServletWebServerFactory 类的名字
String[] beanNames = getBeanFactory().getBeanNamesForType(ServletWebServerFactory.class);
if (beanNames.length == 0) {
// 异常:找不到要使用的服务器,
throw new MissingWebServerFactoryBeanException(getClass(), ServletWebServerFactory.class,
WebApplicationType.SERVLET);
}
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);
}
根据调试内容,我们发现当前应用加载的是tomcatServletWebServerFactory,而这个bean是否非常眼熟,这个bean自动装载阶段Spring Boot加入到IOC容器中的。该工厂经过了postProcessor,来配置了属性。实际上,我们如果想在WebServerFactory中做一些属性操作,可以自己实现一些postProcessor
factory.getWebServer(...)
TomcatServletWebServerFactory#getWebServer(...)
有了工厂,现在使用工厂来创建WebServer。实际上,Tomcat本身就支持嵌入式启动,所以这里主要是将tomcat嵌入式启动可做的操作给做一遍。
@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {
if (this.disableMBeanRegistry) {
Registry.disableRegistry();
}
// 创建tomcat类
Tomcat tomcat = new Tomcat();
File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat"); // 临时目录
tomcat.setBaseDir(baseDir.getAbsolutePath());
// 为服务器配置生命周期监听器
for (LifecycleListener listener : this.serverLifecycleListeners) {
tomcat.getServer().addLifecycleListener(listener);
}
// 为tomcat配置连接器,默认http11Nio
Connector connector = new Connector(this.protocol);
connector.setThrowOnFailure(true);
tomcat.getService().addConnector(connector);
// 自定义连接器
customizeConnector(connector);
tomcat.setConnector(connector);
// 注册连接器对应的执行器
registerConnectorExecutor(tomcat, connector);
tomcat.getHost().setAutoDeploy(false);
// 配置引擎
configureEngine(tomcat.getEngine());
// 如果有额外的连接器
for (Connector additionalConnector : this.additionalTomcatConnectors) {
tomcat.getService().addConnector(additionalConnector);
registerConnectorExecutor(tomcat, additionalConnector);
}
// 配置上下文,通过initializers处理tomcat的context
prepareContext(tomcat.getHost(), initializers);
// 创建服务器,主要是封装tomcat
return getTomcatWebServer(tomcat);
}
下面的函数prepareContext会对Context进行set设置,之后进行configureContext
protected void prepareContext(Host host, ServletContextInitializer[] initializers) {
// 返回doc_root的根路径
File documentRoot = getValidDocumentRoot();
TomcatEmbeddedContext context = new TomcatEmbeddedContext();
if (documentRoot != null) {
context.setResources(new LoaderHidingResourceRoot(context));
}
context.setName(getContextPath()); // ""
context.setDisplayName(getDisplayName()); // application
context.setPath(getContextPath()); // ""
File docBase = (documentRoot != null) ? documentRoot : createTempDir("tomcat-docbase");
context.setDocBase(docBase.getAbsolutePath());
// 添加生命周期监听器
context.addLifecycleListener(new FixContextListener());
ClassLoader parentClassLoader = (this.resourceLoader != null) ? this.resourceLoader.getClassLoader()
: ClassUtils.getDefaultClassLoader();
context.setParentClassLoader(parentClassLoader);
// 默认的地域与字符集映射;例:JAPANESE -> UTF-8
resetDefaultLocaleMapping(context);
// 添加本地的一些映射关系
addLocaleMappings(context);
try {
context.setCreateUploadTargets(true);
}
catch (NoSuchMethodError ex) {
// Tomcat is < 8.5.39. Continue.
}
configureTldPatterns(context);
// webapploader
WebappLoader loader = new WebappLoader();
loader.setLoaderInstance(new TomcatEmbeddedWebappClassLoader(parentClassLoader));
loader.setDelegate(true);
context.setLoader(loader);
// 如果有默认servlet,则添加
if (isRegisterDefaultServlet()) {
addDefaultServlet(context);
}
// 如果有jsp,则添加
if (shouldRegisterJspServlet()) {
addJspServlet(context);
addJasperInitializer(context);
}
context.addLifecycleListener(new StaticResourceConfigurer(context));
ServletContextInitializer[] initializersToUse = mergeInitializers(initializers);
host.addChild(context);
// 配置tomcat的上下文,主要是配置初始化器
configureContext(context, initializersToUse);
// 后处理, 空的
postProcessContext(context);
}
在prepareContext中,比较重要的操作是configureContext(...),主要是设置initializers
/**
* 配置tomcat的context
* @param context the Tomcat context
* @param initializers initializers to apply
*/
protected void configureContext(Context context, ServletContextInitializer[] initializers) {
// 封装init,starter会在 tomcat 启动时触发
TomcatStarter starter = new TomcatStarter(initializers);
if (context instanceof TomcatEmbeddedContext embeddedContext) {
embeddedContext.setStarter(starter);
embeddedContext.setFailCtxIfServletStartFails(true);
}
// 添加容器初始化器,很重要,涉及到springboot与tomcat的交互
context.addServletContainerInitializer(starter, NO_CLASSES);
for (LifecycleListener lifecycleListener : this.contextLifecycleListeners) {
context.addLifecycleListener(lifecycleListener);
}
for (Valve valve : this.contextValves) {
context.getPipeline().addValve(valve);
}
// 当tomcat发生error时,触发errorPage
for (ErrorPage errorPage : getErrorPages()) {
org.apache.tomcat.util.descriptor.web.ErrorPage tomcatErrorPage = new org.apache.tomcat.util.descriptor.web.ErrorPage();
tomcatErrorPage.setLocation(errorPage.getPath()); // error
tomcatErrorPage.setErrorCode(errorPage.getStatusCode()); // 0
tomcatErrorPage.setExceptionType(errorPage.getExceptionName());
context.addErrorPage(tomcatErrorPage);
}
setMimeMappings(context);
// 配置cookie和session
configureSession(context);
configureCookieProcessor(context);
new DisableReferenceClearingContextCustomizer().customize(context);
for (String webListenerClassName : getWebListenerClassNames()) {
context.addApplicationListener(webListenerClassName);
}
for (TomcatContextCustomizer customizer : this.tomcatContextCustomizers) {
customizer.customize(context);
}
}
其中最重要的初始化器如图所示。其会触发spring boot的ServletWebServerApplicationContext#selfInitialize,触发时机则为容器的初始化阶段,因为TomcatStarter实现了ServletContainerInitializer接口
在selfInitialize函数中,Spring Boot会为其添加1个servlet,3个filter,1个listener
servlet: DispacherServlet,了解springmvc的应该知道他的重要性
filter:characterEncodingFilter、formContentFilter、requestContextFilter
listener:cglib动态生成的主类
getTomcatWebServer(...)
实际上直接,tomcat.start()就可以启动tomcat了,但spring boot依然为其包装了一层,以进行一些initialize()操作
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 " + getPortsDescription(false));
synchronized (this.monitor) {
try {
// 设置实例化的名字
addInstanceIdToEngineName();
Context context = findContext();
context.addLifecycleListener((event) -> {
if (context.equals(event.getSource()) && Lifecycle.START_EVENT.equals(event.getType())) {
// Remove service connectors so that protocol binding doesn't
// happen when the service is started.
removeServiceConnectors();
}
});
// 关闭绑定
disableBindOnInit();
// 启动服务器,进入apache.tomcat包执行。会去执行初始化
// 所做的初始化操作在上面说过了
this.tomcat.start();
// We can re-throw failure exception directly in the main thread
rethrowDeferredStartupExceptions();
try {
ContextBindings.bindClassLoader(context, context.getNamingToken(), getClass().getClassLoader());
}
catch (NamingException ex) {
// Naming is not enabled. Continue
}
// Unlike Jetty, all Tomcat threads are daemon threads. We create a
// blocking non-daemon to stop immediate shutdown
// 创建阻塞非守护线程防止程序关闭
startNonDaemonAwaitThread();
}
catch (Exception ex) {
stopSilently();
destroySilently();
throw new WebServerException("Unable to start embedded Tomcat", ex);
}
}
}
总结
在处理嵌入式容器阶段,SpringBoot主要是
- 通过
AutoConfigration加载对应的Tomcat工厂以及PostProcessorsRegistrar(Registrar会引入 BeanPostProcessor) - 在实例化容器工厂时,会通过
BeanPostProcessor对工厂进行Customizer(自定义) - 通过工厂创建tomcat,除了一些配置操作,最重要的是设置Tomcat的初始化器,在Tomcat启动阶段将
DispacherServlet作为servlet加载进去
其中第1步由自动装载触发,也就是run方法中的invokeBeanFactoryPostProcessors(beanFactory);
第2,3步,在run方法中的onRefresh()触发。