Avoid duplicate customization of management web server factory

Previously, customization was performed in two places:

1. By customizers defined in the reactive and servlet web servlet
   factory auto-configuration
     - ServletWebServerFactoryAutoConfiguration
     - ReactiveWebServerFactoryAutoConfiguration
 2. By a ManagementWebServerFactoryCustomizer that delegates to
    customizers of certain types found in the application context
    hierarchy.

This led to some double customization as the customizers registered
by the auto-configuration classes were also found and called by the
ManagementWebServerFactoryCustomizer.

Additionally, the ManagementWebServerFactoryCustomizer would find
customizers from the parent context registered by
EmbeddedWebServerFactoryCustomizerAutoConfiguration.

This commit reworks the customization of the management web server
factory to remove the double customization.
ManagementWebServerFactoryCustomizer no longer delegates to
customizers that it finds in the context hierarchy. This
prevents the customizers defined in the reactive and servlet web
server factory auto-configuration classes from being called twice.
Additionally, EmbeddedWebServerFactoryCustomizerAutoConfiguration is
now registered in the child context so that its customizers continue
to be called when preparing the management context web server
factory.

Closes gh-44151
This commit is contained in:
Andy Wilkinson
2025-02-07 12:23:40 +00:00
parent 40059a037a
commit 8ce7da9bb0
5 changed files with 29 additions and 43 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2023 the original author or authors.
* Copyright 2012-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,15 +26,7 @@ import org.springframework.boot.actuate.autoconfigure.web.server.ManagementServe
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementWebServerFactoryCustomizer;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
import org.springframework.boot.autoconfigure.web.embedded.JettyVirtualThreadsWebServerFactoryCustomizer;
import org.springframework.boot.autoconfigure.web.embedded.JettyWebServerFactoryCustomizer;
import org.springframework.boot.autoconfigure.web.embedded.NettyWebServerFactoryCustomizer;
import org.springframework.boot.autoconfigure.web.embedded.TomcatVirtualThreadsWebServerFactoryCustomizer;
import org.springframework.boot.autoconfigure.web.embedded.TomcatWebServerFactoryCustomizer;
import org.springframework.boot.autoconfigure.web.embedded.UndertowWebServerFactoryCustomizer;
import org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryCustomizer;
import org.springframework.boot.autoconfigure.web.reactive.TomcatReactiveWebServerFactoryCustomizer;
import org.springframework.boot.web.reactive.server.ConfigurableReactiveWebServerFactory;
import org.springframework.boot.web.server.ConfigurableWebServerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.http.server.reactive.ContextPathCompositeHandler;
@@ -58,9 +50,9 @@ import org.springframework.web.server.adapter.WebHttpHandlerBuilder;
public class ReactiveManagementChildContextConfiguration {
@Bean
public ReactiveManagementWebServerFactoryCustomizer reactiveManagementWebServerFactoryCustomizer(
public ManagementWebServerFactoryCustomizer<ConfigurableWebServerFactory> reactiveManagementWebServerFactoryCustomizer(
ListableBeanFactory beanFactory) {
return new ReactiveManagementWebServerFactoryCustomizer(beanFactory);
return new ManagementWebServerFactoryCustomizer<>(beanFactory);
}
@Bean
@@ -73,17 +65,4 @@ public class ReactiveManagementChildContextConfiguration {
return httpHandler;
}
static class ReactiveManagementWebServerFactoryCustomizer
extends ManagementWebServerFactoryCustomizer<ConfigurableReactiveWebServerFactory> {
ReactiveManagementWebServerFactoryCustomizer(ListableBeanFactory beanFactory) {
super(beanFactory, ReactiveWebServerFactoryCustomizer.class, TomcatWebServerFactoryCustomizer.class,
TomcatReactiveWebServerFactoryCustomizer.class,
TomcatVirtualThreadsWebServerFactoryCustomizer.class, JettyWebServerFactoryCustomizer.class,
JettyVirtualThreadsWebServerFactoryCustomizer.class, UndertowWebServerFactoryCustomizer.class,
NettyWebServerFactoryCustomizer.class);
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2022 the original author or authors.
* Copyright 2012-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -25,6 +25,7 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
import org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration;
import org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryAutoConfiguration;
import org.springframework.boot.web.reactive.server.ReactiveWebServerFactory;
import org.springframework.context.annotation.Bean;
@@ -44,7 +45,8 @@ public class ReactiveManagementContextAutoConfiguration {
@Bean
public static ManagementContextFactory reactiveWebChildContextFactory() {
return new ManagementContextFactory(WebApplicationType.REACTIVE, ReactiveWebServerFactory.class,
ReactiveWebServerFactoryAutoConfiguration.class);
ReactiveWebServerFactoryAutoConfiguration.class,
EmbeddedWebServerFactoryCustomizerAutoConfiguration.class);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2024 the original author or authors.
* Copyright 2012-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -39,7 +39,7 @@ import org.springframework.core.Ordered;
* @author Andy Wilkinson
* @since 2.0.0
*/
public abstract class ManagementWebServerFactoryCustomizer<T extends ConfigurableWebServerFactory>
public class ManagementWebServerFactoryCustomizer<T extends ConfigurableWebServerFactory>
implements WebServerFactoryCustomizer<T>, Ordered {
private final ListableBeanFactory beanFactory;
@@ -48,12 +48,24 @@ public abstract class ManagementWebServerFactoryCustomizer<T extends Configurabl
@SafeVarargs
@SuppressWarnings("varargs")
@Deprecated(since = "3.5.0", forRemoval = true)
protected ManagementWebServerFactoryCustomizer(ListableBeanFactory beanFactory,
Class<? extends WebServerFactoryCustomizer<?>>... customizerClasses) {
this.beanFactory = beanFactory;
this.customizerClasses = customizerClasses;
}
/**
* Creates a new customizer that will retrieve beans using the given
* {@code beanFactory}.
* @param beanFactory the bean factory to use
* @since 3.5.0
*/
public ManagementWebServerFactoryCustomizer(ListableBeanFactory beanFactory) {
this.beanFactory = beanFactory;
this.customizerClasses = null;
}
@Override
public int getOrder() {
return 0;
@@ -65,7 +77,9 @@ public abstract class ManagementWebServerFactoryCustomizer<T extends Configurabl
.beanOfTypeIncludingAncestors(this.beanFactory, ManagementServerProperties.class);
// Customize as per the parent context first (so e.g. the access logs go to
// the same place)
customizeSameAsParentContext(factory);
if (this.customizerClasses != null) {
customizeSameAsParentContext(factory);
}
// Then reset the error pages
factory.setErrorPages(Collections.emptySet());
// and add the management-specific bits

View File

@@ -39,14 +39,6 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplicat
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
import org.springframework.boot.autoconfigure.condition.SearchStrategy;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.autoconfigure.web.embedded.JettyVirtualThreadsWebServerFactoryCustomizer;
import org.springframework.boot.autoconfigure.web.embedded.JettyWebServerFactoryCustomizer;
import org.springframework.boot.autoconfigure.web.embedded.TomcatVirtualThreadsWebServerFactoryCustomizer;
import org.springframework.boot.autoconfigure.web.embedded.TomcatWebServerFactoryCustomizer;
import org.springframework.boot.autoconfigure.web.embedded.UndertowWebServerFactoryCustomizer;
import org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryCustomizer;
import org.springframework.boot.autoconfigure.web.servlet.TomcatServletWebServerFactoryCustomizer;
import org.springframework.boot.autoconfigure.web.servlet.UndertowServletWebServerFactoryCustomizer;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.embedded.jetty.JettyServletWebServerFactory;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
@@ -125,10 +117,7 @@ class ServletManagementChildContextConfiguration {
extends ManagementWebServerFactoryCustomizer<ConfigurableServletWebServerFactory> {
ServletManagementWebServerFactoryCustomizer(ListableBeanFactory beanFactory) {
super(beanFactory, ServletWebServerFactoryCustomizer.class, TomcatServletWebServerFactoryCustomizer.class,
TomcatWebServerFactoryCustomizer.class, TomcatVirtualThreadsWebServerFactoryCustomizer.class,
JettyWebServerFactoryCustomizer.class, JettyVirtualThreadsWebServerFactoryCustomizer.class,
UndertowServletWebServerFactoryCustomizer.class, UndertowWebServerFactoryCustomizer.class);
super(beanFactory);
}
@Override

View File

@@ -27,6 +27,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProp
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
import org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration;
import org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration;
import org.springframework.boot.web.servlet.filter.ApplicationContextHeaderFilter;
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
@@ -49,7 +50,8 @@ public class ServletManagementContextAutoConfiguration {
@Bean
public static ManagementContextFactory servletWebChildContextFactory() {
return new ManagementContextFactory(WebApplicationType.SERVLET, ServletWebServerFactory.class,
ServletWebServerFactoryAutoConfiguration.class);
ServletWebServerFactoryAutoConfiguration.class,
EmbeddedWebServerFactoryCustomizerAutoConfiguration.class);
}
@Bean