承接前文 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 {
- }
主要是引入了 ServerProperties 配置类, 而其是读取 spring 上下文环境中的以 server 为开头的属性, 简单的看下
- @ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
- public class ServerProperties {
- @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);
- }
- }
- }
样例中的 port/servlet.context-path 便是保存在 ServerProperties 对象中的, 具体其内部的属性本文就不展开了, 读者可自行去阅读源码. 由上述的简单代码得知该自动配置类主要根据 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 接口去实现
来源: https://www.cnblogs.com/question-sky/p/9580060.html