一、如何定制和修改Servlet容器的相關配置
1、配置文件(ServerProperties);
優先級最高
server.port=8081 server.context‐path=/crud server.tomcat.uri‐encoding=UTF‐8 //通用的Servlet容器設置 server.xxx //Tomcat的設置 server.tomcat.xxx
2、java代碼
2.1 Spring Boot 1.5.10 版本
@Bean //一定要將這個定制器加入到容器中 嵌入式的Servlet容器定制器 public EmbeddedServletContainerCustomizer embeddedServletContainerCustomizer(){ return new EmbeddedServletContainerCustomizer() { //定制嵌入式的Servlet容器相關的規則 @Override public void customize(ConfigurableEmbeddedServletContainer container) { container.setPort(8083); } }; }
2.2 Spring Boot 2.1.0版本
2.2.1、 方式1 (優先級第二)
import org.springframework.boot.web.server.WebServerFactoryCustomizer; import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory; import org.springframework.stereotype.Component; @Component public class CustomizationBean implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> { @Override public void customize(ConfigurableServletWebServerFactory server) { server.setPort(9000); } }
2.2.2、方式2 (優先級最低)
@Bean public ConfigurableServletWebServerFactory webServerFactory() { TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory(); factory.setPort(9000); // factory.setSessionTimeout(10, TimeUnit.MINUTES); 此方法沒找到 factory.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/notfound.html")); return factory; }
二、自定義Servlet容器
1、Spring Boot 支持的 Servlet容器類型
2、切換Tomcat容器為jetty容器
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions><!-- 排除掉默認的Tomcat web容器--> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </exclusion> </exclusions> </dependency> <!-- 引入jetty容器。jetty 適合長連接的場景--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jetty</artifactId><!-- spring-boot-starter-undertow ,不支持JSP--> </dependency>
undertow
包下:spring-boot-autoconfigure-2.1.0.RELEASE.jar
三、嵌入式Servlet容器自動配置原理Spring Boot 2.1.0版本示例: (注:1.5.10 和2.1.0版本有很大不同)
Servelt容器的自動配置類
@Configuration @ConditionalOnWebApplication @EnableConfigurationProperties(ServerProperties.class) public class EmbeddedWebServerFactoryCustomizerAutoConfiguration { /** * Nested configuration if Tomcat is being used. */ @Configuration @ConditionalOnClass({ Tomcat.class, UpgradeProtocol.class }) public static class TomcatWebServerFactoryCustomizerConfiguration { @Bean public TomcatWebServerFactoryCustomizer tomcatWebServerFactoryCustomizer( Environment environment, ServerProperties serverProperties) { return new TomcatWebServerFactoryCustomizer(environment, serverProperties); } } /** * Nested configuration if Jetty is being used. */ @Configuration @ConditionalOnClass({ Server.class, Loader.class, WebAppContext.class }) public static class JettyWebServerFactoryCustomizerConfiguration { @Bean public JettyWebServerFactoryCustomizer jettyWebServerFactoryCustomizer( Environment environment, ServerProperties serverProperties) { return new JettyWebServerFactoryCustomizer(environment, serverProperties); } } /** * Nested configuration if Undertow is being used. */ @Configuration @ConditionalOnClass({ Undertow.class, SslClientAuthMode.class }) public static class UndertowWebServerFactoryCustomizerConfiguration { @Bean public UndertowWebServerFactoryCustomizer undertowWebServerFactoryCustomizer( Environment environment, ServerProperties serverProperties) { return new UndertowWebServerFactoryCustomizer(environment, serverProperties); } } /** * Nested configuration if Netty is being used. */ @Configuration @ConditionalOnClass(HttpServer.class) public static class NettyWebServerFactoryCustomizerConfiguration { @Bean public NettyWebServerFactoryCustomizer nettyWebServerFactoryCustomizer( Environment environment, ServerProperties serverProperties) { return new NettyWebServerFactoryCustomizer(environment, serverProperties); } } }
Tomcat 的Server的定制(Jetty、Netty、Undertow 類似)
public class TomcatWebServerFactoryCustomizer implements WebServerFactoryCustomizer<ConfigurableTomcatWebServerFactory>, Ordered { private final Environment environment; private final ServerProperties serverProperties; public TomcatWebServerFactoryCustomizer(Environment environment, ServerProperties serverProperties) { this.environment = environment; this.serverProperties = serverProperties; } @Override public int getOrder() { return 0; } @Override public void customize(ConfigurableTomcatWebServerFactory factory) { ServerProperties properties = this.serverProperties; ServerProperties.Tomcat tomcatProperties = properties.getTomcat(); PropertyMapper propertyMapper = PropertyMapper.get(); propertyMapper.from(tomcatProperties::getBasedir).whenNonNull() .to(factory::setBaseDirectory); propertyMapper.from(tomcatProperties::getBackgroundProcessorDelay).whenNonNull() .as(Duration::getSeconds).as(Long::intValue) .to(factory::setBackgroundProcessorDelay); customizeRemoteIpValve(factory); propertyMapper.from(tomcatProperties::getMaxThreads).when(this::isPositive) .to((maxThreads) -> customizeMaxThreads(factory, tomcatProperties.getMaxThreads())); propertyMapper.from(tomcatProperties::getMinSpareThreads).when(this::isPositive) .to((minSpareThreads) -> customizeMinThreads(factory, minSpareThreads)); propertyMapper.from(this::determineMaxHttpHeaderSize).whenNonNull() .asInt(DataSize::toBytes).when(this::isPositive) .to((maxHttpHeaderSize) -> customizeMaxHttpHeaderSize(factory, maxHttpHeaderSize)); propertyMapper.from(tomcatProperties::getMaxSwallowSize).whenNonNull() .asInt(DataSize::toBytes) .to((maxSwallowSize) -> customizeMaxSwallowSize(factory, maxSwallowSize)); propertyMapper.from(tomcatProperties::getMaxHttpPostSize).asInt(DataSize::toBytes) .when((maxHttpPostSize) -> maxHttpPostSize != 0) .to((maxHttpPostSize) -> customizeMaxHttpPostSize(factory, maxHttpPostSize)); propertyMapper.from(tomcatProperties::getAccesslog) .when(ServerProperties.Tomcat.Accesslog::isEnabled) .to((enabled) -> customizeAccessLog(factory)); propertyMapper.from(tomcatProperties::getUriEncoding).whenNonNull() .to(factory::setUriEncoding); propertyMapper.from(properties::getConnectionTimeout).whenNonNull() .to((connectionTimeout) -> customizeConnectionTimeout(factory, connectionTimeout)); propertyMapper.from(tomcatProperties::getMaxConnections).when(this::isPositive) .to((maxConnections) -> customizeMaxConnections(factory, maxConnections)); propertyMapper.from(tomcatProperties::getAcceptCount).when(this::isPositive) .to((acceptCount) -> customizeAcceptCount(factory, acceptCount)); customizeStaticResources(factory); customizeErrorReportValve(properties.getError(), factory); } 。。。。。。 }
Spring Boot 2.1.0 版本的web容器啟動流程 (Spring boot 1.5.10版本類似但不同)
1.Spring boot啟動器
@SpringBootApplication public class SpringBootDemo01Application { public static void main(String[] args) { SpringApplication.run(SpringBootDemo01Application.class, args); } }
2.run方法啟動過程源碼分析
public class SpringApplication { //在createApplicationContext()中根據類型classforName()加載 不同類型的 public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context." + "annotation.AnnotationConfigApplicationContext"; public static final String DEFAULT_SERVLET_WEB_CONTEXT_CLASS = "org.springframework.boot." + "web.servlet.context.AnnotationConfigServletWebServerApplicationContext"; public static final String DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = "org.springframework." + "boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext";
//run1調用run2 public static ConfigurableApplicationContext run(Class<?> primarySource,String... args) { return run(new Class<?>[] { primarySource }, args); } //run2調用run3 public static ConfigurableApplicationContext run(Class<?>[] primarySources,String[] args) { return new SpringApplication(primarySources).run(args); }
//run3啟動Web服務器 public ConfigurableApplicationContext run(String... args) { StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>(); configureHeadlessProperty(); SpringApplicationRunListeners listeners = getRunListeners(args); listeners.starting(); try { ApplicationArguments applicationArguments = new DefaultApplicationArguments( args); ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); configureIgnoreBeanInfo(environment); Banner printedBanner = printBanner(environment); context = createApplicationContext(); exceptionReporters = getSpringFactoriesInstances( SpringBootExceptionReporter.class, new Class[] { ConfigurableApplicationContext.class }, context); prepareContext(context, environment, listeners, applicationArguments, printedBanner); refreshContext(context); afterRefresh(context, applicationArguments); stopWatch.stop(); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass) .logStarted(getApplicationLog(), stopWatch); } listeners.started(context); callRunners(context, applicationArguments); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, listeners); throw new IllegalStateException(ex); } try { listeners.running(context); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, null); throw new IllegalStateException(ex); } return context; } protected ConfigurableApplicationContext createApplicationContext() { Class<?> contextClass = this.applicationContextClass; if (contextClass == null) { try { switch (this.webApplicationType) { case SERVLET: contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS); break; case REACTIVE: contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS); break; default: contextClass = Class.forName(DEFAULT_CONTEXT_CLASS); } } catch (ClassNotFoundException ex) { throw new IllegalStateException( "Unable create a default ApplicationContext, " + "please specify an ApplicationContextClass", ex); } } return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass); } private void refreshContext(ConfigurableApplicationContext context) { refresh(context); if (this.registerShutdownHook) { try { context.registerShutdownHook(); } catch (AccessControlException ex) { // Not allowed in some environments. } } } protected void refresh(ApplicationContext applicationContext) { Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext); ((AbstractApplicationContext) applicationContext).refresh(); } }
public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext { @Override public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. prepareRefresh(); // Tell the subclass to refresh the internal bean factory. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. prepareBeanFactory(beanFactory); try { // Allows post-processing of the bean factory in context subclasses. postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context. invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation. registerBeanPostProcessors(beanFactory); // Initialize message source for this context. initMessageSource(); // Initialize event multicaster for this context. initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. onRefresh(); // Check for listener beans and register them. registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. finishRefresh(); } catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); } // Destroy already created singletons to avoid dangling resources. destroyBeans(); // Reset 'active' flag. cancelRefresh(ex); // Propagate exception to caller. throw ex; } finally { // Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore... resetCommonCaches(); } } } protected void onRefresh() throws BeansException { // For subclasses: do nothing by default. } }
public class ServletWebServerApplicationContext extends GenericWebApplicationContext implements ConfigurableWebServerApplicationContext { @Override protected void onRefresh() { super.onRefresh(); try { createWebServer(); } catch (Throwable ex) { throw new ApplicationContextException("Unable to start web server", ex); } } private void createWebServer() { WebServer webServer = this.webServer; ServletContext servletContext = getServletContext(); if (webServer == null && servletContext == null) { ServletWebServerFactory factory = getWebServerFactory(); this.webServer = factory.getWebServer(getSelfInitializer()); } else if (servletContext != null) { try { getSelfInitializer().onStartup(servletContext); } catch (ServletException ex) { throw new ApplicationContextException("Cannot initialize servlet context", ex); } } initPropertySources(); } }
spring-boot-2.1.0.RELEASE.jar