承接前文springboot情操陶冶-@SpringBootApplication注解解析,在前文講解的基礎上依次看下web方面的相關配置
環境包依賴
在pom.xml
文件中引入web依賴,炒雞簡單,如下
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
上述的三行依賴代碼便完成了對web環境的配置,此時可以直接運行main()方法
package com.example.demospringbootweb;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DemoSpringbootWebApplication {
public static void main(String[] args) {
SpringApplication.run(DemoSpringbootWebApplication.class, args);
}
}
默認服務是掛載在Tomcat容器中,端口為8080。所以可以通過該鏈接直接訪問http://127.0.0.1:8080便可得到以下頁面(未配置index頁面的效果)
應用端口和上下文配置
本文將在上文的基礎山講解端口和上下文路徑的具體配置以及解析。現附上簡單的步驟操作
創建application-servlet.properties文件,專門用於配置應用服務
#server application config
server.port=9001
server.servlet.context-path=/demoWeb
在application.properties文件中指定激活的profile,用於使上述文件生效
spring.profiles.active=servlet
為了使界面變得稍微友好,引入index.html文件,放置於static目錄下,如下
繼續運行對應的main()函數,便可訪問http://127.0.0.1:9001/demoWeb,得到以下結果
源碼剖析
關於Tomcat等容器的配置,springboot采用了EmbeddedWebServerFactoryCustomizerAutoConfiguration和ServletWebServerFactoryAutoConfiguration兩個類便完成了。筆者針對這兩個類進行簡單的分析
EmbeddedWebServerFactoryCustomizerAutoConfiguration
直接查看其內部源碼,如下
@Configuration
@EnableConfigurationProperties(ServerProperties.class)
public class EmbeddedWebServerFactoryCustomizerAutoConfiguration {
@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);
}
}
}
主要是引入了ServerProperties配置類,樣例中的server.port/server.servlet.context-path便是保存在ServerProperties對象中的,其是讀取spring上下文環境中的以server為開頭的屬性,具體的屬性用戶可自行查看源碼。
由上述的簡單代碼得知該自動配置類主要根據classpath環境創建不同的應用容器,默認springboot集成的都是tomcat。我們此處只關注下TomcatWebServerFactoryCustomizer類,下文中會有所提及
ServletWebServerFactoryAutoConfiguration
具體的ServletWebServer容器配置是通過ServletWebServerFactoryAutoConfiguration來創建的,由於代碼過長筆者分為幾個部分來講解
頭上注解先瞧一發
@Configuration
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@ConditionalOnClass(ServletRequest.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(ServerProperties.class)
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration {
}
要想本自動配置生效則必須classpath環境中存在ServletRequest.class
等servlet環境依賴類,這一般引入開頭的starter-web
版塊便基本滿足了
創建webServerFactory個性化配置類
@Bean
public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(
ServerProperties serverProperties) {
return new ServletWebServerFactoryCustomizer(serverProperties);
}
@Bean
@ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat")
public TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer(
ServerProperties serverProperties) {
return new TomcatServletWebServerFactoryCustomizer(serverProperties);
}
這兩個bean類和上文中的TomcatWebServerFactoryCustomizer很相似,但仔細閱讀源碼之后便發現其實這只是tomcat配置的分工處理,小結如下
- TomcatWebServerFactoryCustomizer 配置tomcat的主要信息,包含remoteIpValue、connector(最大/最小可接收線程、最大可接收頭部大小等等)、uriEncoding、connectionTimeout、maxConnection等屬性
- TomcatServletWebServerFactoryCustomizer 配置tomcat的額外信息,redirectContextRoot(是否在請求根上下文時轉發,true則轉發路徑為/demoWeb/)和useRelativeRedirects(是否使用相對路徑)等路徑跳轉問題處理
- ServletWebServerFactoryCustomizer 主要配置tomcat的servlet的信息,包含端口、上下文路徑、應用名、Session配置、Servlet攜帶的初始變量等等
通過上述的三個bean類便基本完成了基本的tomcat配置,其都是WebServerFactoryCustomizer接口的實現類,那么是被誰來統一調用以完成上述的配置呢?
1.首先引入了WebServerFactory工廠類,此點可直接看由上述@Import引入的EmbeddedTomcat分析可得
@Configuration
@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
public static class EmbeddedTomcat {
@Bean
public TomcatServletWebServerFactory tomcatServletWebServerFactory() {
return new TomcatServletWebServerFactory();
}
}
創建了TomcatServletWebServerFactory的tomcat容器,其余的web容器讀者可自行分析
2.最后通過beanPostProcessor接口來完成相應的容器初始化
由@Import引入的BeanPostProcessorsRegistrar類,注冊了webServerFactoryCustomizerBeanPostProcessor類來完成相應的tomcat個性化配置
// 初始化上述的WebServerFactory對象前操作
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof WebServerFactory) {
this.postProcessBeforeInitialization((WebServerFactory)bean);
}
return bean;
}
// 調用所有實現了WebServerFactoryCustomizer接口的對象
private void postProcessBeforeInitialization(WebServerFactory webServerFactory) {
((Callbacks)LambdaSafe.callbacks(WebServerFactoryCustomizer.class, this.getCustomizers(), webServerFactory, new Object[0]).withLogger(WebServerFactoryCustomizerBeanPostProcessor.class)).invoke((customizer) -> {
customizer.customize(webServerFactory);
});
}
// 查找當前bean工廠中所有類型為WebServerFactoryCustomizer接口對象集合
private Collection<WebServerFactoryCustomizer<?>> getCustomizers() {
if (this.customizers == null) {
this.customizers = new ArrayList(this.getWebServerFactoryCustomizerBeans());
this.customizers.sort(AnnotationAwareOrderComparator.INSTANCE);
this.customizers = Collections.unmodifiableList(this.customizers);
}
return this.customizers;
}
private Collection<WebServerFactoryCustomizer<?>> getWebServerFactoryCustomizerBeans() {
return this.beanFactory.getBeansOfType(WebServerFactoryCustomizer.class, false, false).values();
}
具體的解析見上述的代碼注釋,其實也很簡單並一目了然,所以如果用戶想在tomcat上再作個性化的需求,可自行實現WebServerFactoryCustomizer接口並注冊至bean工廠即可
@Configuration
public MyWebServerFactoryCustomizer implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>{
@Override
public void customize(ConfigurableServletWebServerFactory factory) {
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
// do personal binding
}
}
小結
本文只講述tomcat的相關配置,並舉例說明了其port/contextPath的應用配置,更多的配置讀者可采用springboot實現的帶server前綴的配置以及自行實現WebServerFactoryCustomizer接口去實現