java 在 Spring Boot IntegrationTest 上禁用 @Schedule
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/40684903/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Disable @Schedule on Spring Boot IntegrationTest
提问by fassoline
How can I disable the schedule auto-start on Spring Boot IntegrationTest?
如何在 Spring Boot IntegrationTest 上禁用计划自动启动?
Thanks.
谢谢。
回答by Duke0fAnkh
Be aware that external components could be enabling scheduling automatically (see HystrixStreamAutoConfigurationand MetricExportAutoConfigurationfrom the Spring Framework). So if you try and use @ConditionalOnProperty
or @Profile
on the @Configuration
class that specifies @EnableScheduling
, then scheduling will be enabled anyway due to external components.
请注意,外部组件可能会自动启用调度(请参阅Spring 框架中的HystrixStreamAutoConfiguration和MetricExportAutoConfiguration)。因此,如果您尝试使用@ConditionalOnProperty
或指定@Profile
的@Configuration
类@EnableScheduling
,那么由于外部组件,无论如何都会启用调度。
One solution
一种解决方案
Have one @Configuration
class that enables scheduling via @EnableScheduling
, but then have your scheduled jobs in separate classes, each of those using @ConditionalOnProperty
to enable/disable the classes that contain the @Scheduled tasks.
拥有一个@Configuration
通过 启用调度的类,@EnableScheduling
然后将调度的作业放在单独的类中,每个类都@ConditionalOnProperty
用于启用/禁用包含@Scheduled 任务的类。
Don't have the @Scheduled
and @EnableScheduling
in the same class, or you will have the issue where external components are enabling it anyway, so the @ConditionalOnProperty
is ignored.
不要将@Scheduled
和@EnableScheduling
放在同一个类中,否则您将遇到外部组件启用它的问题,因此@ConditionalOnProperty
忽略 。
Eg:
例如:
@Configuration
@EnableScheduling
public class MyApplicationSchedulingConfiguration {
}
and then in a separate class
然后在一个单独的班级
@Named
@ConditionalOnProperty(value = "scheduling.enabled", havingValue = "true", matchIfMissing = false)
public class MyApplicationScheduledTasks {
@Scheduled(fixedRate = 60 * 60 * 1000)
public void runSomeTaskHourly() {
doStuff();
}
}
The issue with this solution is that every scheduled job needs to be in it's own class with @ConditionalOnProperty
specified. If you miss that annotation, then the job will run.
这个解决方案的问题是每个预定的作业都需要在它自己的类中@ConditionalOnProperty
指定。如果您错过了该注释,则作业将运行。
Another Solution
另一种解决方案
Extend the ThreadPoolTaskScheduler
and override the TaskScheduler
methods. In these methods you can perform a check to see if the job should run.
扩展ThreadPoolTaskScheduler
和覆盖TaskScheduler
方法。在这些方法中,您可以执行检查以查看作业是否应该运行。
Then, in your @Configuration class where you use @EnableScheduling, you also create a @Bean called taskScheduler which returns your custom thread pool task scheduler).
然后,在您使用@EnableScheduling 的@Configuration 类中,您还创建了一个名为taskScheduler 的@Bean,它返回您的自定义线程池任务调度程序)。
Eg:
例如:
public class ConditionalThreadPoolTaskScheduler extends ThreadPoolTaskScheduler {
@Inject
private Environment environment;
// Override the TaskScheduler methods
@Override
public ScheduledFuture<?> schedule(Runnable task, Trigger trigger) {
if (!canRun()) {
return null;
}
return super.schedule(task, trigger);
}
@Override
public ScheduledFuture<?> schedule(Runnable task, Date startTime) {
if (!canRun()) {
return null;
}
return super.schedule(task, startTime);
}
@Override
public ScheduledFuture<?> scheduleAtFixedRate(Runnable task, Date startTime, long period) {
if (!canRun()) {
return null;
}
return super.scheduleAtFixedRate(task, startTime, period);
}
@Override
public ScheduledFuture<?> scheduleAtFixedRate(Runnable task, long period) {
if (!canRun()) {
return null;
}
return super.scheduleAtFixedRate(task, period);
}
@Override
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, Date startTime, long delay) {
if (!canRun()) {
return null;
}
return super.scheduleWithFixedDelay(task, startTime, delay);
}
@Override
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, long delay) {
if (!canRun()) {
return null;
}
return super.scheduleWithFixedDelay(task, delay);
}
private boolean canRun() {
if (environment == null) {
return false;
}
if (!Boolean.valueOf(environment.getProperty("scheduling.enabled"))) {
return false;
}
return true;
}
}
Configuration class that creates the taskScheduler bean using our custom scheduler, and enables scheduling
使用我们的自定义调度程序创建 taskScheduler bean 并启用调度的配置类
@Configuration
@EnableScheduling
public class MyApplicationSchedulingConfiguration {
@Bean
public TaskScheduler taskScheduler() {
return new ConditionalThreadPoolTaskScheduler();
}
}
The potential issue with the above is that you've created a dependency on an internal Spring class, so if there are changes in the future, you'd have to fix compatibility.
上面的潜在问题是您已经创建了对内部 Spring 类的依赖,因此如果将来有更改,您必须修复兼容性。
回答by MandeSin
I had the same problem. Tried Spring's @ConditionalOnProperty
attribute with my Scheduling Bean but Scheduling still got activated in tests.
我有同样的问题。@ConditionalOnProperty
用我的调度 Bean尝试了 Spring 的属性,但调度仍然在测试中被激活。
The only good workaround I found was to overwrite the scheduling properties in the Test class, so that the job does not have a realchance to run.
我发现的唯一好的解决方法是覆盖 Test 类中的调度属性,这样作业就没有真正运行的机会。
If your real job runs every 5 minutes using property my.cron=0 0/5 * * * *
如果您的实际工作使用属性每 5 分钟运行一次 my.cron=0 0/5 * * * *
public class MyJob {
@Scheduled(cron = "${my.cron}")
public void execute() {
// do something
}
}
Then in the test class you can configure it as:
然后在测试类中,您可以将其配置为:
@RunWith(SpringRunner.class)
@SpringBootTest(properties = {"my.cron=0 0 0 29 2 ?"}) // Configured as 29 Feb ;-)
public class MyApplicationTests {
@Test
public void contextLoads() {
}
}
So even if your job is activated, it will only run at the 0th hour of 29 Feb which happens once in 4 years. So you have a very slim chance of running it.
因此,即使您的作业被激活,它也只会在 2 月 29 日的第 0 小时运行,这种情况每 4 年发生一次。所以你运行它的机会非常渺茫。
You can come up with more fancy cron settings to suit your requirements.
您可以想出更多花哨的 cron 设置来满足您的要求。
回答by Bunarro
An easy solution I figured out in Spring Boot 2.0.3:
我在 Spring Boot 2.0.3 中找到了一个简单的解决方案:
1) extract scheduled method(s) to a separate bean
1) 将预定方法提取到单独的 bean
@Service
public class SchedulerService {
@Autowired
private SomeTaskService someTaskService;
@Scheduled(fixedRate = 60 * 60 * 1000)
public void runSomeTaskHourly() {
someTaskService.runTask();
}
}
2) mock the scheduler bean in your test class
2) 在您的测试类中模拟调度程序 bean
@RunWith(SpringRunner.class)
@SpringBootTest
public class SomeTaskServiceIT {
@Autowired
private SomeTaskService someTaskService;
@MockBean
private SchedulerService schedulerService;
}
回答by Samuel Morais
One way is to use Spring profiles
一种方法是使用 Spring 配置文件
In your test class:
在您的测试课程中:
@SpringBootTest(classes = Application.class)
@ActiveProfiles("integration-test")
public class SpringBootTestBase {
...
}
In your scheduler class or method:
在您的调度程序类或方法中:
@Configuration
@Profile("!integration-test") //to disable all from this configuration
public class SchedulerConfiguration {
@Scheduled(cron = "${some.cron}")
@Profile("!integration-test") //to disable a specific scheduler
public void scheduler1() {
// do something
}
@Scheduled(cron = "${some.cron}")
public void scheduler2() {
// do something
}
...
}
回答by Tom
When your real Spring Boot Application class looks like this:
当您真正的 Spring Boot Application 类如下所示时:
@SpringBootApplication
@EnableScheduling
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
you would have to create another Application class without @EnableScheduling for your integration tests like this:
您必须为您的集成测试创建另一个没有 @EnableScheduling 的 Application 类,如下所示:
@SpringBootApplication
public class MyTestApplication {
public static void main(String[] args) {
SpringApplication.run(MyTestApplication.class, args);
}
}
And then use the MyTestApplication class in your integration test like this
然后像这样在集成测试中使用 MyTestApplication 类
RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = MyTestApplication.class)
public class MyIntegrationTest {
...
}
That's the way I do it, since I have not found a better way.
这就是我这样做的方式,因为我还没有找到更好的方法。
回答by Tobske
I solved this problem by using a separate configuration class and then overwrite this class in the test context. So instead of putting the annotation at the application I only put it at the separate configuration classes.
Normal context:
我通过使用单独的配置类解决了这个问题,然后在测试上下文中覆盖了这个类。因此,我没有将注释放在应用程序中,而是将其放在单独的配置类中。
正常上下文:
@Configuration
@EnableScheduling
public class SpringConfiguration {}
Test context:
测试上下文:
@Configuration
public class SpringConfiguration {}
回答by Felix Aballi
Integrating some answers from above:
整合上面的一些答案:
- Create a separate configuration class for testing purposes (a.k.a "TestConfiguration.class")
Enable Mockito annotations there for other beans: schedulers, etc.
- Read this: 2)
@ConditionalOnClass @ConditionalOnMissingBean @ConditionalOnBean @ConditionalOnJava @ConditionalOnJndi @ConditionalOnMissingClass @ConditionalOnExpression @ConditionalOnNotWebApplication @ConditionalOnWebApplication @ConditionalOnProperty @ConditionalOnResource @ConditionalOnSingleCandidate
- 为测试目的创建一个单独的配置类(又名“TestConfiguration.class”)
为其他 bean 启用 Mockito 注释:调度程序等。
- Read this: 2)
@ConditionalOnClass @ConditionalOnMissingBean @ConditionalOnBean @ConditionalOnJava @ConditionalOnJndi @ConditionalOnMissingClass @ConditionalOnExpression @ConditionalOnNotWebApplication @ConditionalOnWebApplication @ConditionalOnProperty @ConditionalOnResource @ConditionalOnSingleCandidate
Plus, always checking:
另外,始终检查:
- "application.yml" self-creation properties depending on external devices/services
- Auto-configuration annotations on Main class breaking beans initialization sequence
- "applicationContext.xml", "beans.xml" or Classpath loaders
- “application.yml”自创属性取决于外部设备/服务
- Main 类中断 bean 初始化序列上的自动配置注释
- “applicationContext.xml”、“beans.xml”或类路径加载器
Please, read these:
请阅读这些:
- 1) SpringBoot Auto-configuration
- 2) How Spring Boot Autoconfiguration works
Try using mocking annotation on non-required beans for testing such as:
@MockBean private StoresRatingAvgScheduler scheduler; @Before public void init() { MockitoAnnotations.initMocks(this); }
- 1) SpringBoot 自动配置
- 2) Spring Boot 自动配置的工作原理
尝试在非必需 bean 上使用模拟注释进行测试,例如:
@MockBean private StoresRatingAvgScheduler scheduler; @Before public void init() { MockitoAnnotations.initMocks(this); }