37

我在这里有一个简单的测试方法,它设置为每 5 秒运行一次,并且确实如此,但是查看 System.out 你会发现它似乎在做一些奇怪的事情。

@Scheduled(cron="*/5 * * * * ?")
public void testScheduledMethod() {
     System.out.println(new Date()+" > Running testScheduledMethod...");
}

输出:

Wed Jan 09 16:49:15 GMT 2013 > Running testScheduledMethod...
Wed Jan 09 16:49:15 GMT 2013 > Running testScheduledMethod...
Wed Jan 09 16:49:20 GMT 2013 > Running testScheduledMethod...
Wed Jan 09 16:49:20 GMT 2013 > Running testScheduledMethod...
Wed Jan 09 16:49:25 GMT 2013 > Running testScheduledMethod...
Wed Jan 09 16:49:25 GMT 2013 > Running testScheduledMethod...
Wed Jan 09 16:49:30 GMT 2013 > Running testScheduledMethod...
Wed Jan 09 16:49:30 GMT 2013 > Running testScheduledMethod...

为什么每次都运行两次(出现) ?

4

24 回答 24

27

如果您查看文档,则有一条注释明确指出了这种现象。

该注释位于此链接的第 25.5.1 节下,内容如下:

确保您没有在运行时初始化同一 @Scheduled 注释类的多个实例,除非您确实想为每个此类实例安排回调。与此相关,请确保不要在使用 @Scheduled 注释并在容器中注册为常规 Spring bean 的 bean 类上使用 @Configurable:否则,您将获得双重初始化,一次通过容器,一次通过 @Configurable 方面,每个@Scheduled 方法被调用两次的结果。

我知道这只是目前的建议,但我认为我们没有足够的信息来进一步诊断问题。

于 2013-01-09T17:24:14.433 回答
5

我知道答案!!

不要初始化你的 Scheduled 两次

掠夺您的网络日志:

WebApplicationContext一次和一次 servlet

所以在你servlet.xml不这样做

import resource="classpath:applicationContext.xml"
于 2013-05-22T08:50:09.393 回答
3

这是因为上下文侦听器而发生的

只需删除

<听众>

<监听类>org.springframework.web.context.ContextLoaderListener</监听类>

</听者>

从 web.xml 它应该可以工作。

于 2014-01-12T08:10:09.377 回答
3

我将我的类声明为“服务”,并使用@Scheduled 注释将其声明为调度程序。调度程序正常调用它,但正如我们将类声明为服务一样。它启动了两次。

我删除了服务注释。并在我维护的上下文 XML 中将该类声明为 bean。(在 web.xml 中声明的自定义上下文 XML)。这为我解决了这个问题。希望它可以帮助某人。

于 2019-09-24T09:31:38.637 回答
2

我遇到了类似的问题。可能是因为以下原因。

  1. 春季版本中的错误 https://jira.spring.io/browse/SPR-10830

  2. 上下文被加载两次。

  3. log4j.xml 两次写入日志。它发生在我的情况下,不确定你的情况。如果您尝试过其他选项,也请尝试此选项。

于 2015-01-15T09:11:56.733 回答
1

我遇到了同样的问题,我最终发现问题是由于root contextservlet context.

因此,要解决此问题,您需要将 bean 的创建分离到适当的上下文中。

这个答案很好地解释了如何做到这一点,并且解决了我的问题。

于 2015-02-23T23:17:12.523 回答
1

我有一个类似的问题,我解决了我的问题:

package com.sample.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;

@Configuration
@EnableScheduling
public class JobExecutorConfig {
}

作为spring boot的配置。我将其添加为作业类:

package com.sample.jobs;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component
public class Job {

  private final Logger log = LoggerFactory.getLogger(this.getClass());

  @Autowired
  MyOtherClass moc;

  @Scheduled(fixedRate = 60000) // every 60 seconds
  public void doJob() {
    log.debug("Job running !!!");
    try {
     moc.doSomething()
    } catch (Exception e) {
      log.error(e.getMessage());
    }
    finally {

      log.debug("job Done !!!");
    }

  }

  // examples of other CRON expressions
  // * "0 0 * * * *" = the top of every hour of every day.
  // * "*/10 * * * * *" = every ten seconds.
  // * "0 0 8-10 * * *" = 8, 9 and 10 o'clock of every day.
  // * "0 0/30 8-10 * * *" = 8:00, 8:30, 9:00, 9:30 and 10 o'clock every day.
  // * "0 0 9-17 * * MON-FRI" = on the hour nine-to-five weekdays
  // * "0 0 0 25 12 ?" = every Christmas Day at midnight
}
于 2017-05-07T21:36:09.337 回答
0

我正在使用 spring 4.0.3,我遇到了这个问题。我通过重命名我的豆子来解决它。

至:

<task:annotation-driven executor="taskExecutor"
    scheduler="taskScheduler" />
<task:executor id="taskExecutor" pool-size="25" />
<task:scheduler id="taskScheduler" pool-size="25" />

我注意到一些 INFO 日志记录说没有找到名为 taskScheduler 的 bean,因此创建了一个新实例。所以我认为taskScheduler有两个实例。

让我知道这是否也适合你:)

于 2014-04-16T08:49:40.080 回答
0

好吧,在我的情况下,作业的 bean 有 @Component 注释,我在 applicationContext.xml 中有这个:

<task:annotation-driven/> <bean id="getxxx" class="com.kitar.xxxx.jobs.RomeExample"></bean>

所以解决方案是删除bean定义(第二行),因为:

<task:annotation-driven/>:允许在任何 Spring 管理的对象上检测 @Async 和 @Scheduled 注释,因此无需定义作业的 bean,否则它将被调用两次。

于 2014-08-03T19:10:03.407 回答
0

根据这篇文章和Spring Jira,这是 Spring Framework Scheduler 组件中的一个错误。

于 2014-10-30T06:58:34.720 回答
0

如果您的应用程序是 WEB,您可能想要检查您是否在两个不同的上下文中扫描相同包的组件,例如 applicationContext.xml 和 some-servlet.xml。

于 2015-02-14T03:44:26.830 回答
0
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>
         /WEB-INF/spring/root-context.xml
         /WEB-INF/spring/security/spring-security.xml
         /WEB-INF/spring/mongo/mongo-config.xml
         /WEB-INF/spring/appServlet/spring-context.xml
    </param-value>
</context-param>
<servlet>
    <servlet-name>appServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/spring/appServlet/spring-context.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

那是我的 web.xml 。因此,您可以看到“/WEB-INF/spring/appServlet/spring-context.xml”被加载了两次(一次在 context-param 中,一次在 servlet -> init-param 中)。

于 2015-03-31T18:58:47.353 回答
0

我遇到了同样的情况并通过以下方式解决:

1) 调度服务

@Service
public class SchedulerService {

    @Autowired
    @Qualifier("WorkerClass")
    private Worker worker;

    @Scheduled(cron="0/5 * * * * ?", zone="Asia/Colombo")//zone is a sample
    public void doSchedule() {
        worker.work();
    }

}

2) 工人阶级

@Component("WorkerClass")
public class WorkerClass implements Worker {

    @Override
    public void work() {
        doSomething();
    }

    protected void doSomething() {
        system.out.pringln("What must I do?");
    }

}
于 2016-06-01T12:43:06.450 回答
0

我确实遇到了同样的问题,查看我的代码并尝试了这里的所有内容后,我确实发现我在 2 个不同的类中有两次 SpringApplicationBuilder

于 2018-04-25T19:16:35.937 回答
0

我有同样的问题,我尝试了所有建议的方法,但对我没有任何效果。最后,我能够通过删除“SpringBootServletInitializer”的扩展名和 WebApplication 类的“configure”方法来找到解决方案,这样我就停止了重复我的计划任务。

于 2019-10-16T21:58:18.503 回答
0

我有同样的问题。我正在使用基于注释的配置,如下所示:

@Configuration
@EnableScheduling
public class SchedulerConfig {

    @Scheduled(fixedDelay = 60 * 1000)
    public void scheduledJob() { 
        // this method gets called twice... 
    }   
}

我还在扩展 AbstractAnnotationConfigDispatcherServletInitializer 来初始化它。

public class SpringWebApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

@Override
protected Class<?>[] getRootConfigClasses() {
    return new Class<?>[] { MainConfig.class, SchedulerConfig.class };
}

@Override
protected Class<?>[] getServletConfigClasses() {
    return new Class<?>[] {SpringWebConfig.class};
}

@Override
protected String[] getServletMappings() {
    return new String[] { "/" };
}

@Override
protected Filter[] getServletFilters() {
    final CharacterEncodingFilter encodingFilter = new CharacterEncodingFilter();
    encodingFilter.setEncoding(CHARACTER_ENCODING);
    encodingFilter.setForceEncoding(true);
    return new Filter[] { encodingFilter };
}

}

删除SchedulerConfig.classfromgetRootConfigClasses()方法对我有用。

@Override
protected Class<?>[] getRootConfigClasses() {
    return new Class<?>[] { MainConfig.class };
}

我希望这有帮助。

于 2018-09-30T09:42:17.117 回答
0

你好,

这可能是由于以下两个原因中的任何一个:

  1. App Context 被加载两次,或者
  2. 您有多个应用程序实例正在运行(用于扩展目的)。

对于 1),您可以遵循上述任何答案。我将谈论(2)。

对于 (2),通常,在云环境中,您将有多个实例(如 2、3、4....)在云中运行。因此,您的调度程序将由每个应用程序实例触发。我在云环境中也遇到过这个问题。我们在 Cloud Foundry 上运行我们的应用程序,并且我们已经扩展到了 2 个应用程序实例。我们的调度程序开始触发两次(从每个应用程序实例,即 app_instance_0 和 app_instance_1)。

为了克服,我们可以执行以下任何一项操作:

方法一:使用 System.getenv("CF_INSTANCE_INDEX");
// 此 var 默认情况下由 Cloud Foundry 作为 env 变量提供给应用程序。它为我们提供了触发方法调用的应用程序实例索引。因此,当 app_instance_0 运行时,我们可以使用简单的条件来触发 schduler:

 @Scheduled(cron = * * * * * *)
 public void scheduledTask() {
 String appInstanceIndex = System.getenv("CF_INSTANCE_INDEX"); 
 if (appInstanceIndex.equals("0")) {
 .... //your scheduling logic here
 .... //your scheduling logic here
 }
 else {
 //dont do anything as its call from other app instances...
 } 
 }
                      

这里的一个主要优点是我们不进行任何数据库调用。当然,如果您不使用 CF,它将无法正常工作。但请检查您的 PaaS 是否提供任何此类变量。

方法二:这是一种通用方法。为调度程序实现一个新表。添加唯一约束列。在您的 shceduler 方法中,生成今天的日期并将其添加到 Db 中。(此日期保存在具有唯一约束的列的表中)。现在,这个调度程序第一次总是会正常运行。当其他应用程序实例尝试再次创建今天的日期并保存到数据库时,会导致异常,因为数据库具有唯一约束并且无法再次添加日期。因此,对于其他应用程序实例,调度程序逻辑将失败并导致异常。

 @Scheduled(cron = * * * * * *)
 public void scheduledTask() {
 String date = LocalDate.now().toString();
 try{
 insertDateInDb();
 // your scheduler logic here
 }
 catch(Exception e){
 // Dont do anything...
 }                    
 }
于 2020-07-29T14:57:19.987 回答
0

我有同样的问题。花了几个小时试图解决。

解决方案是应用程序在 Tomcat 上部署了两次。

尝试清理 Tomcat 时出现错误。

检查 server.xml Tomcat 文件,我注意到它被部署了两次。还有一个未关闭的“主机”标签。不确定其中哪一个修复了它,但让它再次正常工作感到宽慰。

于 2017-07-05T01:27:32.460 回答
0

就我而言,此问题仅发生在 PRODUCTION 环境中。

它没有在本地暂存环境中复制。

后来我们意识到在生产中我们有多个服务器在运行。

因此,每个服务器都运行自己的 cron,这实际上导致了多个 cron 事件同时发生。

于 2021-11-11T14:55:57.020 回答
0

我的 springboot-application 遇到了同样的问题,调度程序运行了两次。

正如之前的评论中多次提到的,原因是 bean 被多次初始化。

我的解决方案是:

  • 删除 @Component 注解(在包含 @Scheduled 方法的类上)
  • 手动将包含 @Scheduled 方法的类添加到 ApplicationContext

在代码中 - 这是我所做的更改:

// @Component <------ Removed
public class FileTransferScheduler {

    private final Logger logger = LoggerFactory.getLogger(FileTransferScheduler.class);

    @Autowired
    private ExtractorService extractorService;

    @Scheduled(cron = "* /1 * * * *")
    public void scheduleFileTransfer() {

        logger.info("Triggered cronjob to transfer new uploads to dms...");
        extractorService.extractTmcUploads();
    }

}

...然后在 springboot-application 的 main 方法中进行了这种更改:

@EnableScheduling
@SpringBootApplication
public class InsidersExtractorApplication extends SpringBootServletInitializer {

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        // The Scheduler Class needs to be added manually, because it dont have to be defined as Bean
        return application.sources(InsidersExtractorApplication.class, FileTransferScheduler.class);
    }

    public static void main(String[] args) {
        SpringApplication.run(InsidersExtractorApplication.class, args);
    }

}
于 2020-10-07T13:55:23.707 回答
-1

对我来说,经过几次不成功的尝试和配置,解决方法很简单:

编辑 conf/server.xml 文件:

<Host appBase="webapps" deployOnStartup="false" autoDeploy="false" name="localhost" unpackWARs="true">
于 2020-03-02T17:03:36.777 回答
-1

我今天遇到了同样的问题。

在我的项目中,我将调度程序与我的 Spring Boot 应用程序一起使用,而在我的 ScheduledTaks 类中,我使用了 @Component 注释。但我犯了一个错误,因为@Component 代表我的类的一个 bean,而在我的 Application 类中,我用代码为这个类创建了另一个 bean:

public class Application extends SpringBootServletInitializer {

  @Override
  protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
      return application.sources(Application.class);
  }

  public static void main(String[] args) throws Exception {
      SpringApplication.run(Application.class, args);
  }

  @Bean
  public ScheduledTasks getScheduledTasks() {
    return new ScheduledTasks();
  }
}

我只是删除了这个注释,调度程序就可以正常工作了。

按照我的代码 ScheduledTasks 类的示例:

public class ScheduledTasks {
  private static final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");

  @Scheduled(cron = "00 14 11  * * *")
  public void reportCurrentTime() {
    log.info("The date is: {} " + dateFormat.format(new Date()) );
  }
}

结果:

2016-10-20 11:13:41.298  INFO 6832 --- [           main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8080 (http)
2016-10-20 11:13:41.302  INFO 6832 --- [           main] br.com.Application                       : Started Application in 9.257 seconds (JVM running for 9.676)
2016-10-20 11:14:00.002  INFO 6832 --- [pool-2-thread-1] br.com.scheduler.ScheduledTasks          : The date is: {} 11:14:00
于 2016-10-20T14:19:52.017 回答
-1

有同样的问题。

就我而言,我将@Scheduled 函数从@Service 移动到带有@Component 注释的新单独类中,它解决了我的问题

于 2018-11-27T13:33:57.890 回答
-1

在 application.properties 中,添加以下属性,告诉 Spring Boot 应用程序不要在应用程序启动时启动批处理作业。

spring.batch.job.enabled=false
于 2018-09-04T11:47:40.523 回答