1. 程式人生 > >【Spring Boot】(19)、Spring Boot嵌入式Servlet容器自動配置原理

【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群)