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.
Related
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/
I'm trying to call a #Cacheable method from within the same class.
And it didn't work. Because of:
In proxy mode (the default), only external method calls coming in through the proxy are intercepted. This means that self-invocation (in effect, a method within the target object that calls another method of the target object) does not lead to actual caching at runtime even if the invoked method is marked with #Cacheable. Consider using the aspectj mode in this case. Also, the proxy must be fully initialized to provide the expected behavior, so you should not rely on this feature in your initialization code (that is, #PostConstruct).
It means, #Cachable(also #Transactional) works by proxy classes which is Spring AOP in. a internal call in the same class make call by 'this' instead of proxy classes.
To solve the problem, I should call a method by proxy or using AspectJ(another AOP).
So, I found 4 solutions.
What is your choice? and why others are not recommended?
Please, share your opinion!
using AspectJ (another AOP)
get the Bean from ApplicationContext and use it
#Service
public class UserService implements Service {
#Autowired
private ApplicationContext applicationContext;
private Service self;
#PostConstruct
private void init() {
self = applicationContext.getBean(UserService.class);
}
}
self-autowiring using #Resource //since Spring 4.3
#Component
#CacheConfig(cacheNames = "SphereClientFactoryCache")
public class CacheableSphereClientFactoryImpl implements SphereClientFactory {
/**
* 1. Self-autowired reference to proxified bean of this class.
*/
#Resource
private SphereClientFactory self;
#Override
#Cacheable(sync = true)
public SphereClient createSphereClient(#Nonnull TenantConfig tenantConfig) {
// 2. call cached method using self-bean
return self.createSphereClient(tenantConfig.getSphereClientConfig());
}
#Override
#Cacheable(sync = true)
public SphereClient createSphereClient(#Nonnull SphereClientConfig clientConfig) {
return CtpClientConfigurationUtils.createSphereClient(clientConfig);
}
}
make the Bean scope of the class as 'prototype' instead of 'singleton'
#Service
#Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
class AService {
private final AService _aService;
#Autowired
public AService(AService aService) {
_aService = aService;
}
#Cacheable("employeeData")
public List<EmployeeData> getEmployeeData(Date date){
..println("Cache is not being used");
...
}
public List<EmployeeEnrichedData> getEmployeeEnrichedData(Date date){
List<EmployeeData> employeeData = _aService.getEmployeeData(date);
...
}
}
I'm a newbie in spring :)
Actually, I choose the 4th solution, but I felt it isn't a good way. because I just need to call the caching method by proxy, and it make several beans to achieve it.
After reading articles, I think AspectJ is the best choice. It looks cool, Spring recommends it, and many people also recommend too.
But I don't understand how to AspectJ works (I will study) and I also don't know why others is not recommended.
references
Spring Cache #Cacheable - not working while calling from another method of the same bean
Spring cache #Cacheable method ignored when called from within the same class
https://spring.io/blog/2012/05/23/transactions-caching-and-aop-understanding-proxy-usage-in-spring
https://docs.spring.io/spring/docs/current/spring-framework-reference/integration.html#cache
These days im working on a Web project and i just want to clarify couple of things regarding Spring bean scopes and best practices for Spring based developments. Here i am using a scenario using a sample code
I have a Web Controller as below
#Controller
Public class JobController{
private JobService jobService;
#Autowired
public void setJobService(JobService jobService ) {
this.jobService = jobService ;
}
public void run(){
Job job = new Job();
-- Setting the properties for the Object
jobService.run(job);
}
}
Then I have the Service as below
#Service
Public class JobService {
public void run(Job job){
-- perform the business logic
}
}
In Here i want to make the JobService class stateless so i can define JobService as singleton hence reduce the unnecessary object creation. As per my understanding in-order make a class stateless we do not want to keep instance properties.In This scenario i pass different Job objects to the service. Does this make this JobService statefull because JObservice process different different job objects? Can you please help me to understand
Thanks,
Keth
Passing different objects does not make your service stateful.
Consider this for example.
#Service
Public class JobService {
private Job currentJob;
public void setJob(Job job) {
currentJob = job;
}
public void run(){
-- perform the business logic on currentJob
}
}
This would make the bean 'stateful' and cause unexplained behavior.
The execution of the method in your singleton by multiple controller/threads will not collide and can be assumed to be safe.
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.
#Scheduled(fixedDelay=10000)
#PostConstruct
public void someMethod(){
//my refresh cache code here
}
If I use both #PostConstruct and #Scheduled on a bean method what will be the consequences. Will this method will be executed twice? one after the other of may be at same time ?
The consequence will be that as soon as the class containing this method is created, the #PostConstruct will run. And the #Scheduled will trigger this method after 10_000ms, but only if #EnabledScheduling is added to the context.
The #Scheduled annotation can do both hence they can be merged like that
#Scheduled(fixedDelay=10000, initialDelay=0)
public void someMethod(){
//my refresh cache code here
}