Search code examples
javaspring-bootentry-pointclient-applications

How to run spring-boot as a client application?


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?


Solution

  • 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);
        }
    }