相信大家都知道,想要为SpringBoot开启定时任务,只需要添加@EnbaleScheduling
注解,项目里的定时任务就可以跑起来。 但是有时候存在一种场景,就是同一套代码,我希望它在某种特定条件下才开启定时任务,例如:只在生产环境开启。简单地说,我需要一个有条件的@EnbaleScheduling
。
自定义Configuration
最简单的方案,就是自定义一个Configuration,为它同时添加@EnbaleScheduling
和Conditional
条件。比如:
java
@Profile("prod")
@EnableScheduling
@Configuration
public class DemoConfig {
}
但这样需要每个有这个需求的项目都自定义一个Configuration,不如@EnbaleScheduling
可以直接注解在Application上。
自定义注解
于是我考虑自定义一个新的@EnbaleSchedulingOn
注解。 类似这样:
java
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface EnableSchedulingOn {
String[] profiles() default {"prod"};
}
先来看一下@EnbaleScheduling
是怎么生效的。

可以看到,定时任务生效的关键就是@Import({SchedulingConfiguration.class})
。
但是如何做到根据条件决定是否引入这个类呢?
@Import
注解不仅可以引入Configuration类,还可以引入一个东西,叫ImportSelector
。可以看一下@EnbaleAsync
注解,就是这么做的。

于是咱们照葫芦画瓢,也引入一个ImportSelector。 注解变成这样:
java
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Import({SchedulingImport.class})
@Documented
public @interface EnableSchedulingOn {
String[] profiles() default {"prod"};
}
然后ImportSelector这样:
java
@Slf4j
public class SchedulingImport implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!matchCondition(annotationMetadata)) {
return new String[0];
}
return new String[]{SchedulingConfiguration.class.getName()};
}
private boolean matchCondition(AnnotationMetadata metadata) {
// todo
}
}
可以看到,在符合条件的时候,我会把之前说的那个关键的类:SchedulingConfiguration.class
的名字返回出去,不符合条件的时候,就直接返回一个空Array。
现在遇到了一个新问题,我如何获取环境变量呢? 这就涉及到另一个接口,叫BeanFactoryAware
。
它可以在初始化的时候把BeanFactory
传进来,通过BeanFactory
就能获取想要的Bean
。现在把ImportSelector写完,这里参照了ProfileCondition
的代码:
java
@Slf4j
public class SchedulingImport implements ImportSelector, BeanFactoryAware {
Environment environment;
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!matchCondition(annotationMetadata)) {
return new String[0];
}
return new String[]{SchedulingConfiguration.class.getName()};
}
private boolean matchCondition(AnnotationMetadata metadata) {
MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(EnableSchedulingOn.class.getName());
if (attrs != null) {
for (Object value : attrs.get("profiles")) {
if (environment.acceptsProfiles(Profiles.of((String[]) value))) {
return true;
}
}
log.warn("Scheduling is off");
return false;
} else {
return true;
}
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
environment = beanFactory.getBean(Environment.class);
}
}
此时定时任务就不开启了。 我更换一下条件:
java
@SpringBootApplication
@EnableSchedulingOn(profiles = {"default"})
@Slf4j
public class Application {
public static void main(String[] args) {
log.info("Start up APPLICATION success!");
SpringApplication.run(Application.class, args);
}
}

定时任务就跑起来了。
这里只是拿环境做一个举例,实际上可以有更多有意思的条件。