Why does each context require a separate port when creating a context hierarchy using Fluent API?
Good day!
When I create a context hierarchy using ServletRegistrationBean, both DispatcherServlets work on the same port:
@SpringBootApplication(exclude = {PropertyPlaceholderAutoConfiguration.class,
DispatcherServletAutoConfiguration.class,
WebMvcAutoConfiguration.class,
ErrorMvcAutoConfiguration.class})
@ConfigurationPropertiesScan
public class SocialAppApplication {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(SocialAppApplication.class);
app.run(args);
}
@Bean
public ServletRegistrationBean<DispatcherServlet> secured()
{
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.register(SecuredConfig.class);
DispatcherServlet servlet = new DispatcherServlet();
servlet.setApplicationContext(context);
ServletRegistrationBean<DispatcherServlet> bean = new ServletRegistrationBean<>(servlet);
bean.setName("secured");
bean.addUrlMappings("/secured/*");
bean.setLoadOnStartup(1);
return bean;
}
@Bean
public ServletRegistrationBean<DispatcherServlet> unsecured()
{
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.register(UnsecuredConfig.class);
DispatcherServlet servlet = new DispatcherServlet();
servlet.setApplicationContext(context);
ServletRegistrationBean<DispatcherServlet> bean = new ServletRegistrationBean<>(servlet);
bean.setName("unsecured");
bean.addUrlMappings("/unsecured/*");
return bean;
}
}
But if I create a context hierarchy using the Fluent API:
new SpringApplicationBuilder(SocialAppApplication.class).web(WebApplicationType.NONE)
.child(SecuredConfig.class).web(WebApplicationType.SERVLET)
.child(UnsecuredConfig.class).web(WebApplicationType.SERVLET)
.run(args);
then each context requires its own port. Why?
Questions to consider:
- Why does SpringApplicationBuilder Fluent API create separate ports for each child context?
- How to configure SpringApplicationBuilder for multiple servlets to work on one port?
- What’s the fundamental difference between the ServletRegistrationBean and SpringApplicationBuilder approaches regarding ports?
Each context in the SpringApplicationBuilder hierarchy requires a separate port because this approach creates separate, independent Spring Boot applications, each with its own embedded web server, while ServletRegistrationBean creates multiple servlets within a single application and single server.
Table of Contents
- Main Difference Between Approaches
- Why SpringApplicationBuilder Requires Separate Ports
- Configuring SpringApplicationBuilder to Work on One Port
- Fundamental Architectural Differences
- Recommendations for Choosing an Approach
Main Difference Between Approaches
The key difference is that SpringApplicationBuilder creates separate Spring Boot applications, while ServletRegistrationBean creates multiple servlets within a single application.
When you use ServletRegistrationBean, you’re working within a single ApplicationContext and a single embedded server (Tomcat, Jetty, etc.). All servlets are registered in one servlet container and listen on the same port, but with different URL patterns.
When using SpringApplicationBuilder, you’re creating full, independent Spring Boot applications, each with its own:
ApplicationContext- Embedded web server
- Port configuration
- Set of beans
- Lifecycle
As noted in the Spring Boot documentation, SpringApplicationBuilder is designed to create hierarchies of ApplicationContext with parent-child relationships, making each child context a full-fledged application.
Why SpringApplicationBuilder Requires Separate Ports
SpringApplicationBuilder creates separate ports for several fundamental reasons:
1. Independent Applications
Each child context in the hierarchy is a full Spring Boot application with its own embedded server. As explained in this article on Spring context hierarchy, each such application should run its own server on a dedicated port.
2. Default Port Conflicts
By default, all Spring Boot applications try to start on the same port (8080). If multiple contexts tried to use the same port, a conflict would occur. As indicated in research on Spring contexts, different contexts require different ports to prevent conflicts.
3. Configuration Isolation
Each context has its isolated configuration. As mentioned in this article on Spring Boot and contexts, placing beans in separate contexts prevents them from being overridden and ensures isolation.
4. Architectural Model
SpringApplicationBuilder follows a multiple applications model rather than multiple servlets. As described in this article on multiple hierarchical contexts, this approach allows creating full microservices within a single application.
Configuring SpringApplicationBuilder to Work on One Port
Although SpringApplicationBuilder creates separate ports by default, there are approaches to work on a single port:
1. Explicit Port Configuration via Properties
You can configure different ports for each context through system properties:
public static void main(String[] args) {
new SpringApplicationBuilder(SocialAppApplication.class)
.web(WebApplicationType.NONE)
.child(SecuredConfig.class)
.web(WebApplicationType.SERVLET)
.properties("server.port=8081")
.child(UnsecuredConfig.class)
.web(WebApplicationType.SERVLET)
.properties("server.port=8082")
.run(args);
}
2. Using a Single Context with Multiple Servlets
If you really need to work on one port, it’s better to use the ServletRegistrationBean approach as in your example:
@Bean
public ServletRegistrationBean<DispatcherServlet> secured() {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.register(SecuredConfig.class);
DispatcherServlet servlet = new DispatcherServlet(context);
ServletRegistrationBean<DispatcherServlet> bean = new ServletRegistrationBean<>(servlet);
bean.setName("secured");
bean.addUrlMappings("/secured/*");
return bean;
}
@Bean
public ServletRegistrationBean<DispatcherServlet> unsecured() {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.register(UnsecuredConfig.class);
DispatcherServlet servlet = new DispatcherServlet(context);
ServletRegistrationBean<DispatcherServlet> bean = new ServletRegistrationBean<>(servlet);
bean.setName("unsecured");
bean.addUrlMappings("/unsecured/*");
return bean;
}
3. Advanced Approach with Conditional Configuration
You can use conditional configuration to determine which context to use:
public static void main(String[] args) {
String contextType = System.getProperty("context.type", "secured");
new SpringApplicationBuilder(SocialAppApplication.class)
.web(WebApplicationType.NONE)
.child(contextType.equals("secured") ? SecuredConfig.class : UnsecuredConfig.class)
.web(WebApplicationType.SERVLET)
.properties("server.port=8080")
.run(args);
}
Fundamental Architectural Differences
Comparison Table of Approaches
| Characteristic | SpringApplicationBuilder | ServletRegistrationBean |
|---|---|---|
| Number of Contexts | Multiple independent | One context with multiple servlets |
| Number of Servers | Multiple embedded servers | One embedded server |
| Ports | Separate ports for each context | One port for all servlets |
| Bean Isolation | Complete isolation between contexts | Beans available to all servlets |
| Lifecycle | Independent lifecycles | Shared lifecycle |
| Configuration Complexity | High (requires port configuration) | Low |
| Performance | Multiple server instances | One server instance |
Architectural Implications
SpringApplicationBuilder approach:
- Pros: Complete isolation, potential for scaling, microservice architecture
- Cons: High resource consumption, complex configuration, multiple ports
ServletRegistrationBean approach:
- Pros: Simple configuration, single port, fewer resources
- Cons: Harder to ensure bean isolation, shared dependencies
As noted in this article on Fluent API, SpringApplicationBuilder is designed for creating context hierarchies, not for replacing the traditional multiple servlets approach.
Recommendations for Choosing an Approach
Use SpringApplicationBuilder when:
- You need complete isolation between components
- You’re creating a microservice architecture within a single application
- You need independent deployment of application parts
- You’re working with different profiles for different contexts
Use ServletRegistrationBean when:
- You need multiple servlets on one port
- You want to minimize resource consumption
- You need simple configuration
- You need shared beans between servlets
Combined Approach
In complex scenarios, you can combine both approaches:
public static void main(String[] args) {
new SpringApplicationBuilder(MainApplication.class)
.web(WebApplicationType.SERVLET)
.properties("server.port=8080")
.child(MicroserviceConfig.class)
.web(WebApplicationType.SERVLET)
.properties("server.port=8081")
.run(args);
}
In this case, the main context runs on port 8080, and the microservice runs on port 8081, providing a balance between isolation and efficient resource utilization.
Conclusion
- SpringApplicationBuilder creates separate ports because each child context is a full Spring Boot application with its own embedded server
- ServletRegistrationBean allows using one port because it creates multiple servlets within a single application and single server
- The choice of approach depends on isolation requirements - if you need complete isolation, use SpringApplicationBuilder; if simplicity and efficiency are important, use ServletRegistrationBean
- To work on one port with SpringApplicationBuilder, you’ll need either manual configuration or switching to the ServletRegistrationBean approach
- Both approaches have their advantages - the choice depends on the specific requirements of your application architecture