SpringBoot自定义Schedule注解

相信大家都知道,想要为SpringBoot开启定时任务,只需要添加@EnbaleScheduling注解,项目里的定时任务就可以跑起来。 但是有时候存在一种场景,就是同一套代码,我希望它在某种特定条件下才开启定时任务,例如:只在生产环境开启。简单地说,我需要一个有条件的@EnbaleScheduling

自定义Configuration

最简单的方案,就是自定义一个Configuration,为它同时添加@EnbaleSchedulingConditional条件。比如:

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

定时任务就跑起来了。

这里只是拿环境做一个举例,实际上可以有更多有意思的条件。

项目地址

相关推荐
独立开阀者_FwtCoder8 分钟前
"页面白屏了?别慌!前端工程师必备的排查技巧和面试攻略"
java·前端·javascript
Touper.13 分钟前
JavaSE -- 泛型详细介绍
java·开发语言·算法
静若繁花_jingjing30 分钟前
Redis线程模型
java·数据库·redis
hello早上好1 小时前
CGLIB代理核心原理
java·spring
魔镜魔镜_谁是世界上最漂亮的小仙女1 小时前
java-web开发
java·后端·架构
爱吃小土豆豆豆豆1 小时前
定时器和守护线程
java
Seven971 小时前
了解GC吗?什么是GC?
java
Edingbrugh.南空1 小时前
Flink ClickHouse 连接器维表源码深度解析
java·clickhouse·flink
掘金-我是哪吒2 小时前
分布式微服务系统架构第157集:JavaPlus技术文档平台日更-Java多线程编程技巧
java·分布式·微服务·云原生·架构
飞翔的佩奇2 小时前
Java项目:基于SSM框架实现的忘忧小区物业管理系统【ssm+B/S架构+源码+数据库+毕业论文+开题报告】
java·数据库·mysql·vue·毕业设计·ssm框架·小区物业管理系统