Considering two and a half years have passed since I originally asked this question, I think it is time that I shared our solution for the benefit of anyone that might read this in the future.
We ended up writing a custom component that implements WebServerFactoryCustomizer. Spring Boot will scan for all beans before starting its embedded Tomcat server. If it detects a bean that implements this interface, it will invoke the customize()
method and pass the server factory as an argument to the function.
From there, it was straightforward:
package your.pack.age.name;
import org.apache.catalina.core.StandardThreadExecutor;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.embedded.tomcat.TomcatWebServer;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.stereotype.Component;
@Component
public class TomcatServerConfig implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {
private final StandardThreadExecutor customExecutor;
public TomcatServerConfig() {
this.customExecutor = YourExecutorImplementation();
}
@Override
public void customize(TomcatServletWebServerFactory factory) {
/*This web server is the Tomcat server embedded in Spring Boot*/
TomcatWebServer webServer = (TomcatWebServer)factory.getWebServer()
webServer.getTomcat().getService().addExecutor(this.customExecutor);
}
}
(The actual code we used was simplified here, for the sake of a clear answer)
It is also worth noting that similar code needs to be written for the Tomcat Connectors, using TomcatConnectorCustomizer:
package your.pack.age.name;
import org.apache.catalina.connector.Connector;
import org.springframework.boot.web.embedded.tomcat.TomcatConnectorCustomizer;
import org.springframework.stereotype.Component;
@Component
public class TomcatConnectorConfig implements TomcatConnectorCustomizer {
private final StandardThreadExecutor customExecutor;
public TomcatConnectorConfig() {
this.customExecutor = YourExecutorImplementation();
}
@Override
public void customize(Connector connector) {
connector.getProtocolHandler().setExecutor(this.customExecutor);
}
}
For convenience, I am adding a skeletal custom implementation of a thread executor:
import org.apache.catalina.LifecycleException;
import org.apache.catalina.core.StandardThreadExecutor;
public class HTTPThreadExecutor extends StandardThreadExecutor {
public HTTPThreadExecutor() {
super();
/* Custom constructor code here */
}
@Override
protected void startInternal() throws LifecycleException {
super.startInternal();
/* Any work you need done upon startup */
}
}