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.
Related
I will simplify the code to make my question easier to understand. The code is something like that:
import sample.backend.*;
import sample.frontend.*;
public class Game {
public void run(){
GameBackendImpl gameBackend = new GameBackendImpl();
GameUIImpl gameUI = new GameUIImpl();
}
}
And this next would be my main code, which I have separated for easy testing.
public class Main {
public static void main(String[] args) {
Game game = new Game();
game.run();
}
}
My question here is to know if it is possible to inject a backend and a frontend instance into the Game class from the Main class, using dependency injection.
Thanks in advance
In this simplified case the dependency injection means just creating the instances of the classes you need in the main() method and passing them to the constructor of the Game class:
public class Game {
privat final GameBackend backend;
privat final GameUI ui;
public Game(GameBackend gameBackend, GameUI gameUI) {
this.backend = gameBackend;
this.ui = gameUI;
}
public void run(){
...
}
}
Main class:
public class Main {
public static void main(String[] args) {
GameBackend gameBackend = new GameBackendImpl();
GameUI gameUI = new GameUIImpl();
Game game = new Game(gameBackend, gameUI);
game.run();
}
}
But this approach, of course, doesn't bring the advantages of the Dependency Injection/Inversion of Control.
I guess you are looking for the possibility to inject your dependencies automatically and to be able to configure them according to your needs (different implementations for different environments etc.). In this case you can use a framework like Spring which will take control on the program flow, instantiating and injecting the dependencies into corresponding classes.
The simple implementation using Spring Boot would be the following:
GameBackend.java:
public interface GameBackend {
String getVersion();
}
GameUI.java:
public interface GameUI {
String getVersion();
}
GameBackendImpl.java:
#Component
public class GameBackendImpl implements GameBackend {
#Override
public String getVersion() {
return "Backend_v1";
}
}
GameUiImpl.java:
#Component
public class GameUiImpl implements GameUI {
#Override
public String getVersion() {
return "UI_v1";
}
}
Game.java:
#Component
public class Game {
private final GameBackend backend;
private final GameUI ui;
#Autowired
public Game (GameBackend backend, GameUI ui) {
this.backend = backend;
this.ui = ui;
}
public void run() {
System.out.println("Game bean instantiated with the UI '"+this.ui.getVersion()+"' and Backend '"+this.backend.getVersion()+"' versions");
}
}
SpringBootDemoApp.java:
#SpringBootApplication
public class SpringBootDemoApp implements CommandLineRunner {
#Autowired
ApplicationContext applicationContext;
public static void main(String[] args) {
SpringApplication.run(SpringBootDemoApp.class, args);
}
#Override
public void run(String... args) {
((Game)(applicationContext.getBean("game"))).run();
}
}
What happens here:
Spring scans classes in the package and finds Beans annotated with the #Component annotation.
Upon instantiating the Game class, Spring considers its constructor, which is annotated with #Autowired and requires exactly 2 parameters (dependencies), and looks for the candidates to be injected. In this case there is only one candidate per each type (you can create multiple different implementations of the GameBackend and GameUI interfaces). Thus, all the preparation work is done by the framework.
There is much more happening under the hood of course.
In the main class, we can check the result by getting the instance of the Game class (Bean with the name "game) from the ApplicationContext. Calling its run() method will print:
"Game bean instantiated with the UI 'UI_v1' and Backend 'Backend_v1' versions"
NOTE: getting and using the instance of the Game class from the main class doesn't bring any advantages and is done only for the demonstration.
There are more DI/IoC frameworks for Java (CDI in J2EE, Guice etc.)
Is there a way to inject a particular interface implementation based on a command line argument in Spring Boot?
I have a data loading app and based on the command line argument I need to load a specific type of data.
Here is my main class and CommandLineRunner:
#SpringBootApplication
public class DataLoadersApplication implements CommandLineRunner {
private Type1LoadProcess type1LoadProcess;
private Type2LoadProcess type2LoadProcess;
public DataLoadersApplication(Type1LoadProcess type1LoadProcess,
Type2LoadProcess type2LoadProcess) {
this.type1LoadProcess = type1LoadProcess;
this.type2LoadProcess = type2LoadProcess;
}
public static void main(String[] args) {
SpringApplication.run(DataLoadersApplication.class, args);
}
#Override
public void run(String... args) {
if (args[0].equalsIgnoreCase("load-type1")) {
type1LoadProcess.process();
} else if (args[0].equalsIgnoreCase("load-type2")) {
type2LoadProcess.process();
}
}
}
Is there a way where I create a DataLoadeProcess interface with two implementations Type1DataLoadProcess and Type2DataLoadProcess and inject the implementaion in main class based on the commandline arg?
You can use Spring profiles to achieve your goal:
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-profiles.html
Create the interface DataLoadProcess
Then the classes:
#Component
#Profile("type1")
public class Type1LoadProcess implements DataLoadProcess {
}
#Component
#Profile("type2")
public class Type2LoadProcess implements DataLoadProcess {
}
Then you can inject the interface type like:
#Autowired
DataLoadProcess dataLoadProcessor;
And now you can start your application with one of the profiles for example with a system property set:
-Dspring.profiles.active=type1
A complete example for this is
#SpringBootApplication
public class DataLoadersApplication implements CommandLineRunner {
public interface LoadProcess {
void doLoad();
}
#Component // default that exists unconditionally in any profile
static class Type1LoadProcess implements LoadProcess {
#Override public void doLoad() { System.out.println("Load1"); }
}
#Profile("type2") // this only exists in the type2 profile
#Primary // if it exists it gets picked over others
#Component
static class Type2LoadProcess implements LoadProcess {
#Override public void doLoad() { System.out.println("Load2"); }
}
// need a 3rd? #Profile("type3") #Primary #Component
#Autowired // need one of them here
private LoadProcess loadProcess;
#Override
public void run(String... args) {
loadProcess.doLoad();
}
public static void main(String[] args) {
SpringApplication.run(DataLoadersApplication.class, args);
}
}
This makes use of profiles and uses the primary bean mechanism to allow for a default implementation when no profile is specified.
You can then select which profile is used via any of the options listed at https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html e.g. by setting an environemnt variable
SPRING_PROFILES_ACTIVE=type2 java -jar myApp.jar
using a property
java -Dspring.profiles.active=type2 java -jar myApp.jar
or even a parameter
java -jar myApp.jar --spring.profiles.active=type2
when you want the type2 implementation. You can still put "type1" as active profile even though it is nowhere defined. It will still do the right thing and use the type1 code since that's the default.
I would use Spring profiles for this. Just turn your implementations into Spring Beans and then load the desired Bean based on an active profile.
When you then specify the active profile(s) as command line parameters when starting the app the respective Bean should get used.
I have a springboot application in the form
src/main/java/example
- Application.java
- JobConfiguration.java
scheduler
- Job1.java
- Job1Runner.java
- Job2.java
- Job2Runner.java
I like to be able to run my jobs on demand locally so I create a seperate Runner class for every job (e.g JobRunner.java) but currently when I run my Application class it also runs my Job1Runner class because it extends CommandLineRunner. Is there a way I can keep my runners seperate? (so the Application class doesnt run them, and the JobRunners dont run each other etc)
Application
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
JobConfiguration
#Configuration
#EnableScheduling
public class JobConfiguration implements SchedulingConfigurer {
#Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
// Register jobs
}
}
Example JobRunner
#SpringBootApplication(scanBasePackages = "com.example")
public class JobXRunner implements CommandLineRunner { // Where X is my job index
#Autowired
private final JobX job;
#Override
public void run(String... args) throws Exception {
job.run();
}
public static void main(String[] args) throws Exception {
SpringApplication.run(JobXRunner.class, args);
}
}
There are more than one ways to solve this problem:
Create multiple profiles and assign them to the job runners. Activate one from command line using spring.profiles.active=jobrunner1, or spring.profiles.active=jobrunner1,jobrunner2 if you want to run more than one job. This solution lets you keep multiple CommandLineRunner implementations.
Specify which class to run, which in turn depends on how you're executing the code. If running using an IDE or a build tool like Maven or Gradle, you'll have to specify the main class name since the default resolution won't work. See this thread for a Maven-specific solution. If you're running the jar, see this thread. CommandLineRunner has a main method, so you can keep them, or just convert to regular main classes.
I need to autowire a bean in a standalone main program as i need to set Up some data . Main program has a dependency on "MyDependencyClass" to set up some services.
I am unclear as to how to get the ApplicationContext as the "MyDependencyClass" is not declared in any spring xml,nor the class is annotated. Please help.
My main program :
public class Main {
#Autowired
private MyDependencyClass myDepClass
public static void main(String[] args) {
Main main1 = new Main();
main1.callDep();
}
private void callDep(){
myDepClass.setUp();
}
}
MyDependencyClass:
public class MyDependencyClass {
public void setUp() {
Sysout("Setting up");
}
}
IF you don't define beans in .xml you need to use #Component or #Repository annotation based on your class type. annotations
Im fairly new to Java Spring IoC and here's my problem
I have a FactoryConfig class with all beans and annotation #Configuration and #ComponentScan written as below.
import org.springframwork.*
#Configuration
#ComponentScan(basePackages="package.name")
public class FactoryConfig {
public FactoryConfig() {
}
#Bean
public Test test(){
return new Test();
}
//And few more #Bean's
}
My Test class has a simple Print method
public class Test {
public void Print() {
System.out.println("Hello Test");
}
}
Now in my Main Class Ive created an ApplicationContentext of FactoryConfig. (I'm expecting all of my #Beans in Factory config will be initialised. However, it returns null when I access the Test class using #Autowired
My Main Class
public class Main {
#Autowired
protected static Test _autoTest;
public static void main(String[] args) throws InterruptedException {
// TODO Auto-generated method stub
ApplicationContext context =
new AnnotationConfigApplicationContext(FactoryConfig.class);
FactoryConfig config = context.getBean(FactoryConfig.class);
config.test().Print();
// _autoTest.Print(); <--- Im getting NULL Pointer Ex here
}
}
What is the correct way to #Autowire and use objects/beans? any clearer explanation would be much appreciated.
Only beans managed by Spring can have #Autowire annotations. Your main class is not managed by Spring: it's created by you and not declared in a Spring context: Spring doesn't known anything about your class, and doesn't inject this property.
You can just access in your main method the Test bean with :
context.getBean(Test.class).Print();
Usually, you get a "bootstrap" from the context, and call this bootstrap to start your application.
Moreover:
On Java, a method shouldn't start with an uppercase. Your Test class should have a print method, not Print.
If you start with Spring, you should maybe try Spring Boot
Spring does not manage your Main class, that's why you are getting Nullpointer Exception.
Using ApplicationContext to load beans, you can get your beans and access Methods as you are already doing -
ApplicationContext context =
new AnnotationConfigApplicationContext(FactoryConfig.class);
FactoryConfig config = context.getBean(FactoryConfig.class);
config.test().Print();
remove the static argument
protected Test _autoTest;
Your class
public class Test {
public void Print() {
System.out.println("Hello Test");
}
}
is not visible to Spring. Try adding an appropriate annotation to it, like #Component.
The reason is that your Main is not managed by Spring. Add it as bean in your configuration:
import org.springframwork.*
#Configuration
#ComponentScan(basePackages="package.name")
public class FactoryConfig {
public FactoryConfig() {
}
#Bean
public Test test(){
return new Test();
}
#Bean
public Main main(){
return new Main();
}
//And few more #Bean's
}
And then you can edit your main() as follows:
public class Main {
#Autowired
protected Test _autoTest;
public static void main(String[] args) throws InterruptedException {
ApplicationContext context =
new AnnotationConfigApplicationContext(FactoryConfig.class);
Test test = context.getBean(Test.class);
Main main = context.getBean(Main.class);
test.Print();
main._autoTest.Print();
}
}