XXL-Job详解(四):任务注册原理

前言

看该文章之前,最好看一下之前的文章,比较方便我们理解
XXL-Job详解(一):组件架构
XXL-Job详解(二):安装部署
XXL-Job详解(三):任务开发

任务注册

我们在执行器开发任务的时候,只需要一个@XxlJob注解就可以定义一个任务,那么它是怎么做到的呢

xxl-job之前的版本中是通过继承 IJobHandler 和在类上加注解的方式进行任务标识,在最新版中则抛弃了原有的做法,将任务的粒度细化到了方法级别。

前者的好处是任务编写的范式已经规定好,只需要重写对应抽象类中的方法并加上注解,但每一次编写新的任务执行程序都需要创建新的类来重新实现接口。后者的好处是在于细化了任务的粒度,将注解细化到了方法级别,不需要再重复地继承方法,很好地实现了类的复用。

在SpringBoot版本中,我们使用的执行器是XxlJobSpringExecutor,它是XxlJobExecutor的子类,并且实现了ApplicationContextAware 、SmartInitializingSingleton、DisposableBean 三个接口

public class XxlJobSpringExecutor extends XxlJobExecutor implements ApplicationContextAware, SmartInitializingSingleton, DisposableBean {
}

下面介绍一下XxlJobSpringExecutor实现的三个接口

ApplicationContextAware
ApplicationContextAware 是 Spring 框架中的一个接口,用于在 Spring 容器中获取 ApplicationContext 对象。通过实现这个接口,可以将 Spring 容器中的上下文信息注入到 JavaBean 中,从而让 JavaBean 访问 Spring 配置文件中的 Bean 和其他资源。

当一个类实现 ApplicationContextAware 接口时,Spring 容器会自动将当前应用程序的 ApplicationContext 对象注入到该类的 setApplicationContext 方法中。这样,该类就可以通过 ApplicationContext 对象访问 Spring 容器中的其他 Bean 和资源。

SmartInitializingSingleton
SmartInitializingSingleton 是 Spring 框架中的一个接口,它用于定义在单例对象初始化时执行特定操作的接口。主要用于在Spring容器启动完成时进行扩展操作,即afterSingletonsInstantiated()方法;

DisposableBean
DisposableBean,是在Spring容器关闭的时候预留的一个扩展点,实现DisposableBean接口,并重写destroy(),可以在Spring容器销毁bean的时候获得一次回调

注册方法

下面是XxlJobSpringExecutor实现SmartInitializingSingleton接口的afterSingletonsInstantiated方法,任务注册就在这个方法里

	@Override
    public void afterSingletonsInstantiated() {

        // init JobHandler Repository
        /*initJobHandlerRepository(applicationContext);*/

        // init JobHandler Repository (for method)
        initJobHandlerMethodRepository(applicationContext);

        // refresh GlueFactory
        GlueFactory.refreshInstance(1);

        // super start
        try {
            super.start();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

可以看到在 afterSingletonsInstantiated 方法做了这样几件事情:

1、初始化任务执行程序仓库,即进行任务注册
2、刷新GlueFactory,获取SpringGlueFactory(用于动态脚本任务)
3、启动执行器,此处调用父类start()方法

可以看到任务注册的方法就在initJobHandlerMethodRepository方法

private void initJobHandlerMethodRepository(ApplicationContext applicationContext) {
        if (applicationContext == null) {
            return;
        }
        // 获取bean名称列表
        String[] beanDefinitionNames = applicationContext.getBeanNamesForType(Object.class, false, true);
        for (String beanDefinitionName : beanDefinitionNames) {
            // 从上下文中根据bean元数据名称获取bean对象
            Object bean = applicationContext.getBean(beanDefinitionName);

            Map<Method, XxlJob> annotatedMethods = null;   // referred to :org.springframework.context.event.EventListenerMethodProcessor.processBean
            try {
// 根据bean类元信息获取被@XxlJob注解的方法
                annotatedMethods = MethodIntrospector.selectMethods(bean.getClass(),
                        new MethodIntrospector.MetadataLookup<XxlJob>() {
                            @Override
                            public XxlJob inspect(Method method) {
                                return AnnotatedElementUtils.findMergedAnnotation(method, XxlJob.class);
                            }
                        });
            } catch (Throwable ex) {
                logger.error("xxl-job method-jobhandler resolve error for bean[" + beanDefinitionName + "].", ex);
            }
            //如果没有xxljob方法,则跳过
            if (annotatedMethods==null || annotatedMethods.isEmpty()) {
                continue;
            }

            for (Map.Entry<Method, XxlJob> methodXxlJobEntry : annotatedMethods.entrySet()) {
                //获取被注解的方法
                Method executeMethod = methodXxlJobEntry.getKey();
 //获取注解信息
                XxlJob xxlJob = methodXxlJobEntry.getValue();
                // 注册handler
                registJobHandler(xxlJob, bean, executeMethod);
            }
        }
    }



protected void registJobHandler(XxlJob xxlJob, Object bean, Method executeMethod){
        if (xxlJob == null) {
            return;
        }
        //获取任务名称
        String name = xxlJob.value();
        //make and simplify the variables since they'll be called several times later
        Class<?> clazz = bean.getClass();
        String methodName = executeMethod.getName();
        //判断任务名称是否有效
        if (name.trim().length() == 0) {
            throw new RuntimeException("xxl-job method-jobhandler name invalid, for[" + clazz + "#" + methodName + "] .");
        }
        //检查当前任务名是否已经被使用,注意这里是通过任务名称来进行任务判重的
        if (loadJobHandler(name) != null) {
            throw new RuntimeException("xxl-job jobhandler[" + name + "] naming conflicts.");
        }

        executeMethod.setAccessible(true);

        // 设置初始化方法和销毁方法
        Method initMethod = null;
        Method destroyMethod = null;

        if (xxlJob.init().trim().length() > 0) {
            try {
                initMethod = clazz.getDeclaredMethod(xxlJob.init());
                initMethod.setAccessible(true);
            } catch (NoSuchMethodException e) {
                throw new RuntimeException("xxl-job method-jobhandler initMethod invalid, for[" + clazz + "#" + methodName + "] .");
            }
        }
        if (xxlJob.destroy().trim().length() > 0) {
            try {
                destroyMethod = clazz.getDeclaredMethod(xxlJob.destroy());
                destroyMethod.setAccessible(true);
            } catch (NoSuchMethodException e) {
                throw new RuntimeException("xxl-job method-jobhandler destroyMethod invalid, for[" + clazz + "#" + methodName + "] .");
            }
        }

        //进行任务处理程序注册
        registJobHandler(name, new MethodJobHandler(bean, executeMethod, initMethod, destroyMethod));

    }

首先initJobHandlerMethodRepository方法的applicationContext参数就是通过ApplicationContextAware 获取的,下面我们来看下initJobHandlerMethodRepository方法的具体逻辑

1、从ApplicationContext中获取所有Bean元数据名称,通过Bean元数据名称获取所有Bean;
2、遍历获取到的Bean,找到有XxlJob注解的类,获取类中被注解的所有方法;
3、获取被注解方法的相应信息,根据注解中的任务名称,调用loadJobHandler(name)方法检查该任务是否已经注册;
4、进行方法编写范式检查,主要检查方法名称、入参类型以及返回值类型是否符合要求;
5、设置被注解方法的初始化方法和销毁方法;
6、最后,将上述被注解方法注册到任务处理程序仓库中;

  • 19
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
xxl-job-nacos 是一个基于微服务的调度系统。它的设计理念是通过将大型应用拆分成多个微服务,每个微服务都有一个独立的调度器,用于管理该微服务的任务调度和执行。xxl-job-nacos 使用 Nacos 作为注册中心,通过 Nacos 实现微服务的注册和发现。 通过将任务调度设置为微服务的一部分,xxl-job-nacos 可以实现任务的集中管理和分布式部署。它提供了一套友好的调度管理界面,用于配置和监控任务的运行情况。用户可以通过该界面添加、编辑和删除任务,指定任务的执行时间和频率。同时,xxl-job-nacos 支持任务的动态调度,可以实时修改任务的执行策略和参数,以适应不同的业务需求。 通过使用 Nacos 作为注册中心,xxl-job-nacos 实现了任务注册和发现。当一个新任务被添加到调度系统中时,它会通过 Nacos 将任务的信息注册注册中心,并通知相应的微服务。微服务通过订阅注册中心的信息,获知新任务的相关信息,并根据任务的调度策略和配置进行任务的执行。当任务执行完成后,微服务会将执行结果反馈给 xxl-job-nacos,并更新任务状态和日志。 总的来说,xxl-job-nacos 是一个基于微服务的调度系统,它通过将任务调度设置为微服务的一部分,实现了任务的集中管理和分布式部署。同时,xxl-job-nacos 使用 Nacos 作为注册中心,实现了任务注册和发现,以实时更新任务的信息和执行结果。通过这样的设计,xxl-job-nacos 可以提供强大的任务调度能力,适用于大型应用的任务调度和分布式执行。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值