Spring @Scheduled执行作业的次数是预期的两倍

时间:2018-06-11 03:16:50

标签: java spring scheduled-tasks

我正在尝试定期在Spring MVC应用程序中运行一些工作。根据在线教程,我按如下方式设置了预定作业:

这是AppInitializer(我没有在XML中设置):

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("/");

    }
}

以下是配置文件:

@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 */


}

这是处理预定作业的类:

@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());
    }

}

正如您所看到的,固定延迟设置为10秒,但我的工作每5秒运行一次。我无法理解为什么会这样。我之前查看了有关此主题的问题,但我找不到匹配的解决方案。

其他帖子建议在这种情况下,bean可能会被初始化两次,但根据我的配置,我不确定是怎么回事。

Similar Question 上面的这个问题似乎与我要问的内容完全相同,但是OP没有发布他/她的配置或设置。大多数答案建议组件的双重初始化,我不确定我的应用程序的情况。

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

修改-1

根据Jennifer的建议,我确实看到两个实例正在调用run方法。

EDIT-2

M.Deinum的猜测绝对正确,我的MvcConfig.java由@ComponentScan注释,这使得Schedule Job运行了两次。但是在从MvcConfig.java中删除该注释后,我的终点停止了工作。我在这里错过了什么..

2 个答案:

答案 0 :(得分:2)

当您创建Scheduled类(ScheduledJobService)的多个实例时,通常会发生这种情况。一个常见的原因是Spring上下文不止一次被创建。

将此添加到run方法以查看是否有多个实例:

public void run() {
   System.out.println(this + " Current time is :: " + Calendar.getInstance().getTime());
}

您将能够在输出中看到它是否有多个实例。

要确保它不是属性值,请尝试不使用10000的硬编码值。

@Scheduled(fixedDelay = 10000)

答案 1 :(得分:1)

问题是您可能在JPAConfigurationMvcConfig中都进行了相同的组件扫描。结果是你基本上都在加载你的整个应用程序两次(所以除非你想要内存问题,奇怪的事务问题等,这是要走的路)。

你想要的是你的ContextLoaderListener(根上下文)加载除了与web相关的bean之外的所有东西,而DispatcherServlet应该只加载与web相关的bean(视图,控制器,建议等)。

@ComponentScan具有您可以控制此属性的属性。在JPAConfiguration添加excludeFilters上的MvcConfig,停用默认值并添加一些includeFilters

@ComponentScan的{​​{1}}应包含以下内容:

JPAConfiguration

您的@ComponentScan( basePackages = {"com.prime.tutorials"}, excludeFilters = { @ComponentScan.Filter( { Controller.class, ControllerAdvice.class }) }) 应该使用相同的MvcConfig而不是includeFilters并禁用默认过滤器。

@ComponentScan(
        basePackages = {"com.prime.tutorials"},
        useDefaultFilters = false,
        includeFilters = {
            @ComponentScan.Filter( { Controller.class, ControllerAdvice.class })
})

你不想做不同的基础套餐,因为这会非常麻烦。恕我直言,你不应该使用技术分离作为创建包的方式(另见https://softwareengineering.stackexchange.com/questions/258196/java-application-structure-horizontal-vs-vertical-split/258197#258197)。