Scheduled method in Spring Boot - spring

I want to send a post request within a period. I created my method like this ;
#Scheduled(cron = "0 0 */6 * *")
#PostMapping
public List<TagsRes> getTags(Date date) {
return null;
}
#Scheduled(cron = "0 0 5 * * ?")
#PostMapping
public List<TagsRes> getAll() {
return null;
}
Should i use #Scheduled in my controller ? Is there any better way to do it?
Thanks!

Controllers are meant to receive web requests, not to post anything.
You can think about them as endpoints exposed by your application and called by external service from time to time.
Now, the Controller abstraction by itself should do any business logic. You may want to validate some parameters received in the request, maybe convert the request parameters to java object with some customization and then call the class (usually mentioned as Service in spring universe) that actually executes your business logic.
Now back to your question.
I suspect you should not "POST a request" but should invoke some piece of code "as if someone called the controller's method (endpoint)". But this time not the external "user" will cause the code execution but an internal scheduler.
If so you can slightly refactor your code to achieve the better clarity:
Create a service that will execute the code
Do not put any scheduling related stuff on controller
From controller call the service
Create a bean and put a "#Scheduled" method on it. The bean will have the service injected and will call it just like the controller does.
Don't forget to put #EnableScheduling annotation - otherwise the scheduled code won't run.
public class MyService {
public void doBusinessLogic(){ ... }
}
#RestController
public class MyController {
#Autowired
private MyService service;
public void myPostMethod(...) {
service.doBusinessLogic(...);
}
}
public class MyScheduledInvoker {
#Autowired
private MyService service;
#Scheduled(...cron expression or whatever...)
public void invokeScheduled() {
service.doBusinessLogic(...);
}
}
#SpringBootApplication
#EnableScheduling
public class MyApp {
public static void main(String [] args) { .... run the app ...}
}

To schedule a job in spring boot application to run periodically, spring boot provides #EnableScheduling and #Scheduled annotations. In my opinion, since spring boot provides the annotation and functionality for scheduler using it will make more sense
Add #EnableScheduling annotation to your spring boot application class.#EnableScheduling is a Spring Context module annotation. It internally imports the SchedulingConfiguration via the #Import(SchedulingConfiguration.class) instruction
#SpringBootApplication
#EnableScheduling
public class SpringBootWebApplication {
}
Now you can add #Scheduled annotations on methods that you want to schedule. The only condition is that methods should be without arguments.
ScheduledAnnotationBeanPostProcessor that will be created by the
imported SchedulingConfiguration scans all declared beans for the
presence of the #Scheduled annotations.
For every annotated method without arguments, the appropriate executor thread pool will be created. This thread pool will manage the scheduled invocation of the annotated method.
#Scheduled(initialDelay = 1000, fixedRate = 10000)
public void run() {
logger.info("Current time is :: " + Calendar.getInstance().getTime());
}
Source: https://howtodoinjava.com/spring-boot/enable-scheduling-scheduled-job-example/

Related

Spring boot request aspect bean with #scheduled task

I use in a spring boot application a component as a Spring aspect:
#RequestScope
#Component
#Aspect
public class MyComponentAspect{
#Around("execution(public * com.mycompany..MyService.saveEntity(*))")
public void getsaveEntityExecutionTime(ProceedingJoinPoint joinPoint) {
...
}
// other advices
}
I need to collect execution time of some methods for each http request the app receives. So as you can see, I have annotated the aspect with #RequestScope.
I Recently add a scheduled task which also needs to call the MyService.saveEntity method.
#Service
#EnableScheduling
public class MyScheduledJob {
private MyService myService;
// ...
}
Of course this is not working because the scheduledJob can't actually find the request scope bean and fails. Actually I don't want the aspect to be enabled when the scheduledJob is running.
Is there a way to let the aspect enabled for normal web request and not for disable it for scheduled task?

How to call #Scheduled method in only one instance using #PostConstruct

There is job that needs to be done on cron schedule
The same logic as in the job must be performed at the start of the spring boot application, therefore #PostConstruct method is used
Shedlock is used, since it is planned to run the application in multiple instances
The question is: how to make the logic from the #PostConstruct method be called in only one instance and not called in others?
An example of my code:
#Component
#AllArgsConstructor
public class TestJob {
private TestService testService;
#PostConstruct
public void init() {
testService.upload();
}
#Scheduled(cron = "${cron}")
#SchedulerLock(name = "uploadJob", lockAtMostFor = "${lockAtMostFor}")
public void execute() {
testService.upload();
}
}
It should work if you put the #PostConstruct method to a different service and call the execute() method.
The reason is that ShedLock by default uses Spring AOP which wraps each method marked by #SchedulerLock in an aspect that does the locking. And Spring AOP usually does not get applied if you call another method in the same class.

Where should I place the application logic of non-web, non-batch Spring Boot app?

I'm trying to start my non-web/non-batch Spring boot application properly.
However, if I place the main tasks in a CommandLineRunner it also gets triggered while running the tests. Running the tasks as batch job will work, but my task doesn't follow batch job semantics.
Is extending SpringApplication class and putting the logic in run() method after super() call the standard way?
If you annotate your CommandLineRunner bean with the #Profile annotation, you can tell Spring to only create the bean when running with (or without) certain profiles, for example:
#Component
#Profile("autorun")
public class JobRunner implements CommandLineRunner {
// ...
}
As long as you don't use those profile when testing, it should not be invoked. You can then run the application using the -Dspring.profiles.active=autorun parameter.
You can create a separate Component class and assign a profile
#Component
#Profile("!test")
public class RunApplication implements CommandLineRunner {
#Override
public void run(String... args) throws IOException {
//Your code here
}
}
This class will only be initialized when the spring.profiles.active variable is not equal to test
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-profiles.html
https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/CommandLineRunner.html

Why does Spring #Scheduled not work with #Bean

I am using Spring Boot and Scheduling.
In Spring, why does this work properly (the method gets called every 5 seconds) -
//#Bean
#Scheduled(fixedRate = 5000)
public void foo() {
System.out.println("bar");
}
But this does not (the method does not get called every 5 seconds) -
#Bean
#Scheduled(fixedRate = 5000)
public void foo() {
System.out.println("bar");
}
A #Bean annotation is used for Java-based configuration to annotate a method, that constructs a bean. It's just wrong annotation and should not be there.
The #Bean annotation is used to define bean instances and must return a result. The #Scheduled annotation belongs on a bean method, not the bean itself, and that method must return void. Also, don't forget to enable scheduling by putting #EnableScheduling on one of your #Configuration classes. For example:
MyConfig.java
#Configuration
#EnableScheduling
public class MyConfig
{
}
MyScheduledJob.java
#Component
public class MyScheduledJob
{
#Scheduled(fixedRate = 5000)
public void doSomething() // Note: Should only return void
{
System.out.println(Instant.now() + ": MyScheduledJob.doSomething() invoked.");
}
}
Output:
2017-01-28T03:08:04.760Z: MyScheduledJob.doSomething() invoked.
2017-01-28T03:08:09.756Z: MyScheduledJob.doSomething() invoked.
2017-01-28T03:08:14.755Z: MyScheduledJob.doSomething() invoked.
2017-01-28T03:08:19.758Z: MyScheduledJob.doSomething() invoked.
The two annotations that you are putting on that method do not make much sense to put together.
#Bean is used to create a singleton for autowiring depdencies across your application. http://docs.spring.io/spring-javaconfig/docs/1.0.0.M4/reference/html/ch02s02.html
#Scheduled is used for scheduling tasks for rerunning.
https://docs.spring.io/spring/docs/current/spring-framework-reference/html/scheduling.html
The reason that this does not make sense is that, generally, your Beans are created at application startup and autowired into their dependencies (done once). In contrast, Schedule implies that you would want this to happen on a regular cadence.

#Async annotated method doesn't operate Asynchronously when called by controller

I'm toying with asynchronous calls to reduce some waiting time on client side when calling a page.
As a experiement i have a controller that calls a pojo with a method annotated with #Async. In that menthod i sleep for 10000 ms to simulation operation to test whether my theory works, and it seems not to. Code below and further information can be found after it:
Test Controller
#Conroller
public class TestController {
#RequestMapping("/test")
public String testAsyncCall() {
new AsyncTestClass().asyncOpOne();
return "secondpage";
}
}
Asynchronous Class containing the #Async annotated method
public class AsyncTestClass {
#Async
public void asyncOpOne() {
try {
Thread.sleep(10000);
System.out.println("done working");
} catch (InterruptedException e) {
//
}
}
}
Now from my understanding when the client makes the call to "/test" in their browser the controller should return call the asynchronous method and instantly return "secondpage" to be rendered.
What is happening is that the controller doesn't return the second page until the 10000 ms in the asynchronous call has finished, only then it returns the secondpage.
FYI
#EnableAsync is added to one of my config files (using Java Configuration).
What am i doing wrong here that is causing the controller to wait for the async to finish its sleep before continuing?
Spring uses AOP to apply #Async behavior to your beans (the same goes for #Transactional for instance).
Spring will only apply AOP to beans it knows, as you are constructing a new instance outside of the scope of Spring the #Async does nothing. Simply add it as a bean to your configuration and inject it into your controller.
#Bean
public AsyncTestClass asyncTestClass() {
return new AsyncTestClass():
}
Then in your calling class.
#Conroller
public class TestController {
#Autowired
private AsyncTestClass asyncTestClass;
#RequestMapping("/test")
public String testAsyncCall() {
asyncTestClass.asyncOpOne();
return "secondpage";
}
}

Resources