Reputation: 5628
I am trying to run some job in my Spring MVC application periodically. Based on tutorials online I set up the Scheduled Job as follows:
Here is the AppInitializer (I have no setup in the XMLs):
public class AppInitializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
rootContext.register(JPAConfiguration.class);
servletContext.addListener(new ContextLoaderListener(rootContext));
AnnotationConfigWebApplicationContext dispatcherServlet = new AnnotationConfigWebApplicationContext();
dispatcherServlet.register(MvcConfig.class);
ServletRegistration.Dynamic dispatcher = servletContext.addServlet("dispatcher", new DispatcherServlet(dispatcherServlet));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/");
}
}
Here is the configuration file:
@Configuration
@EnableScheduling
@EnableTransactionManagement
@ComponentScan(basePackages = {"com.prime.tutorials"})
@EnableJpaRepositories(basePackages = {"com.prime.tutorials.model", "com.prime.tutorials.repository"})
public class JPAConfiguration {
@Value("${jdbcURL}")
private transient String jdbcURL;
@Value("${dbPassword}")
private transient String dbPassword;
/* The usual stuff here, let me know if you want me post that as well */
}
This is the class handling the Scheduled job:
@Service
public class ScheduledJobService {
@Autowired
private PrimeRepository primeRepository
@Scheduled(fixedDelayString = "${fixedDelay.in.milliseconds:10000}")
public void run() {
System.out.println("Current time is :: " + Calendar.getInstance().getTime());
}
}
As you can see the fixed delay is set to 10 secs but my job is running every 5 seconds. I am not able to understand why this is happening. I went through the questions posted in regards to this topic before but I havent been able to find a matching solution.
Other posts suggest that in such cases the beans might be getting initialized twice but based on my config I am not sure how that is happening.
Similar Question This question above seems like an exact duplicate of the what I am asking but the OP hasnt posted his/her configuration or setup. Most of the answers suggest double initialization of the components which I am not sure is the case with my application.
Current time is :: Sun Jun 10 22:53:16 EDT 2018
Current time is :: Sun Jun 10 22:53:22 EDT 2018
Current time is :: Sun Jun 10 22:53:26 EDT 2018
Current time is :: Sun Jun 10 22:53:32 EDT 2018
Current time is :: Sun Jun 10 22:53:36 EDT 2018
EDIT-1
Based on suggestion from Jennifer I do see that two instances are calling the run method.
EDIT-2
M.Deinum's guess was absolutely right, my MvcConfig.java was annotated by @ComponentScan which made the Schedule Job run twice. But after removing that annotation from the MvcConfig.java my end-points stopped working. What am I missing here..
Upvotes: 0
Views: 6412
Reputation: 125292
The problem is that you, probably, have the same component scan in both your JPAConfiguration
and MvcConfig
. The result is that you basically are loading your whole application twice (so unless you want to have memory-issues, weird transactional issues etc. that is the way to go).
What you want is that your ContextLoaderListener
(the root context) loads everything but web related beans and the DispatcherServlet
should load only web related beans (views, controllers, advices etc.).
The @ComponentScan
has properties with which you can control this. In the JPAConfiguration
add some excludeFilters
on the MvcConfig
disable the defaults and add some includeFilters
.
Your @ComponentScan
for the JPAConfiguration
should contain the following:
@ComponentScan(
basePackages = {"com.prime.tutorials"},
excludeFilters = {
@ComponentScan.Filter( { Controller.class, ControllerAdvice.class })
})
Your MvcConfig
you should use the same but as an includeFilters
instead and disable the default filters.
@ComponentScan(
basePackages = {"com.prime.tutorials"},
useDefaultFilters = false,
includeFilters = {
@ComponentScan.Filter( { Controller.class, ControllerAdvice.class })
})
You don't want to do different base packages as that would be quite cumbersome. It is also IMHO that you shouldn't use technical separation as a way of creating packages (see also https://softwareengineering.stackexchange.com/questions/258196/java-application-structure-horizontal-vs-vertical-split/258197#258197) .
Upvotes: 2
Reputation: 387
This typically happens when you create more than one instance of the Scheduled
class (ScheduledJobService
). A common cause is that the Spring context is created more than once.
Add this to the run method to see if you have more than one instance:
public void run() {
System.out.println(this + " Current time is :: " + Calendar.getInstance().getTime());
}
You will be able to see in the output if its more than one instance.
To make sure its not the property value, try without a hard coded value of 10000.
@Scheduled(fixedDelay = 10000)
Upvotes: 2