We have a project in spring-integration
that will work with either webflex
or servlet
implementations and this seems to work pretty well using the spring.main.web-application-type
property set as a system property.
I'm now looking for how to choose the embedded web server at runtime.
The spring-boot
documentation says that this is accomplished via dependency inclusion or exclusion with the pom.xml
.
https://docs.spring.io/spring-boot/docs/2.1.9.RELEASE/reference/html/howto-embedded-web-servers.html
What I'm looking for is to be able to start any of jetty
, netty
, undertow
or tomcat
from the same project/executable jar.
Is this feasible by some specific startup sequence in SpringApplication?
Thanks for any pointers/suggestions.
CodePudding user response:
You just can have all of them as a test
scope dependency and use Spring Boot's FilteredClassLoader
to exclude whatever you don't need in the current unit test: https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#features.developing-auto-configuration.testing.overriding-classpath.
You might need to learn details of all those auto-configurations for web servers to determine what exactly class to exclude.
Might be also possible to achieve your goal with just plain @SpringBootApplication.exclude()
(or similar configuration property) for the specific auto-configurations not needed in the current test.
Nothing to do with Spring Integration though.
CodePudding user response:
I think that I've found a relatively simple and clean way to do this.
At Application context creation, spring-boot
loads the WebServerApplicationContext
in one of two flavors, depending upon the value in property spring.main.web-application-type
: either reactive
or servlet
(respectively ReactiveWebServerApplicationContext
and ServletWebServerApplicationContext
).
These two classes both have a method createWebServer()
which will look for a bean of type (respectively) ReactiveWebServerFactory
or ServletWebServerFactory
loaded into the context, in which case this bean will be the WebServerFactory
used by the WebApplicationContext
.
Hence need to declare the target WebServerFactory
as a bean.
There are a number of different ways of doing this, including using profiles.
I've chosen to use a factory bean that instantiates the WebServerFactory
as a function of a system property, here my.main.web-server
(as a parallel to spring.main.web-application-type
).
Using a BeanFactory
to create the bean that holds the WebServerFactory
:
package net.demo;
...
public class MyWebServerFactoryBeanFactory implements EnvironmentAware {
private Environment environment;
public ReactiveWebServerFactory createReactiveWebServerFactory() {
switch(getWebServer()) {
case "jetty":
return new JettyReactiveWebServerFactory();
case "netty":
return new NettyReactiveWebServerFactory();
case "undertow":
return new UndertowReactiveWebServerFactory();
case "tomcat":
case default:
return new TomcatReactiveWebServerFactory();
}
}
public ServletWebServerFactory createServletWebServerFactory() {
switch(getWebServer()) {
case "jetty":
return new JettyServletWebServerFactory();
case "undertow":
return new UndertowServletWebServerFactory();
case "tomcat":
case default:
return new TomcatServletWebServerFactory();
}
}
private String getWebServer() {
return environment.getProperty("my.main.web-server");
}
}
Using XML context definitions, I define the BeanFactory and import a source which will be different for reactive
and servlet
:
...
<import resource="classpath:my-${spring.main.web-application-type:servlet}.xml" />
<bean id="myWebServerFactoryBeanFactory"
/>
...
Finally, create the WebServerFactory
within the specific source file. Within my-reactive.xml
:
<bean id="myWebServerFactory" factory-bean="net.demo.MyWebServerFactoryBeanFactory"
factory-method="createReactiveWebServerFactory" />
and within my-servlet.xml
:
<bean id="myWebServerFactory" factory-bean="net.demo.MyWebServerFactoryBeanFactory"
factory-method="createServletWebServerFactory" />
I suppose that you don't necessarily need to have the specialized import because you can create both the ReactiveWebServerFactory
and the ServletWebServerFactory
within the context but only the matching bean will be used.