spring boot 源码解析12-servlet容器的建立
前言spring boot 一般都会加入如下依赖:<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency>加入后,就会启动一个嵌入式容器,其默认启动的是tomcat.那么他是如何启动的,我们接下来就分析下
前言
spring boot 一般都会加入如下依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
加入后,就会启动一个嵌入式容器,其默认启动的是tomcat.那么他是如何启动的,我们接下来就分析下.
解析
通过之前的文章我们知道了在SpringApplication#run方法的第9步会调用AbstractApplicationContext#refresh方法,而在该方法的第5步中会调用invokeBeanFactoryPostProcessors方法,在该方法会依次调用BeanFactoryPostProcessors的postProcessBeanDefinitionRegistry 进行处理.其中ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry 会依次的扫描配置类,然后进行注册.当我们加入spring-boot-starter-web 依赖时,就会加入EmbeddedServletContainerAutoConfiguration这么一个配置类.代码如下:
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) @Configuration @ConditionalOnWebApplication @Import(BeanPostProcessorsRegistrar.class) public class EmbeddedServletContainerAutoConfiguration { /** * Nested configuration if Tomcat is being used. */ @Configuration @ConditionalOnClass({ Servlet.class, Tomcat.class }) @ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT) public static class EmbeddedTomcat { @Bean public TomcatEmbeddedServletContainerFactory tomcatEmbeddedServletContainerFactory() { return new TomcatEmbeddedServletContainerFactory(); } } /** * Nested configuration if Jetty is being used. */ @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(); } } /** * Nested configuration if Undertow is being used. */ @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(); } } /** * Registers a {@link EmbeddedServletContainerCustomizerBeanPostProcessor}. Registered * via {@link ImportBeanDefinitionRegistrar} for early registration. * 在EmbeddedServletContainerAutoConfiguration自动化配置类中被导入, * 实现了BeanFactoryAware接口(BeanFactory会被自动注入进来)和ImportBeanDefinitionRegistrar接口 * (会被ConfigurationClassBeanDefinitionReader解析并注册到Spring容器中) */ 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; } // 如果Spring容器中不存在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); } } } }
首先该类有@Configuration注解,因此会被ConfigurationClassPostProcessor处理,又因为有@ConditionalOnWebApplication注解.该注解如下:
@Target({ ElementType.TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented @Conditional(OnWebApplicationCondition.class) public @interface ConditionalOnWebApplication { }
引入了OnWebApplicationCondition,代码如下:
class OnWebApplicationCondition extends SpringBootCondition { private static final String WEB_CONTEXT_CLASS = "org.springframework.web.context." + "support.GenericWebApplicationContext"; @Override public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) { // 1. 检查是否被@ConditionalOnWebApplication 注解 boolean required = metadata .isAnnotated(ConditionalOnWebApplication.class.getName()); // 2. 判断是否是WebApplication ConditionOutcome outcome = isWebApplication(context, metadata, required); if (required && !outcome.isMatch()) { // 3. 如果有@ConditionalOnWebApplication 注解,但是不是WebApplication环境,则返回不匹配 return ConditionOutcome.noMatch(outcome.getConditionMessage()); } if (!required && outcome.isMatch()) { // 4. 如果没有被@ConditionalOnWebApplication 注解,但是是WebApplication环境,则返回不匹配 return ConditionOutcome.noMatch(outcome.getConditionMessage()); } // 5. 如果被@ConditionalOnWebApplication 注解,并且是WebApplication环境,则返回不匹配 return ConditionOutcome.match(outcome.getConditionMessage()); } private ConditionOutcome isWebApplication(ConditionContext context, AnnotatedTypeMetadata metadata, boolean required) { ConditionMessage.Builder message = ConditionMessage.forCondition( ConditionalOnWebApplication.class, required ? "(required)" : ""); // 1. 判断GenericWebApplicationContext是否在类路径中,如果不存在,则返回不匹配 if (!ClassUtils.isPresent(WEB_CONTEXT_CLASS, context.getClassLoader())) { return ConditionOutcome .noMatch(message.didNotFind("web application classes").atAll()); } // 2. 容器里是否有名为session的scope,如果存在,则返回匹配 if (context.getBeanFactory() != null) { String[] scopes = context.getBeanFactory().getRegisteredScopeNames(); if (ObjectUtils.containsElement(scopes, "session")) { return ConditionOutcome.match(message.foundExactly("'session' scope")); } } // 3. Environment是否为StandardServletEnvironment,如果是的话,则返回匹配 if (context.getEnvironment() instanceof StandardServletEnvironment) { return ConditionOutcome .match(message.foundExactly("StandardServletEnvironment")); } // 4. 当前ResourceLoader是否为WebApplicationContext,如果是,则返回匹配 if (context.getResourceLoader() instanceof WebApplicationContext) { return ConditionOutcome.match(message.foundExactly("WebApplicationContext")); } // 5. 其他情况,返回不匹配. return ConditionOutcome.noMatch(message.because("not a web application")); } }
其中getMatchOutcome是判断逻辑,做了5件事:
- 检查是否被@ConditionalOnWebApplication 注解
通过调用isWebApplication判断是否是Web环境,
- 判断GenericWebApplicationContext是否在类路径中,如果不存在,则返回不匹配
- 容器里是否有名为session的scope,如果存在,则返回匹配
- Environment是否为StandardServletEnvironment,如果是的话,则返回匹配
- 当前ResourceLoader是否为WebApplicationContext,如果是,则返回匹配
- 其他情况,返回不匹配.
如果有@ConditionalOnWebApplication 注解,但是不是WebApplication环境,则返回不匹配
- 如果没有被@ConditionalOnWebApplication 注解,但是是WebApplication环境,则返回不匹配
- 如果被@ConditionalOnWebApplication 注解,并且是WebApplication环境,则返回不匹配
那么该类是在什么时候调用的呢?调用链如下:
org.springframework.context.annotation.ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry--> org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions--> org.springframework.context.annotation.ConfigurationClassParser#parse--> org.springframework.context.annotation.ConfigurationClassParser#parse--> org.springframework.context.annotation.ConfigurationClassParser#processConfigurationClass--> org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClass--> org.springframework.context.annotation.ConditionEvaluator#shouldSkip--> org.springframework.boot.autoconfigure.condition.SpringBootCondition#matches--> org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition#getMatchOutcome
接下来就开始对该配置类进行解析了,通过上篇文章可以知道,会首先处理内部类,那么由于该类有4个内部类.如下:
EmbeddedTomcat EmbeddedJetty EmbeddedUndertow BeanPostProcessorsRegistrar
其中BeanPostProcessorsRegistrar 没有@Configuration注解,因此该类不会被处理.
还是由之前可知,会因此判断该类是否能够被扫描,那么它们能够被扫描的条件分别为:
- EmbeddedTomcat –> 当前类路径下存在 Servlet.class, Tomcat.class,并且不含有类型为EmbeddedServletContainerFactory的bean,则该配置类生效.
- EmbeddedJetty –> 当前类路径下存在 Servlet.class, Server.class, Loader.class,WebAppContext.class ,并且不含有类型为EmbeddedServletContainerFactory的bean,则该配置类生效.
- EmbeddedUndertow–> 当前类路径下存在 Servlet.class, Undertow.class, SslClientAuthMode.class ,并且不含有类型为EmbeddedServletContainerFactory的bean,则该配置类生效.
那么当我们加入spring-boot-starter-web依赖时,默认加入了spring-boot-starter-tomcat 依赖,因此 EmbeddedTomcat 会被扫描处理.
还是由之前可知,会依次扫描EmbeddedTomcat 中被@Bean 注解的方法,因此tomcatEmbeddedServletContainerFactory方法会被处理,向BeanDefinitionRegistry注册一个id 为tomcatEmbeddedServletContainerFactory class 为 TomcatEmbeddedServletContainerFactory的bean.
还是由之前的内容可知,当EmbeddedServletContainerAutoConfiguration中的内部类处理完后,接下来会处理@Import 注解, 会依次扫描@Import所引入的类,由于此时只有一个–>BeanPostProcessorsRegistrar, 由于此类是ImportBeanDefinitionRegistrar的子类,因此为加入到ConfigurationClass的ImportBeanDefinitionRegistrar中,那么该类何时被调用呢?如下:
其registerBeanDefinitions代码如下:
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { if (this.beanFactory == null) { return; } // 如果Spring容器中不存在EmbeddedServletContainerCustomizerBeanPostProcessor类型的bean // 那么就注册一个 registerSyntheticBeanIfMissing(registry, "embeddedServletContainerCustomizerBeanPostProcessor", EmbeddedServletContainerCustomizerBeanPostProcessor.class); registerSyntheticBeanIfMissing(registry, "errorPageRegistrarBeanPostProcessor", ErrorPageRegistrarBeanPostProcessor.class); }
- 如果beanFactory等于null,则直接return.此处是不会发生的.因为在实例化时,就会向其注入beanFactory,因为其实现了BeanFactoryAware.
- 向容器注册id 为 embeddedServletContainerCustomizerBeanPostProcessor,类型为EmbeddedServletContainerCustomizerBeanPostProcessor的bean
- 向容器注册id 为 errorPageRegistrarBeanPostProcessor,类型为ErrorPageRegistrarBeanPostProcessor的bean.
还是在org.springframework.context.support.AbstractApplicationContext#refresh中的第9步,会调用onRefresh 这个扩展点,此时调用的是org.springframework.boot.context.embedded.EmbeddedWebApplicationContext#onRefresh,如下:
protected void onRefresh() { super.onRefresh(); try { createEmbeddedServletContainer(); } catch (Throwable ex) { throw new ApplicationContextException("Unable to start embedded container", ex); } }
其中调用createEmbeddedServletContainer 进行创建容器.如下:
private void createEmbeddedServletContainer() { EmbeddedServletContainer localContainer = this.embeddedServletContainer; // 1. 获得ServletContext ServletContext localServletContext = getServletContext(); if (localContainer == null && localServletContext == null) { // 2 内置Servlet容器和ServletContext都还没初始化的时候执行 // 2.1 获取自动加载的工厂 EmbeddedServletContainerFactory containerFactory = getEmbeddedServletContainerFactory(); // 2.2 获取Servlet初始化器并创建Servlet容器,依次调用Servlet初始化器中的onStartup方法 this.embeddedServletContainer = containerFactory .getEmbeddedServletContainer(getSelfInitializer()); } else if (localServletContext != null) { // 3. 内置Servlet容器已经初始化但是ServletContext还没初始化,则进行初始化.一般不会到这里 try { getSelfInitializer().onStartup(localServletContext); } catch (ServletException ex) { throw new ApplicationContextException("Cannot initialize servlet context", ex); } } // 4. 初始化PropertySources initPropertySources(); }
4件事
- 获得ServletContext
如果内置Servlet容器和ServletContext都还没初始化
- 获取EmbeddedServletContainerFactory. 此时获得的是TomcatEmbeddedServletContainerFactory
- 获取Servlet初始化器并创建Servlet容器,依次调用Servlet初始化器中的onStartup方法
- 内置Servlet容器已经初始化但是ServletContext还没初始化,则进行初始化.一般不会到这里
初始化PropertySources,最终调用WebApplicationContextUtils#initServletPropertySources.代码如下:
public static void initServletPropertySources( MutablePropertySources propertySources, ServletContext servletContext, ServletConfig servletConfig) { Assert.notNull(propertySources, "'propertySources' must not be null"); if (servletContext != null && propertySources.contains(StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME) && propertySources.get(StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME) instanceof StubPropertySource) { propertySources.replace(StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME, new ServletContextPropertySource(StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME, servletContext)); } if (servletConfig != null && propertySources.contains(StandardServletEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME) && propertySources.get(StandardServletEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME) instanceof StubPropertySource) { propertySources.replace(StandardServletEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME, new ServletConfigPropertySource(StandardServletEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME, servletConfig)); } }
其中2.1 获得TomcatEmbeddedServletContainerFactory方法如下:
protected EmbeddedServletContainerFactory getEmbeddedServletContainerFactory() { // Use bean names so that we don't consider the hierarchy // 1. 从BeanFactory中获得EmbeddedServletContainerFactory 类型的容器 String[] beanNames = getBeanFactory() .getBeanNamesForType(EmbeddedServletContainerFactory.class); // 2. 如果没有的话,或者如果有大于1个的话,抛出异常 if (beanNames.length == 0) { throw new ApplicationContextException( "Unable to start EmbeddedWebApplicationContext due to missing " + "EmbeddedServletContainerFactory bean."); } if (beanNames.length > 1) { throw new ApplicationContextException( "Unable to start EmbeddedWebApplicationContext due to multiple " + "EmbeddedServletContainerFactory beans : " + StringUtils.arrayToCommaDelimitedString(beanNames)); } // 3. 获得实例 return getBeanFactory().getBean(beanNames[0], EmbeddedServletContainerFactory.class); }
最终调用了AbstractBeanFactory#getBean,该bean触发了bean的实例化,在实例化的过程中,会触发一系列的扩展点的调用.其中,在AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInitialization中,会调用一系列的BeanPostProcessor.在当前场景有12个,如下:
org.springframework.context.support.ApplicationContextAwareProcessor, org.springframework.boot.context.embedded.WebApplicationContextServletContextAwareProcessor, org.springframework.context.annotation.ConfigurationClassPostProcessor$ImportAwareBeanPostProcessor, org.springframework.context.support.PostProcessorRegistrationDelegate$BeanPostProcessorChecker, org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor, org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizerBeanPostProcessor, org.springframework.boot.web.servlet.ErrorPageRegistrarBeanPostProcessor, org.springframework.context.annotation.CommonAnnotationBeanPostProcessor, org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor, org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor, org.springframework.context.support.ApplicationListenerDetector
其中真正发挥作用的有EmbeddedServletContainerCustomizerBeanPostProcessor,ErrorPageRegistrarBeanPostProcessor.其实现分别如下:
EmbeddedServletContainerCustomizerBeanPostProcessor#postProcessBeforeInitialization
代码如下:
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { // 在Spring容器中寻找ConfigurableEmbeddedServletContainer类型的bean,SpringBoot内部的3种内置Servlet容器工厂都实现了这个接口,该接口的作用就是进行Servlet容器的配置 // 比如添加Servlet初始化器addInitializers、添加错误页addErrorPages、设置session超时时间setSessionTimeout、设置端口setPort等等 if (bean instanceof ConfigurableEmbeddedServletContainer) { postProcessBeforeInitialization((ConfigurableEmbeddedServletContainer) bean); } return bean; }
调用
private void postProcessBeforeInitialization( ConfigurableEmbeddedServletContainer bean) { for (EmbeddedServletContainerCustomizer customizer : getCustomizers()) { customizer.customize(bean); } }
通过beanFactory 获得EmbeddedServletContainerCustomizer类型的bean,对于当前场景,有4个.如下:
org.springframework.boot.autoconfigure.websocket.TomcatWebSocketContainerCustomizer, org.springframework.boot.autoconfigure.web.HttpEncodingAutoConfiguration$LocaleCharsetMappingsCustomizer, org.springframework.boot.autoconfigure.web.ServerProperties, org.springframework.boot.autoconfigure.web.ServerPropertiesAutoConfiguration$DuplicateServerPropertiesDetector
TomcatWebSocketContainerCustomizer#customize
代码如下:
public void customize(ConfigurableEmbeddedServletContainer container) { if (getContainerType().isAssignableFrom(container.getClass())) { doCustomize((T) container); } }
调用
public void doCustomize(TomcatEmbeddedServletContainerFactory tomcatContainer) { tomcatContainer.addContextCustomizers(new TomcatContextCustomizer() { @Override public void customize(Context context) { addListener(context, findListenerType()); } }); }
向tomcatContainer 添加了2个ApplicationListener:
- org.apache.tomcat.websocket.server.WsContextListener
- org.apache.catalina.deploy.ApplicationListener(tomcat7)或者org.apache.tomcat.util.descriptor.web.ApplicationListener(tomcat8)
LocaleCharsetMappingsCustomizer#customize
代码如下:
public void customize(ConfigurableEmbeddedServletContainer container) { if (this.properties.getMapping() != null) { container.setLocaleCharsetMappings(this.properties.getMapping()); } }
没有执行
ServerProperties#customize
public void customize(ConfigurableEmbeddedServletContainer container) { if (getPort() != null) { container.setPort(getPort()); } if (getAddress() != null) { container.setAddress(getAddress()); } if (getContextPath() != null) { container.setContextPath(getContextPath()); } if (getDisplayName() != null) { container.setDisplayName(getDisplayName()); } if (getSession().getTimeout() != null) { container.setSessionTimeout(getSession().getTimeout()); } container.setPersistSession(getSession().isPersistent()); container.setSessionStoreDir(getSession().getStoreDir()); if (getSsl() != null) { container.setSsl(getSsl()); } if (getJspServlet() != null) { container.setJspServlet(getJspServlet()); } if (getCompression() != null) { container.setCompression(getCompression()); } container.setServerHeader(getServerHeader()); if (container instanceof TomcatEmbeddedServletContainerFactory) { getTomcat().customizeTomcat(this, (TomcatEmbeddedServletContainerFactory) container); } if (container instanceof JettyEmbeddedServletContainerFactory) { getJetty().customizeJetty(this, (JettyEmbeddedServletContainerFactory) container); } if (container instanceof UndertowEmbeddedServletContainerFactory) { getUndertow().customizeUndertow(this, (UndertowEmbeddedServletContainerFactory) container); } // 添加SessionConfiguringInitializer这个Servlet初始化器 // SessionConfiguringInitializer初始化器的作用是基于ServerProperties的内部静态类Session设置Servlet中session和cookie的配置 container.addInitializers(new SessionConfiguringInitializer(this.session)); // 添加InitParameterConfiguringServletContextInitializer初始化器 // InitParameterConfiguringServletContextInitializer初始化器的作用是基于ServerProperties的contextParameters配置设置到ServletContext的init param中 container.addInitializers(new InitParameterConfiguringServletContextInitializer( getContextParameters())); }
- 设置属性
- 添加SessionConfiguringInitializer这个Servlet初始化器,SessionConfiguringInitializer初始化器的作用是基于ServerProperties的内部静态类Session设置Servlet中session和cookie的配置。
- 添加InitParameterConfiguringServletContextInitializer初始化器,InitParameterConfiguringServletContextInitializer初始化器的作用是基于ServerProperties的contextParameters配置设置到ServletContext的init param中
那么有个问题,这些属性是何时注入的呢? 在调用org.springframework.boot.context.embedded.EmbeddedWebApplicationContext#getEmbeddedServletContainerFactory,获得EmbeddedServletContainerFactory时,会调用org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInitialization,进行扩展点的执行,其中EmbeddedServletContainerCustomizerBeanPostProcessor#postProcessBeforeInitialization执行时,会触发 加载 EmbeddedServletContainerCustomizer 类型的bean, ServerProperties实现了EmbeddedServletContainerCustomizer接口,因此会在此时被加载.
同样在加载过程中,会调用AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInitialization.因此会触发ConfigurationPropertiesBindingPostProcessor#postProcessBeforeInitialization的调用,由于该类有@ConfigurationProperties 注解,因此会最终调用org.springframework.boot.bind.PropertiesConfigurationFactory#bindPropertiesToTarget,其后就开始真正的属性绑定,调用链如下:org.springframework.boot.bind.PropertiesConfigurationFactory.doBindPropertiesToTarget()--> org.springframework.validation.DataBinder.bind(PropertyValues)--> org.springframework.validation.DataBinder.doBind(MutablePropertyValues)--> org.springframework.boot.bind.RelaxedDataBinder.doBind(MutablePropertyValues)--> org.springframework.validation.DataBinder.doBind(MutablePropertyValues)--> org.springframework.validation.DataBinder.applyPropertyValues(MutablePropertyValues)--> org.springframework.beans.AbstractPropertyAccessor.setPropertyValues(PropertyValues, boolean, boolean)--> org.springframework.boot.bind.RelaxedDataBinder.RelaxedBeanWrapper.setPropertyValue(PropertyValue)--> org.springframework.beans.AbstractNestablePropertyAccessor.setPropertyValue(PropertyValue)--> org.springframework.beans.AbstractNestablePropertyAccessor.setPropertyValue(PropertyTokenHolder, PropertyValue)--> org.springframework.beans.AbstractNestablePropertyAccessor.processLocalProperty(PropertyTokenHolder, PropertyValue)--> org.springframework.beans.BeanWrapperImpl.BeanPropertyHandler.setValue(Object, Object)--> java.lang.reflect.Method.invoke(Object, Object...)--> org.springframework.boot.autoconfigure.web.ServerProperties.setPort(Integer)
DuplicateServerPropertiesDetector#customize
代码如下:
public void customize(ConfigurableEmbeddedServletContainer container) { // ServerProperties handles customization, this just checks we only have // a single bean String[] serverPropertiesBeans = this.applicationContext .getBeanNamesForType(ServerProperties.class); Assert.state(serverPropertiesBeans.length == 1, "Multiple ServerProperties beans registered " + StringUtils .arrayToCommaDelimitedString(serverPropertiesBeans)); }
只是做检查,ServerProperties类型的bean 是否为1个
ErrorPageRegistrarBeanPostProcessor#postProcessBeforeInitialization
代码如下:
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof ErrorPageRegistry) { postProcessBeforeInitialization((ErrorPageRegistry) bean); } return bean; }
调用
private void postProcessBeforeInitialization(ErrorPageRegistry registry) { for (ErrorPageRegistrar registrar : getRegistrars()) { registrar.registerErrorPages(registry); } }
在此处获得类型为ErrorPageRegistrar的bean,然后依次调用ErrorPageRegistry#registerErrorPages进行注册,默认情况下,只有ErrorPageCustomizer一个.
代码如下:public void registerErrorPages(ErrorPageRegistry errorPageRegistry) { ErrorPage errorPage = new ErrorPage(this.properties.getServletPrefix() + this.properties.getError().getPath()); errorPageRegistry.addErrorPages(errorPage); }
path为/error
接下来我们分析创建容器的第2.2步. TomcatEmbeddedServletContainerFactory#getEmbeddedServletContainer代码如下:
public EmbeddedServletContainer getEmbeddedServletContainer( ServletContextInitializer... initializers) { // 1. 实例化Tomcat Tomcat tomcat = new Tomcat(); // 2. 设置临时目录 File baseDir = (this.baseDirectory != null ? this.baseDirectory : createTempDir("tomcat")); tomcat.setBaseDir(baseDir.getAbsolutePath()); // 3. 添加Connector Connector connector = new Connector(this.protocol); tomcat.getService().addConnector(connector); // 4. 个性化设置 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); }
8件事:
- 实例化Tomcat
- 设置临时目录
- 添加Connector,protocol 为 org.apache.coyote.http11.Http11NioProtocol
个性化设置.代码如下:
protected void customizeConnector(Connector connector) { int port = (getPort() >= 0 ? getPort() : 0); connector.setPort(port); if (StringUtils.hasText(this.getServerHeader())) { connector.setAttribute("server", this.getServerHeader()); } if (connector.getProtocolHandler() instanceof AbstractProtocol) { customizeProtocol((AbstractProtocol<?>) connector.getProtocolHandler()); } if (getUriEncoding() != null) { connector.setURIEncoding(getUriEncoding().name()); } // If ApplicationContext is slow to start we want Tomcat not to bind to the socket // prematurely... connector.setProperty("bindOnInit", "false"); if (getSsl() != null && getSsl().isEnabled()) { customizeSsl(connector); } if (getCompression() != null && getCompression().getEnabled()) { customizeCompression(connector); } for (TomcatConnectorCustomizer customizer : this.tomcatConnectorCustomizers) { customizer.customize(connector); } }
- 设置端口号 默认是 8080
- 设置server
- 此时ProtocolHandler 为Http11NioProtocol,是AbstractProtocol 的子类,因此会执行该步骤,设置地址.
- 设置编码,默认是 UTF-8
- 如果配置了ssl,则进行ssl的设置,默认情况下不会执行
- 如果配置了压缩,则进行压缩的配置,默认不会执行
- 遍历tomcatConnectorCustomizers,进行个性化配置,默认是不存在的
- 配置引擎
- 添加Connector,一般情况下是没有的
- 准备上下文
- 实例化TomcatEmbeddedServletContainer
接着在org.springframework.context.support.AbstractApplicationContext#refresh中的第12步.会执行如下代码:
protected void finishRefresh() { super.finishRefresh(); EmbeddedServletContainer localContainer = startEmbeddedServletContainer(); if (localContainer != null) { // 发布EmbeddedServletContainerInitializedEvent事件 publishEvent( new EmbeddedServletContainerInitializedEvent(this, localContainer)); } }
3件事:
- 调用AbstractApplicationContext#finishRefresh
- 启动容器
- 发布EmbeddedServletContainerInitializedEvent事件
其中第2步,代码如下:
private EmbeddedServletContainer startEmbeddedServletContainer() { EmbeddedServletContainer localContainer = this.embeddedServletContainer; if (localContainer != null) { localContainer.start(); } return localContainer; }
最终执行TomcatEmbeddedServletContainer#start,代码如下:
public void start() throws EmbeddedServletContainerException { synchronized (this.monitor) { if (this.started) { return; } try { addPreviouslyRemovedConnectors(); Connector connector = this.tomcat.getConnector(); if (connector != null && this.autoStart) { startConnector(connector); } checkThatConnectorsHaveStarted(); this.started = true; TomcatEmbeddedServletContainer.logger .info("Tomcat started on port(s): " + getPortsDescription(true)); } catch (ConnectorStartFailedException ex) { stopSilently(); throw ex; } catch (Exception ex) { throw new EmbeddedServletContainerException( "Unable to start embedded Tomcat servlet container", ex); } finally { Context context = findContext(); ContextBindings.unbindClassLoader(context, getNamingToken(context), getClass().getClassLoader()); } } }
启动后,会打印如下日志:
Tomcat started on port(s):8080 (http)
第3步,对EmbeddedServletContainerInitializedEvent 感兴趣的Listener 有如下2个:
org.springframework.boot.context.config.DelegatingApplicationListener, org.springframework.boot.context.embedded.ServerPortInfoApplicationContextInitializer
其中DelegatingApplicationListener 没有做任何事.
ServerPortInfoApplicationContextInitializer#onApplicationEvent.代码如下:
protected void onApplicationEvent(EmbeddedServletContainerInitializedEvent event) { String propertyName = getPropertyName(event.getApplicationContext()); setPortProperty(event.getApplicationContext(), propertyName, event.getEmbeddedServletContainer().getPort()); }
- 生成属性名–>local.server.port
- 向environment 添加了一个名为server.ports,值为配置的端口号的MapPropertySource.
参考链接
更多推荐
所有评论(0)