caychen的博客

业精于勤而荒于嬉,行成于思而毁于随。

【Spring Boot】(19)、Spring Boot嵌入式Servlet容器自动配置原理

    其中EmbeddedServletContainerAutoConfiguration是嵌入式Servlet容器的自动配置类,该类在spring-boot-autoconfigure-xxx.jar中的web模块可以找到。

@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration
@ConditionalOnWebApplication
@Import(BeanPostProcessorsRegistrar.class)
public class EmbeddedServletContainerAutoConfiguration {

    @Configuration
	@ConditionalOnClass({ Servlet.class, Tomcat.class })
	@ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT)
	public static class EmbeddedTomcat {

		@Bean
		public TomcatEmbeddedServletContainerFactory tomcatEmbeddedServletContainerFactory() {
			return new TomcatEmbeddedServletContainerFactory();
		}
	}
    
    @Configuration
	@ConditionalOnClass({ Servlet.class, Server.class, Loader.class,
			WebAppContext.class })
	@ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT)
	public static class EmbeddedJetty {

		@Bean
		public JettyEmbeddedServletContainerFactory jettyEmbeddedServletContainerFactory() {
			return new JettyEmbeddedServletContainerFactory();
		}

	}
    
    @Configuration
	@ConditionalOnClass({ Servlet.class, Undertow.class, SslClientAuthMode.class })
	@ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT)
	public static class EmbeddedUndertow {

		@Bean
		public UndertowEmbeddedServletContainerFactory undertowEmbeddedServletContainerFactory() {
			return new UndertowEmbeddedServletContainerFactory();
		}

	}
    
   	//other code...
}

在这个自动配置类中配置了三个容器工厂的Bean,分别是:

  • TomcatEmbeddedServletContainerFactory

  • JettyEmbeddedServletContainerFactory

  • UndertowEmbeddedServletContainerFactory

    这里以大家熟悉的Tomcat为例,首先Spring Boot会判断当前环境中是否引入了Servlet和Tomcat依赖,并且当前容器中没有自定义的EmbeddedServletContainerFactory的情况下,则创建Tomcat容器工厂。其他Servlet容器工厂也是同样的道理。

1)、EmbeddedServletContainerFactory:嵌入式Servlet容器工厂

public interface EmbeddedServletContainerFactory {

	EmbeddedServletContainer getEmbeddedServletContainer(
			ServletContextInitializer... initializers);
}

内部只有一个方法,用于获取嵌入式的Servlet容器。

该工厂接口主要有三个实现类,分别对应三种嵌入式Servlet容器的工厂类,如图所示:


2)、EmbeddedServletContainer:嵌入式Servlet容器

同样道理,对应三种嵌入式Servlet容器,如图所示:


3)、以Tomcat容器工厂TomcatEmbeddedServletContainerFactory类为例:

public class TomcatEmbeddedServletContainerFactory
		extends AbstractEmbeddedServletContainerFactory implements ResourceLoaderAware {
    
    //other code...
    
    @Override
	public EmbeddedServletContainer getEmbeddedServletContainer(
			ServletContextInitializer... initializers) {
        //创建一个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);
		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对象,返回一个嵌入式Tomcat容器,内部会启动该tomcat容器
		return getTomcatEmbeddedServletContainer(tomcat);
	}
}

看看TomcatEmbeddedServletContainerFactory#getTomcatEmbeddedServletContainer函数:

protected TomcatEmbeddedServletContainer getTomcatEmbeddedServletContainer(
    Tomcat tomcat) {
    return new TomcatEmbeddedServletContainer(tomcat, getPort() >= 0);
}

该函数很简单,就是来创建Tomcat容器并返回。

看看TomcatEmbeddedServletContainer类定义:

public class TomcatEmbeddedServletContainer implements EmbeddedServletContainer {

	public TomcatEmbeddedServletContainer(Tomcat tomcat, boolean autoStart) {
        Assert.notNull(tomcat, "Tomcat Server must not be null");
        this.tomcat = tomcat;
        this.autoStart = autoStart;
        
        //初始化嵌入式Tomcat容器,并启动Tomcat
        initialize();
    }
    
    private void initialize() throws EmbeddedServletContainerException {
		TomcatEmbeddedServletContainer.logger
				.info("Tomcat initialized with port(s): " + getPortsDescription(false));
		synchronized (this.monitor) {
			try {
				addInstanceIdToEngineName();
				try {
					final Context context = findContext();
					context.addLifecycleListener(new LifecycleListener() {

						@Override
						public void lifecycleEvent(LifecycleEvent 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();
							}
						}

					});

					// Start the server to trigger initialization listeners
                      //启动tomcat
					this.tomcat.start();

					// We can re-throw failure exception directly in the main thread
					rethrowDeferredStartupExceptions();

					try {
						ContextBindings.bindClassLoader(context, getNamingToken(context),
								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
					startDaemonAwaitThread();
				}
				catch (Exception ex) {
					containerCounter.decrementAndGet();
					throw ex;
				}
			}
			catch (Exception ex) {
				stopSilently();
				throw new EmbeddedServletContainerException(
						"Unable to start embedded Tomcat", ex);
			}
		}
	}
}

到这里就启动了嵌入式的Servlet容器,其他容器类似。

那么问题来了,我们对嵌入式容器的修改配置是如何生效的?

之前讲过可以通过修改ServerProperties相关配置或者自定义EmbeddedServletContainerCustomizer定制器两种方式来修改默认配置。而ServerProperties其实就是EmbeddedServletContainerCustomizer的子类,所以说到底还是EmbeddedServletContainerCustomizer起了修改的作用。

​ 其实在EmbeddedServletContainerAutoConfiguration类上导入了一个BeanPostProcessorsRegistrar类:

@Import(BeanPostProcessorsRegistrar.class)

该类主要用于给容器导入组件,看定义:

public static class BeanPostProcessorsRegistrar
			implements ImportBeanDefinitionRegistrar, BeanFactoryAware {

    private ConfigurableListableBeanFactory beanFactory;

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        if (beanFactory instanceof ConfigurableListableBeanFactory) {
            this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
        }
    }

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
                                        BeanDefinitionRegistry registry) {
        if (this.beanFactory == null) {
            return;
        }
        //注册了一个EmbeddedServletContainerCustomizerBeanPostProcessor后置处理器的Bean
        registerSyntheticBeanIfMissing(registry,
                                       "embeddedServletContainerCustomizerBeanPostProcessor",
                                       EmbeddedServletContainerCustomizerBeanPostProcessor.class);
        registerSyntheticBeanIfMissing(registry,
                                       "errorPageRegistrarBeanPostProcessor",
                                       ErrorPageRegistrarBeanPostProcessor.class);
    }

    private void registerSyntheticBeanIfMissing(BeanDefinitionRegistry registry,
                                                String name, Class<?> beanClass) {
        if (ObjectUtils.isEmpty(
            this.beanFactory.getBeanNamesForType(beanClass, true, false))) {
            RootBeanDefinition beanDefinition = new RootBeanDefinition(beanClass);
            beanDefinition.setSynthetic(true);
            registry.registerBeanDefinition(name, beanDefinition);
        }
    }

}

后置处理器:在bean初始化前(创建完成,还未属性赋值),会执行初始化工作。

所以重点是在registerBeanDefinitions方法中向容器中导入了EmbeddedServletContainerCustomizerBeanPostProcessor类型的Bean:

public class EmbeddedServletContainerCustomizerBeanPostProcessor
		implements BeanPostProcessor, BeanFactoryAware {

    //初始化之前
    @Override
	public Object postProcessBeforeInitialization(Object bean, String beanName)
			throws BeansException {
		if (bean instanceof ConfigurableEmbeddedServletContainer) {
			postProcessBeforeInitialization((ConfigurableEmbeddedServletContainer) bean);
		}
		return bean;
	}
    
    private void postProcessBeforeInitialization(
			ConfigurableEmbeddedServletContainer bean) {
        //获取所有的定制器,调用每个定制器的customize方法,给Servlet容器进行属性赋值
		for (EmbeddedServletContainerCustomizer customizer : getCustomizers()) {
			customizer.customize(bean);
		}
	}
    
    //从IOC容器中获取所有类型为EmbeddedServletContainerCustomizer的定制器
    private Collection<EmbeddedServletContainerCustomizer> getCustomizers() {
		if (this.customizers == null) {
			// Look up does not include the parent context
			this.customizers = new ArrayList<EmbeddedServletContainerCustomizer>(
                //类型为EmbeddedServletContainerCustomizer的Bean
					this.beanFactory
							.getBeansOfType(EmbeddedServletContainerCustomizer.class,
									false, false)
							.values());
			Collections.sort(this.customizers, AnnotationAwareOrderComparator.INSTANCE);
			this.customizers = Collections.unmodifiableList(this.customizers);
		}
		return this.customizers;
	}
    
    //other code...
}

所以之前介绍可以向容器中添加一个自定义的EmbeddedServletContainerCustomizer类型的组件,用于自定义属性配置,然后在导入的后置处理器中获取到该组件,并调用该自定义组件的customize方法,来修改默认的属性配置。

总结:

(1)、Spring Boot根据导入容器类型的依赖情况,会给容器中添加相应的EmbeddedServletContainerFactory嵌入式Servlet容器工厂;

        (2)、应用程序一旦导入了嵌入式Servlet容器依赖,就会触动后置处理器EmbeddedServletContainerCustomizerBeanPostProcessor

        (3)、后置处理器从IOC容器中获取所有的EmbeddedServletContainerCustomizer类型的嵌入式Servlet容器定制器,并调用每个定制器的定制方法customize,从而修改默认属性配置。

====================打个广告,欢迎关注====================

QQ:
412425870
微信公众号:Cay课堂

csdn博客:
http://blog.csdn.net/caychen
码云:
https://gitee.com/caychen/
github:
https://github.com/caychen

点击群号或者扫描二维码即可加入QQ群:

328243383(1群)




点击群号或者扫描二维码即可加入QQ群:

180479701(2群)




阅读更多
版权声明:本文为博主原创文章,未经博主允许不得转载。请联系博主或者emailTo:412425870@qq.com,谢谢! https://blog.csdn.net/caychen/article/details/80346331
所属专栏: Spring Boot技术
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

不良信息举报

【Spring Boot】(19)、Spring Boot嵌入式Servlet容器自动配置原理

最多只允许输入30个字

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭