If I am developing a rather simple Spring Boot console-based application, I am unsure about the placement of the main execution code. Should I place it in the public static void main(String[] args) method, or have the main application class implement the CommandLineRunner interface and place the code in the run(String... args) method?
I will use an example as the context. Say I have the following [rudimentary] application (coded to interfaces, Spring style):
Application.java
public class Application {
#Autowired
private GreeterService greeterService;
public static void main(String[] args) {
// ******
// *** Where do I place the following line of code
// *** in a Spring Boot version of this application?
// ******
System.out.println(greeterService.greet(args));
}
}
GreeterService.java (interface)
public interface GreeterService {
String greet(String[] tokens);
}
GreeterServiceImpl.java (implementation class)
#Service
public class GreeterServiceImpl implements GreeterService {
public String greet(String[] tokens) {
String defaultMessage = "hello world";
if (args == null || args.length == 0) {
return defaultMessage;
}
StringBuilder message = new StringBuilder();
for (String token : tokens) {
if (token == null) continue;
message.append(token).append('-');
}
return message.length() > 0 ? message.toString() : defaultMessage;
}
}
The equivalent Spring Boot version of Application.java would be something along the lines:
GreeterServiceImpl.java (implementation class)
#EnableAutoConfiguration
public class Application
// *** Should I bother to implement this interface for this simple app?
implements CommandLineRunner {
#Autowired
private GreeterService greeterService;
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
System.out.println(greeterService.greet(args)); // here?
}
// Only if I implement the CommandLineRunner interface...
public void run(String... args) throws Exception {
System.out.println(greeterService.greet(args)); // or here?
}
}
You should have a standard loader:
#SpringBootApplication
public class MyDemoApplication {
public static void main(String[] args) {
SpringApplication.run(MyDemoApplication.class, args);
}
}
and implement a CommandLineRunner interface with #Component annotation
#Component
public class MyRunner implements CommandLineRunner {
#Override
public void run(String... args) throws Exception {
}
}
#EnableAutoConfiguration will do the usual SpringBoot magic.
UPDATE:
As #jeton suggests the latest Springboot implements a straight:
spring.main.web-application-type=none
spring.main.banner-mode=off
See docs at 72.2
Related
Application.java
#SpringBootApplication(scanBasePackages = {"ru.pcask.clients",
"ru.pcask.activities"
})
#EntityScan(value={"ru.pcask.clients",
"ru.pcask.activities"})
#EnableJpaRepositories(value={"ru.pcask.clients",
"ru.pcask.activities"})
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
"ru.pcask.clients" seems to be a constant. But I don't know how to organize it?
I tried like this:
Constants.java
#Configuration
public class Constants {
private static final String CLIENT = "ru.pcask.clients";
#Bean
public String getClientConst() {
return this.CLIENT;
}
}
But this seems to be a garbage. I don't even know how to use it in #SpringBootApplication.
I already managed to start Spring Shell using Spring Boot:
#SpringBootApplication
public class Main {
public static void main(String[] args) {
SpringApplication.run(Main.class);
}
}
All my #ShellComponent classes are detected and I can use the shell as expected.
Now I would like to run the shell without Spring Boot, I expect it to look something like this
Shell shell = context.getBean(Shell.class);
shell.run(...);
What approach should I take to configure all required dependencies myself?
Thanks in advance!
By extracting the necessary parts of ebottard's link (Thank you!) I finally managed to run the shell like I wanted:
#Configuration
#Import({
SpringShellAutoConfiguration.class,
JLineShellAutoConfiguration.class,
JCommanderParameterResolverAutoConfiguration.class,
StandardAPIAutoConfiguration.class,
StandardCommandsAutoConfiguration.class,
})
public class SpringShell {
public static void main(String[] args) throws IOException {
ApplicationContext context = new AnnotationConfigApplicationContext(SpringShell.class);
Shell shell = context.getBean(Shell.class);
shell.run(context.getBean(InputProvider.class));
}
#Bean
#Autowired
public InputProvider inputProvider(LineReader lineReader, PromptProvider promptProvider) {
return new InteractiveShellApplicationRunner.JLineInputProvider(lineReader, promptProvider);
}
}
See this example that shows how to wire up everything without relying on Autoconfiguration.
without Spring Boot I write this:
#Configuration
#ComponentScan(value = {"path.to.commands", "org.springframework.shell.commands", "org.springframework.shell.converters", "org.springframework.shell.plugin.support"})
public class TestShell {
private static String[] args;
#Bean("commandLine")
public CommandLine getCommandLine() throws IOException {
return SimpleShellCommandLineOptions.parseCommandLine(args);
}
#Bean("shell")
public JLineShellComponent getShell() {
return new JLineShellComponent();
}
public static void main(String[] args) {
TestShell.args = args;
System.setProperty("jline.terminal", "none");
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(TestShell.class);
ctx.registerShutdownHook();
JLineShellComponent shell = ctx.getBean(JLineShellComponent.class);
shell.start();
shell.waitForComplete();
ExitShellRequest exitShellRequest = shell.getExitShellRequest();
if (exitShellRequest == null)
exitShellRequest = ExitShellRequest.NORMAL_EXIT;
System.exit(exitShellRequest.getExitCode());
}
}
and command class:
#Component
public class Hello implements CommandMarker {
#CliCommand(value="hi", help = "say hello.")
public String hi() {
return "hello";
}
}
see org.springframework.shell.Bootstrap.
I have spring boot application with profiles. Now I want to switch profile at runtime, refresh spring context and continue application execution. How to switch active profile at runtime (switchEnvironment method)?
#SpringBootApplication
public class Application implements CommandLineRunner {
#Autowired
private Config config;
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Override
public void run(String ... strings) throws Exception {
System.out.printf("Application is running in %s environment, service parameters below:\n",
getEnvProperty("spring.profiles.active").toUpperCase());
printServiceParameters();
switchEnvironment();
printServiceParameters();
}
private String getEnvProperty(String propertyName) {
return config.getEnv().getProperty(propertyName);
}
private void printServiceParameters() {
System.out.println(getEnvProperty("service.endpoint"));
}
private void switchEnvironment() {
//todo Switch active profile
}
}
Config.class
#Configuration
#ConfigurationProperties
public class Config{
#Autowired
private ConfigurableEnvironment env;
public ConfigurableEnvironment getEnv() {
return env;
}
public void setEnv(ConfigurableEnvironment env) {
this.env = env;
}
}
All what you need, it's add this method into your main class, and create Controller or Service for call this method.
#SpringBootApplication
public class Application {
private static ConfigurableApplicationContext context;
public static void main(String[] args) {
context = SpringApplication.run(Application.class, args);
}
public static void restart() {
Thread thread = new Thread(() -> {
context.close();
context = SpringApplication.run(Application.class, "--spring.profiles.active=your_profile");
});
thread.setDaemon(false);
thread.start();
}
}
Controller:
#RestController
public class RestartController {
#PostMapping("/restart")
public void restart() {
Application.restart();
}
}
To elaborate on some of the other answers, this is what tools like Netflix Archaius (https://github.com/Netflix/archaius/wiki) attempt to solve (Dynamic Context Configurations). As far as I'm aware, the only way to accomplish this would be to refresh the contexts by restarting the application.
In Spring Boot, a main class needs to be specified, which is the entry point to the app. Typically this is a simple class with a standard main method, as follows;
#SpringBootApplication
public class MySpringApplication {
public static void main(String [] args) {
SpringApplication.run(MySpringApplication.class, args);
}
}
This class is then specified as the main entry point when the application runs.
However, I want to run my code using a different main class using config to define this, And without using a different jar!! (I know rebuilding the jar will enable me to specify an alternative main class, but this effectively gives me two apps, not one! So, how can I do this to utilise one jar with two main classes and select the one to use via the Spring application.yml file?
I found an answer - use the CommandLineRunner interface...
So now I have two classes;
public class ApplicationStartupRunner1 implements CommandLineRunner {
#Override
public void run(String... args) throws Exception {
//implement behaviour 1
}
and
public class ApplicationStartupRunner2 implements CommandLineRunner {
#Override
public void run(String... args) throws Exception {
//implement behaviour 2
}
and how to switch between them in config..
#Configuration
public class AppConfig {
#Value("${app.runner}")
private int runner;
#Bean
CommandLineRunner getCommandLineRunner() {
CommandLineRunner clRunner = null;
if (runner == 1) {
clRunner = new ApplicationStartupRunner1();
} (else if runner == 2) {
clRunner = new ApplicationStartupRunner2();
} else {
//handle this case..
}
return clRunner;
}
}
and finally in the application.properties file, use
app.runner=1
I would stick with the original pattern of having just one main class and main method, however within that method you could configure where you want to go. I.e. rather than having 2 main methods and configuring which gets called make these 2 methods just normal methods, and create one main method which uses the config to determine which of your two methods get run.
The answer of jonny.l is fine.
Another very similar, but more manual/DIY solution is to get the ApplicationContext, from which you can get all other things:
#SpringBootApplication
public class App {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(App.class);
ConfigurableEnvironment env = context.getBean(ConfigurableEnvironment.class);
String mainApp = env.getProperty("app.runner");
if (mainApp.equals("Main1")) {
Main1 main1 = context.getBean(Main1.class);
main1.run();
} else if (mainApp.equals("Main2")) {
Main2 main2 = context.getBean(Main2.class);
main2.run();
}
}
}
#Service #Lazy
public class Main1 {
public void run() {}
}
#Service #Lazy
public class Main2 {
public void run() {}
}
#Lazyis used to prevent those beans from loading unnecessarily automatically.
I notice that Spring Boot application classes can extend other classes, but that the main(String[] args) methods generally all use SpringApplication.run(Application.class, args). The examples often use different annotations above the Application class definition.
This OP asks for a simple summary of three closely related questions:
1.) What are the possible classes that a Spring Boot Application.java class can extend?
2.) What are the intended uses of each of the extension options?
3.) And does the choice of a given extension also dictate specific annotations that must be added to the class definition?
From my research, I have identified the following three extension options:
1.) Extend nothing at all, as per this example:
#Configuration
#EnableAutoConfiguration
#ComponentScan
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
2.) Extend WebMvcConfigurerAdapter, as per this example:
#SpringBootApplication
#Controller
public class UiApplication extends WebMvcConfigurerAdapter {
public static void main(String[] args) {
SpringApplication.run(UiApplication.class, args);
}
}
3.) Extend SpringBootServletInitializer, as per this example:
#Configuration
#EnableAutoConfiguration
#EnableScheduling
#ComponentScan
public class Application extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}
public static void main(String... args) {
System.setProperty("spring.profiles.default", System.getProperty("spring.profiles.default", "dev"));
final ApplicationContext applicationContext = SpringApplication.run(Application.class, args);
}
}
Notice that I kept the annotations and minimal other stuff from the examples. This OP asks simply if specific annotation choices or minimal other stuff are dictated by the choice of extension.
Another one, which is not inheritance but it's composition, is to implement the CommandLineRunner interface so that you can perform some operations when the Spring Boot application starts up, like so:
#SpringBootApplication
public class DevopsbuddyApplication implements CommandLineRunner {
/** The application logger */
private static final Logger LOG = LoggerFactory.getLogger(DevopsbuddyApplication.class);
#Autowired
private UserService userService;
#Value("${webmaster.username}")
private String webmasterUsername;
#Value("${webmaster.password}")
private String webmasterPassword;
#Value("${webmaster.email}")
private String webmasterEmail;
public static void main(String[] args) {
SpringApplication.run(DevopsbuddyApplication.class, args);
}
#Override
public void run(String... args) throws Exception {
User user = UserUtils.createBasicUser(webmasterUsername, webmasterEmail);
user.setPassword(webmasterPassword);
Set<UserRole> userRoles = new HashSet<>();
userRoles.add(new UserRole(user, new Role(RolesEnum.ADMIN)));
LOG.debug("Creating user with username {}", user.getUsername());
userService.createUser(user, PlansEnum.PRO, userRoles);
LOG.info("User {} created", user.getUsername());
}
}
Not sure if this is what you're looking for though.