I have 2 Main entry points in a single application.
The first main starts the server, maps the controllers and starts some worker threads. These workers receive messages from cloud queues.
In case of increased load, I want to be able to add additional workers to do my job. So I have a second Main entry point in my application which I want to be able to start without starting the default server in spring-boot (as a client application) so as to avoid port conflict (and obviously which will lead to failure).
How do I achieve this?
Launching from the command line with server
and client
profiles
To use the same jar and same entry point with 2 different profiles, you should simply provide the Spring profile at runtime, to have distinct application-${profile}.properties loaded (and potentially Conditional Java config triggered).
Define 2 spring profiles (client
and server
):
- Each will have its own
application-${profile}.properties
- The client's properties will disable the web container
Have a single SpringBootApp and entry point:
@SpringBootApplication
public class SpringBootApp {
public static void main(String[] args) {
new SpringApplicationBuilder()
.sources(SpringBootApp.class)
.run(args);
}
}
Make this class your main-class.
src/main/resources/application-server.properties:
spring.application.name=server
server.port=8080
src/main/resources/application-client.properties:
spring.application.name=client
spring.main.web-environment=false
Launch both profiles from the command line:
$ java -jar -Dspring.profiles.active=server YourApp.jar
$ java -jar -Dspring.profiles.active=client YourApp.jar
You may have @Configuration
classes triggered conditionally based on the active profile too:
@Configuration
@Profile("client")
public class ClientConfig {
//...
}
Launching from the IDE with server
and client
profiles
Launchers:
@SpringBootApplication
public class SpringBootApp {
}
public class LauncherServer {
public static void main(String[] args) {
new SpringApplicationBuilder()
.sources(SpringBootApp.class)
.profiles("server")
.run(args);
}
}
public class ClientLauncher {
public static void main(String[] args) {
new SpringApplicationBuilder()
.sources(SpringBootApp.class)
.profiles("client")
.web(false)
.run(args);
}
}
You may specify additional configuration classes (specific to client or server):
new SpringApplicationBuilder()
.sources(SpringBootApp.class, ClientSpecificConfiguration.class)
.profiles("client")
.web(false)
.run(args);
src/main/resources/application-server.properties:
spring.application.name=server
server.port=8080
src/main/resources/application-client.properties:
spring.application.name=client
#server.port= in my example, the client is not a webapp
Note, you may also have 2 SpringBootApp (ClientSpringBootApp
,
ServerSpringBootApp
), each with its own main, it's a similar setup
which allow you to configure different AutoConfiguration
or ComponentScan
:
@SpringBootApplication
@ComponentScan("...")
public class ServerSpringBootApp {
public static void main(String[] args) {
new SpringApplicationBuilder()
.sources(ServerSpringBootApp.class)
.profiles("server")
.run(args);
}
}
//Example of a difference between client and server
@SpringBootApplication(exclude = SecurityAutoConfiguration.class)
@ComponentScan("...")
public class ClientSpringBootApp {
public static void main(String[] args) {
new SpringApplicationBuilder()
.sources(ClientSpringBootApp.class)
.profiles("client")
.web(false)
.run(args);
}
}