SpringBoot如何实现Tomcat自动配置

2025-05-29 0 93

准备工作

我们知道SpringBoot的自动装配的秘密在 org.springframework.boot.autoconfigure 包下的 spring.factories 文件中,而嵌入Tomcat的原理就在这个文件中加载的一个配置类: org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67
@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 {

@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);

}

/**

* Registers a {@link WebServerFactoryCustomizerBeanPostProcessor}. Registered via

* {@link ImportBeanDefinitionRegistrar} for early registration.

*/

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;

}

registerSyntheticBeanIfMissing(registry,

"webServerFactoryCustomizerBeanPostProcessor",

WebServerFactoryCustomizerBeanPostProcessor.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);

}

}

}

}

首先看一下上方的几个注解

  • @AutoConfigureOrder 这个注解是决定配置类的加载顺序的,当注解里的值越小越先加载,而 Ordered.HIGHEST_PRECEDENCE 的值是 Integer.MIN_VALUE 也就是说这个类肯定是最先加载的那一批
  • @ConditionalOnXXX 在之前的文章中已经无数次提到了,就不再阐述了
  • @EnableConfigurationProperties 开启 ServerProperties 类的属性值配置。而这个类里面包含的就是Web服务的配置
?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35
@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)

public class ServerProperties {

private Integer port;

private InetAddress address;

@NestedConfigurationProperty

private final ErrorProperties error = new ErrorProperties();

private Boolean useForwardHeaders;

private String serverHeader;

private int maxHttpHeaderSize = 0; // bytes

private Duration connectionTimeout;

@NestedConfigurationProperty

private Ssl ssl;

@NestedConfigurationProperty

private final Compression compression = new Compression();

@NestedConfigurationProperty

private final Http2 http2 = new Http2();

private final Servlet servlet = new Servlet();

private final Tomcat tomcat = new Tomcat();

private final Jetty jetty = new Jetty();

private final Undertow undertow = new Undertow();

}

这个类的代码太多了,这里就不一一贴出来了,我们平常在 application.properties 中配置的server.xxx就是这个类中属性

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29
@Import

BeanPostProcessorsRegistrar

public static class BeanPostProcessorsRegistrar

implements ImportBeanDefinitionRegistrar, BeanFactoryAware {

@Override

public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,

BeanDefinitionRegistry registry) {

if (this.beanFactory == null) {

return;

}

registerSyntheticBeanIfMissing(registry,

"webServerFactoryCustomizerBeanPostProcessor",

WebServerFactoryCustomizerBeanPostProcessor.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: WebServerFactoryCustomizerBeanPostProcessorErrorPageRegistrarBeanPostProcessor 关于这两个bean的作用稍后再详细介绍

?

1

2

3

4

5

6

7

8

9

10

11
@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();

}

}

这个类会在存在Tomcat相关jar包时添加一个 TomcatServletWebServerFactory bean

其他两个相信大家都知道怎么回事了

除了这些这个类还注入了两个类 ServletWebServerFactoryCustomizerTomcatServletWebServerFactoryCustomizer
现在前期准备工作已经做好了,看一下这个Tomcat是如何启动的吧

启动

启动入口在 ServletWebServerApplicationContext 中的 onRefresh 方法

?

1

2

3

4

5

6

7

8

9
protected void onRefresh() {

super.onRefresh();

try {

createWebServer();

}

catch (Throwable ex) {

throw new ApplicationContextException("Unable to start web server", ex);

}

}

Tomcat的启动就在 createWebServer 方法里面了

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19
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();

}

首先看一下 getWebServerFactory

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17
protected ServletWebServerFactory getWebServerFactory() {

// 这里获取的beanname就是上方注册的tomcatServletWebServerFactory了

String[] beanNames = getBeanFactory()

.getBeanNamesForType(ServletWebServerFactory.class);

if (beanNames.length == 0) {

throw new ApplicationContextException(

"Unable to start ServletWebServerApplicationContext due to missing "

+ "ServletWebServerFactory bean.");

}

if (beanNames.length > 1) {

throw new ApplicationContextException(

"Unable to start ServletWebServerApplicationContext due to multiple "

+ "ServletWebServerFactory beans : "

+ StringUtils.arrayToCommaDelimitedString(beanNames));

}

return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);

}

准备环境里注册的bean现在出来一个了。注意,上方还注册了一个后置处理器 EmbeddedServletContainerCustomizerBeanPostProcessor ,获取bean tomcatServletWebServerFactory 的时候就会执行后置处理器的 postProcessBeforeInitialization 方法

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30
public Object postProcessBeforeInitialization(Object bean, String beanName)

throws BeansException {

if (bean instanceof WebServerFactory) {

postProcessBeforeInitialization((WebServerFactory) bean);

}

return bean;

}

private void postProcessBeforeInitialization(WebServerFactory webServerFactory) {

LambdaSafe

.callbacks(WebServerFactoryCustomizer.class, getCustomizers(),

webServerFactory)

.withLogger(WebServerFactoryCustomizerBeanPostProcessor.class)

.invoke((customizer) -> customizer.customize(webServerFactory));

}

private Collection<WebServerFactoryCustomizer<?>> getCustomizers() {

if (this.customizers == null) {

// Look up does not include the parent context

this.customizers = new ArrayList<>(getWebServerFactoryCustomizerBeans());

this.customizers.sort(AnnotationAwareOrderComparator.INSTANCE);

this.customizers = Collections.unmodifiableList(this.customizers);

}

return this.customizers;

}

@SuppressWarnings({ "unchecked", "rawtypes" })

private Collection<WebServerFactoryCustomizer<?>> getWebServerFactoryCustomizerBeans() {

return (Collection) this.beanFactory

.getBeansOfType(WebServerFactoryCustomizer.class, false, false).values();

}

这个处理器的作用是获得所有定制器,然后执行定制器的方法

接着往下看

这个时候就可以启动Tomcat

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65
public WebServer getWebServer(ServletContextInitializer... initializers) {

Tomcat tomcat = new 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);

return getTomcatWebServer(tomcat);

}

protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {

return new TomcatWebServer(tomcat, getPort() >= 0);

}

public TomcatWebServer(Tomcat tomcat, boolean autoStart) {

Assert.notNull(tomcat, "Tomcat Server must not be null");

this.tomcat = tomcat;

this.autoStart = autoStart;

initialize();

}

private void initialize() throws WebServerException {

TomcatWebServer.logger.info("Tomcat initialized with port(s): " + getPortsDescription(false));

synchronized (this.monitor) {

try {

addInstanceIdToEngineName();

Context context = findContext();

context.addLifecycleListener((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

this.tomcat.start();

// We can re-throw failure exception directly in the main thread

rethrowDeferredStartupExceptions();

try {

ContextBindings.bindClassLoader(context, context.getNamingToken(),

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) {

throw new WebServerException("Unable to start embedded Tomcat", ex);

}

}

}

以上就是SpringBoot如何实现Tomcat自动配置的详细内容,更多关于SpringBoot实现Tomcat自动配置的资料请关注快网idc其它相关文章!

原文链接:https://www.cicoding.cn/springboot/tomcat-auto-configuration-in-springboot/

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。

快网idc优惠网 建站教程 SpringBoot如何实现Tomcat自动配置 https://www.kuaiidc.com/108596.html

相关文章

发表评论
暂无评论