When and why do we need ApplicationRunner and Runner interface? - java

I'm learning Spring boot. What's some typical use cases for ApplicationRunner or any runner interface?
import org.junit.jupiter.api.Test;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.test.context.SpringBootTest;
#SpringBootTest
class PersistencedemoApplicationTests implements ApplicationRunner {
#Test
void contextLoads() {
}
#Override
public void run(ApplicationArguments args) throws Exception {
// load initial data in test DB
}
}
This is one case I'm aware of. Anything else?

These runners are used to run the logic on application startup, for example spring boot has ApplicationRunner(Functional Interface) with run method
ApplicationRunner run() will get execute, just after applicationcontext is created and before spring boot application startup.
ApplicationRunner takes ApplicationArgument which has convenient methods like getOptionNames(), getOptionValues() and getSourceArgs().
And CommandLineRunner is also a Functional interface with run method
CommandLineRunner run() will get execute, just after applicationcontext is created and before spring boot application starts up.
It accepts the argument, which are passed at time of server startup.
Both of them provides the same functionality and the only difference between CommandLineRunner and ApplicationRunner is CommandLineRunner.run() accepts String array[] whereas ApplicationRunner.run() accepts ApplicationArguments as argument. you can find more information with example here

In order to use ApplicationRunner or CommandLineRunner interfaces, one needs to create a Spring bean and implement either ApplicationRunner or CommandLineRunner interfaces, both perform similarly. Once complete, your Spring application will detect your bean.
In addition, you can create multiple ApplicationRunner or CommandLineRunner beans, and control the ordering by implementing either
org.springframework.core.Ordered interface
org.springframework.core.annotation.Order annotation.
use case:
One might wish to log some command line arguments.
You could provide some instructions to the user on termination of this application.
consider:
#Component
public class MyBean implements CommandLineRunner {
#Override
public void run(String...args) throws Exception {
logger.info("App started with arguments: " + Arrays.toString(args));
}
}
Details on ApplicationRunner

ApplicationRunner and CommandLineRunner are two interfaces Spring Boot provides to run any custom code just before application is fully started.
Spring-batch is a batch processing framework. This uses CommandLineRunner to register and start batch jobs at application startup.
You can also use this interface to load some master data into cache/perform health checks.
The use-case varies from application to application.

Or like this:
#Bean
ApplicationRunner appStarted() {
return args -> {
logger.info("Ready to go - args: {}", args.getNonOptionArgs());
};
}

Related

Spring Boot multiple CommandLineRunner execution order and state

I have some startup operations to be executed one after another. Both of these runners performs some heavy database operations.
#Slf4j
#Data
#Component
#Order(1)
public class MyFailureHandlerA implements CommandLineRunner {
#Override
public void run(String... args) throws Exception {
some_io_calls_a();
}
}
#Slf4j
#Data
#Component
#Order(2)
public class MyFailureHandlerB implements CommandLineRunner {
#Override
public void run(String... args) throws Exception {
some_io_calls_b();
}
}
I need to make sure that #Order(2) runner starts only after #Order(1) has finished it's processing.
Is there any way to achieve this, like registering the runners onto some strategies so that they can be monitored for their completion.
I found a way, like, I can go for waiting till the value of a singleton scoped bean variable turns true, which is set only when first runner finishes it's task. But I'm afraid if this is recommended or not.
Or, can I rely on #DependsOn for the task completion? Is is guaranteed that, by using #DependsOn("MyFailureHandlerA") over MyFailureHandlerB will guaranteee that 1st runner has completed it's entire operations?
Another way I was trying is, invoking the services call one after another from one single runner, like this:
#Slf4j
#Data
#Component
public class MyFailureHandler implements CommandLineRunner {
#Override
public void run(String... args) throws Exception {
some_io_calls_q();
some_io_calls_b();
}
}
Would this makes some sense and will ensure that ops are executed in order one after another?
spring-boot-2.3.2
some_io_calls_q();
some_io_calls_b();
If both your methods do synchronized tasks, then using order is enough.
Your command will be executed following the order annotation.
You can print few logs to check about it.
Otherwise, it's not enough with async task. You have to control when/how the task is completed...
The most simple solutions as you describe is calling both method in one commandlinerunner in case you are not sure the internal process of spring.
#DependsOn: only working with process creating bean. It's not relative the execution of commands.

How to use different implementation in unit test when class injects with Qualifier

I have a spring bean component which I want to unit test for example:
#Component
public class UploadRequestTasklet implements Tasklet {
private final Uploader fileUploader;
public UploadRequestTasklet(#Qualifier("mainUploader") final Uploader fileUploader) {
this.fileUploader = fileUploader;
}
#Override
public RepeatStatus execute(final StepContribution stepContribution,
final ChunkContext chunkContext) throws IOException, InterruptedException {
Info result = fileUploader.remoteUpload(context, info);
return RepeatStatus.FINISHED;
}
}
I have multiple different implementation of Uploader interface. For production, I use the mainUploader which uploads files to remote server. For integration and unit test, I want to use the localFileUploader implementation. The uploader classes live outside of my project and I have imported these classes from another project using maven dependency.
My question is, how can I override the mainuploader and in unit test, use the localFileUploader version?
the class UploadRequestTasklet has Qualifier annotation specifying to use main implementation which I cannot seem to override in unit test.
You can use #Profile together with your interface Tasklet implementations, for example
#Component
#Profile("local")
public class UploadRequestTasklet implements Tasklet {
...
}
for a run that set jvm args -Dspring.profiles.active=local if you want to run test by maven or gradle. But, if you want to do it by IDE you can set #ActiveProfiles("local") with your component
see more here

Spring boot application for background workers

I have defined the following Spring boot application:
#SpringBootApplication
public class Application implements CommandLineRunner {
public static void main(String[] args) {
new SpringApplicationBuilder(Application.class).web(WebApplicationType.NONE).run(args);
}
public void run(String... args) throws Exception {
Thread.currentThread().join();
}
}
I also have a package of workers (i.e. could be classes which implements Runnable). They are supposed to run indefinitely.
What is the "Spring way" to run them? (and doing so automatically, without explicitly knowing them)
Thanks
and doing so automatically, without explicitly knowing them
There's no mechanism to automatically run some Runnables from a certain place. You need to find a way to inform Spring about these classes.
Three common scenarios:
Execute some code during the application startup: ApplicationRunner and CommandLineRunner.
You either congregate Runnables and wrap them up into an [Application|CommandLine]Runner which should be a bean (e.g. #Bean, #Component, etc) or make each Runnable a separate [Application|CommandLine]Runner.
Execute some code at some point of time: TaskExecutor.
You inject a TaskExecutor and give it previously gathered Runnables.
Execute some code repeatedly: TaskScheduler.
You inject a TaskScheduler and give it previously gathered Runnables, plus a trigger.
More details: Task Execution and Scheduling
You could (1) have your classes that implement Runnable be annotated with #Component, so that Spring can find them. You can then (2) write a WorkManager, annotated with #Service, with an #Autowired List - which Spring would initialize with a list instances of all your classes from (1). And could (3) write a #PostConstruct method in your WorkManager that would iterate over that list of Runnables, and pass each one to a TaskExecutor to run...

Call Spring component from Java code

I have this Spring component which I want to call from several locations in Java web application:
#Component
public class NotificationListener {
public void notificationProcess(TransactionsBean ro) {
// some code
}
}
Can I use it as a normal object in Spring or I should use other way to call the Spring code?
What you can do is retrieve the current ApplicationContext by implementing ApplicationContextAware and lookup the bean yourself with getBean.
Another thing you can try is autowire like you're used to with #Autowired and then use the following utility from Spring:
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);

Should I test the main() method of Spring Boot Application and how?

When I create Spring Boot Application it generates 2 classes:
#SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
And the second on is test:
#RunWith(SpringRunner.class)
#SpringBootTest
public class AppTest {
#Test
public void contextLoads() {
}
}
Like you can notice contextLoads test is empty. How should I provide correct test for contextLoad? It should stay empty maybe? Is it correct? Or I should put something inside?
UPDATE:
Why this test should stay? If I have more tests in application I'll know if spring context is loaded or nope. Isn't it just excessive.
I readed answers like this but it didn't gave me a clear answer.
Actually, the main() method of the Spring Boot Application is not covered by the unit/integration tests as the Spring Boot tests don't invoke the main() method to start the Spring Boot application.
Now I consider that all answers of this post seem overkill.
They want to add a test of the main() method to make a metric tool happy (Sonar).
Loading a Spring context and loading the application takes time.
Don't add it in each developer build just to win about 0.1% of coverage in your application.
I added an answer about that.
Beyond your simple example and the other post that you refer to, in absolute terms it may make sense to create a test for the main() method if you included some logic in. It is the case for example as you pass specific arguments to the application and that you handle them in the main() meethod.
Leave it empty. If an exception occurs while loading the application context the test will fail. This test is just saying "Will my application load without throwing an exception"
Update for Second Question
The reason you want to keep this test is that when something fails you want to find the root cause. If you application context is unable to load, you can assume all other tests that are failing are due to this. If the above test passes, you can assume that the failing tests are unrelated to loading the context.
When you build a Spring boot application using Spring Initializer. It auto creates Application and its Test Class
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
#SpringBootTest
class ApplicationTest {
#Test
void contextLoads() {
}
}
Note the use of #SpringBootTest annotation on test class which tells Spring Boot to look for a main configuration class (one with #SpringBootApplication, for instance) and use that to start a Spring application context. Empty contextLoads() is a test to verify if the application is able to load Spring context successfully or not.
You do not need to provide any test cases for empty method as such. Though you can do something like this to verify your controller or service bean context:-
#SpringBootTest
public class ApplicationContextTest {
#Autowired
private MyController myController;
#Autowired
private MyService myService;
#Test
public void contextLoads() throws Exception {
assertThat(myController).isNotNull();
assertThat(myService).isNotNull();
}
}

Categories

Resources