springboot-web开发嵌入式servlet容器自动配置原理和启动原理

一、servlet自动配置原理

servlet自动配置是EmbeddedServletContainerAutoConfiguration,我们以tomcat容器来分析一下这个类代码

@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration
@ConditionalOnWebApplication
自动导入BeanPostProcessorsRegistrar,我们修改servlet配置通过这个类生效
@Import(BeanPostProcessorsRegistrar.class)
public class EmbeddedServletContainerAutoConfiguration {
    这个是tomcat容器
	@Configuration
    必须存在类tomcat.class
	@ConditionalOnClass({ Servlet.class, Tomcat.class })
    如果不存在EmbeddedServletContainerFactory则配置tomcat服务器
	@ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT)
	public static class EmbeddedTomcat {
		@Bean
		public TomcatEmbeddedServletContainerFactory tomcatEmbeddedServletContainerFactory() {
			return new TomcatEmbeddedServletContainerFactory();
		}
	}

配置类返回了TomcatEmbeddedServletContainerFactory,这个类继承了AbstractEmbeddedServletContainerFactory,而它实现了EmbeddedServletContainerFactory,

public interface EmbeddedServletContainerFactory {
    
	EmbeddedServletContainer getEmbeddedServletContainer(
			ServletContextInitializer... initializers);

这里面只有一个方法getEmbeddedServletContainer,所以我们需要查看TomcatEmbeddedServletContainerFactory中对这个方法的重写

    @Override
	public EmbeddedServletContainer getEmbeddedServletContainer(
			ServletContextInitializer... initializers) {
		Tomcat tomcat = new 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);
		return getTomcatEmbeddedServletContainer(tomcat);
	}

这个方法创建了tomcat对象,并且设置了它的属性,最后返回了EmbeddedServletContainer,我们查看一下方法getTomcatEmbeddedServletContainer

public TomcatEmbeddedServletContainer(Tomcat tomcat, boolean autoStart) {
		Assert.notNull(tomcat, "Tomcat Server must not be null");
		this.tomcat = tomcat;
		this.autoStart = autoStart;
		initialize();
	}
	private void initialize() throws EmbeddedServletContainerException {
		synchronized (this.monitor) {
			try {
				addInstanceIdToEngineName();
				try {
					removeServiceConnectors();
					this.tomcat.start();
					rethrowDeferredStartupExceptions();
					Context context = findContext();
					try {
						ContextBindings.bindClassLoader(context, getNamingToken(context),
								getClass().getClassLoader());
					}
					catch (NamingException ex) {
					}
					startDaemonAwaitThread();
				}
				catch (Exception ex) {
					containerCounter.decrementAndGet();
					throw ex;
				}
			}
			catch (Exception ex) {
				throw new EmbeddedServletContainerException(
						"Unable to start embedded Tomcat", ex);
			}
		}
	}

这个方法里创建tomcat对象的最后启动了tomcat容器this.tomcat.start();

下面来介绍一下我们修改servlet容器是在哪里生效的,我们看看类BeanPostProcessorsRegistrar

public static class BeanPostProcessorsRegistrar
			implements ImportBeanDefinitionRegistrar, BeanFactoryAware {
	    @Override
		public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
				BeanDefinitionRegistry registry) {
			if (this.beanFactory == null) {
				return;
			}
			registerSyntheticBeanIfMissing(registry,
					"embeddedServletContainerCustomizerBeanPostProcessor",
					EmbeddedServletContainerCustomizerBeanPostProcessor.class);
			registerSyntheticBeanIfMissing(registry,
					"errorPageRegistrarBeanPostProcessor",
					ErrorPageRegistrarBeanPostProcessor.class);
		}

这里注册了类EmbeddedServletContainerCustomizerBeanPostProcessor,这个类就是servlet属性的定制类,它实现了BeanPostProcessor,这个类的作用是在创建bean实例前后可以对修改bean的属性,这里对前置方法postProcessBeforeInitialization进行了重写,前置方法里调用了EmbeddedServletContainerCustomizer的customize,所以我们对容器配置的修改就生效了

@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName)
			throws BeansException {
		if (bean instanceof ConfigurableEmbeddedServletContainer) {
			postProcessBeforeInitialization((ConfigurableEmbeddedServletContainer) bean);
		}
		return bean;
	}
private void postProcessBeforeInitialization(
			ConfigurableEmbeddedServletContainer bean) {
        这里获取了所有的EmbeddedServletContainerCustomizer,进行了调用customize
		for (EmbeddedServletContainerCustomizer customizer : getCustomizers()) {
			customizer.customize(bean);
		}
	}

我们在application.properties中修改servlet属性,ServerProperties同样实现了EmbeddedServletContainerCustomizer

 

二、servlet启动原理

启动web服务时会创建GenericWebApplicationContext,而我们的servlet容器EmbeddedWebApplicationContext继承了GenericWebApplicationContext,在重写方法里有个onrefresh,这个方法是bean容器的扩展方法,在这个方法里创建了EmbeddedServletContainerFactory

@Override
	protected void onRefresh() {
		super.onRefresh();
		try {
			createEmbeddedServletContainer();
		}
		catch (Throwable ex) {
			throw new ApplicationContextException("Unable to start embedded container",
					ex);
		}
	}
private void createEmbeddedServletContainer() {
		EmbeddedServletContainer localContainer = this.embeddedServletContainer;
		ServletContext localServletContext = getServletContext();
		if (localContainer == null && localServletContext == null) {
			EmbeddedServletContainerFactory containerFactory = getEmbeddedServletContainerFactory();
			this.embeddedServletContainer = containerFactory
					.getEmbeddedServletContainer(getSelfInitializer());
		}
		else if (localServletContext != null) {
			try {
				getSelfInitializer().onStartup(localServletContext);
			}
			catch (ServletException ex) {
				throw new ApplicationContextException("Cannot initialize servlet context",
						ex);
			}
		}
		initPropertySources();
	}

这里创建了EmbeddedServletContainerFactory,而tomcat容器实现了这个类,所以在这时创建tomcat对象的过程就执行了,从而就启动了tomcat

你可能感兴趣的:(SpringBoot)