`
xiaoliang330
  • 浏览: 116496 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

spring定时任务之cronJob

 
阅读更多
情景:
  数据库中的数据需要根据formula在一定时间上计算得到相应的结果数据,就是说,根据formula去计算,得到相应结果保存在相应字段上,这个job是定时触发的,计算按照一定的事件类型。


代码:
public class DashboardDataCalculationJob {
    private final static Logger log = LoggerFactory.getLogger(DashboardDataCalculationJob.class);

    protected String cronJobKey;

    private DashboardDataCalculationService dashboardDataCalculationService;

    public void setCronJobKey(final String cronJobKey) {
        this.cronJobKey = cronJobKey;
    }

    public void setDashboardDataCalculationService(
            final DashboardDataCalculationService dashboardDataCalculationService) {
        this.dashboardDataCalculationService = dashboardDataCalculationService;
    }

    public void execute() {

        long start = System.currentTimeMillis();

        Date date = new Date();

        log.info("DashboardDataCalculationJob executing Daily...");
        boolean success = dashboardDataCalculationService.calculate(cronJobKey, PeriodType.Daily,
                date);

        if (success) {
            log.info("DashboardDataCalculationJob executing Monthly...");
            success = dashboardDataCalculationService.calculate(cronJobKey, PeriodType.Monthly,
                    date);
        }

        if (success) {
            log.info("DashboardDataCalculationJob executing Yearly...");
            dashboardDataCalculationService.calculate(cronJobKey, PeriodType.Yearly, date);
        }

        log.info("DashboardDataCalculationJob took {} ms.", System.currentTimeMillis() - start);
    }
}





这个类负责调用正确的cronJob去执行定时任务。我们可以看到,传过来相应的cronJobKey,首先从daily开始计算,只要成功,就会触发一系列的计算

接下来,我们看看DashboardDataCalculationService这个接口.
@WebService
public interface DashboardDataCalculationService {

    public boolean calculate(String cronJobKey, PeriodType periodType, Date date);

    public boolean calculateFields(List<String> fieldList, PeriodType periodType, Date date);

    public Double doCalculate(String formula, Date calculateDate, PeriodType periodType);
}


这个接口中定义了三个calculate方法,用于计算,再看看它的实现类中
public boolean calculate(String cronJobKey, PeriodType periodType, Date date);方法

public boolean calculate(final String cronJobKey, final PeriodType periodType, final Date date) {

        try {
            init();

            Date calculateDate = JobUtil.getCalculateDate(periodType, date);

            SystemCronJob systemCronJob = systemCronJobService.getSystemCronJobByJobKey(cronJobKey);

            if (systemCronJob == null) {
                log.error("SystemCronJob not found by cronJobKey: {}", cronJobKey);
                String subject = "SystemCronJob not found by cronJobKey: " + cronJobKey;
                String content = subject;
                try {
                    emailService.sendEmail(subject, content);
                } catch (Exception e) {
                    log.error(e.getMessage(), e);
                }
                return false;
            }

            List<DashboardDataDefinition> defList = systemCronJob.getDashboardDataDefinitions();

            for (DashboardDataDefinition def : defList) {
                calculateDef(periodType, calculateDate, def);
            }

            return true;

        } catch (Exception ex) {
            log.error(ex.getMessage());
            String subject = "DashboardDataCalculationService Failed: " + ex.getMessage();
            StringWriter writer = new StringWriter();
            ex.printStackTrace(new PrintWriter(writer));
            String content = writer.toString();
            try {
                emailService.sendEmail(subject, content);
            } catch (Exception e) {
                log.error(e.getMessage(), e);
            }
            return false;
        } finally {
            release();
        }
    }



看下这个方法,init()方法初始化了ThreadLocal变量中的参数,
 private static final ThreadLocal<Map<String, Double>> cachedValue = new ThreadLocal<Map<String, Double>>();
    private static final ThreadLocal<Map<String, Boolean>> cachedValueSumLast = new ThreadLocal<Map<String, Boolean>>();



public void init() {
        cachedValue.set(new HashMap<String, Double>());
        cachedValueSumLast.set(new HashMap<String, Boolean>());
    }



接下来,systemCronJobService 负责去数据库拿到相应的SystemCronJob 对象,如果没拿到,则直跳出返回。
拿到SystemCronJob 对象后,通过它可以得到相应的需要计算的数据表对象,得到要计算的记录,分条执行calculateDef(periodType, calculateDate, def)。

  private void calculateDef(final PeriodType periodType, final Date calculateDate,
            final DashboardDataDefinition def) {
        log.info("ChartField: {}, FormulaText: {}", def.getChartField(), def.getFormulaText());

        String formula = def.getFormulaText();

        if (StringUtils.isBlank(formula)) {
            log.warn("'{}' FormulaText is blank", def.getChartField());
            return;
        }

        log.info("formula: {}={}", def.getChartField(), formula);
        Double value = doCalculate(formula, calculateDate, periodType);



在方法public boolean calculate(final String cronJobKey, final PeriodType periodType, final Date date) 调用 private void calculateDef(final PeriodType periodType, final Date calculateDate,
            final DashboardDataDefinition def)方法,在此方法中,最终会调用
 Double value = doCalculate(formula, calculateDate, periodType);


doCalculate()方法,所以,关键的计算在这里面。

    public Double doCalculate(final String formula, final Date calculateDate,
            final PeriodType periodType) {

        String cacheKey = getCacheKey(formula, calculateDate, periodType);

        if (cachedValue.get() == null) {
            init();
        }

        if (cachedValue.get().containsKey(cacheKey)) {
            Double cached = cachedValue.get().get(cacheKey);
            Boolean sumLastCal = cachedValueSumLast.get().get(cacheKey);
            if (sumLastCal != null && sumLastCal.booleanValue() == true) {
                FormulaMethod.sumLastCal.set(Boolean.TRUE);
            }
            log.info("use cached value {} :{}", formula, cached);
            return cached;
        }

        long start = System.currentTimeMillis();

        log.info("calculateDate: {}", calculateDate);
        log.info("calculateDate.time: {}", calculateDate.getTime());
        log.info("periodType: {}", periodType);

        ExpressionParser parser = new SpelExpressionParser();
        Expression exp = parser.parseExpression(formula);

        StandardEvaluationContext context = new StandardEvaluationContext();
        DashboardDataAccessor accessor = new DashboardDataAccessor(calculateDate, periodType);

        context.setRootObject(new FormulaMethod(calculateDate, periodType, accessor));

        List<PropertyAccessor> propertyAccessors = new ArrayList<PropertyAccessor>();
        propertyAccessors.add(accessor);
        context.setPropertyAccessors(propertyAccessors);

        Object value = exp.getValue(context);
        log.info("{} = {}", formula, value);

        log.info("took {} ms.", System.currentTimeMillis() - start);

        double v = Double.parseDouble(value.toString());
        cachedValue.get().put(cacheKey, v);
        cachedValueSumLast.get().put(cacheKey, FormulaMethod.sumLastCal.get());
        return v;
    }



这个方法中,首先把要计算的条件组成一个key,放在本地线程当中,其中有一个相应的计算结果,如果下次计算的条件与其相同,则不需要计算了,直接拿出结果就OK了,这样就节省了很多时间。

关键的计算是调用了spring的ExpressionParser及 Expression,让ExpressionParser去解析我们的formula,得到Expression 对象,最后有Expression对象调用它的getValue()方法得到计算结果。

当然,其中用到StandardEvaluationContext,PropertyAccessor,这些都是计算不可少的spring自带对象, DashboardDataAccessor以及FormulaMethod 都是自己编写的用于计算的条件对象,看名字应该知道它们是用来干什么的。

分享到:
评论

相关推荐

    k8s+springboot+cronjob 定时任务配置

    本篇文章将详细探讨如何在 Kubernetes (k8s) 集群环境中,结合 Spring Boot 应用程序和 Kubernetes CronJob 资源来配置定时任务。 首先,Spring Boot 是一个流行的 Java 框架,用于快速构建微服务应用。它提供了...

    spring定时任务实现

    Spring框架提供了多种方式来实现定时任务,这使得开发者可以在不同场景下选择最适合的方案。本文主要探讨了Spring中实现定时任务的三种主要方法:Java的`java.util.Timer`、Quartz库以及Spring自身的Task调度器。 ...

    Spring中定时任务

    结合Eclipse和Maven,创建Spring定时任务项目时,你需要: 1. 创建一个新的Maven项目,并添加Spring的相关依赖,如`spring-context`, `spring-context-support`(对于Spring Task),以及`quartz`和`spring-quartz`...

    spring之定时任务实现(spring-task和quartz等不同方式)

    Spring框架提供了一系列工具来支持定时任务的实现,其中包括Spring自带的`spring-task`模块和第三方的Quartz库。下面我们将详细探讨这两种方式以及如何通过注解和非注解的方式进行配置。 首先,`spring-task`是...

    spring动态配置定时任务

    在Java Spring框架中,动态配置定时任务是一项非常实用的功能,它允许我们根据需求灵活地更改或添加定时任务,而无需每次改动都重启应用。本文将深入探讨如何在Spring中实现这种动态配置,以及如何结合数据库来管理...

    quartz2 与spring4 集成 定时任务Job

    将Quartz2集成到Spring4中,可以方便地管理和执行定时任务,无需在代码中直接管理Scheduler实例,而是通过Spring配置来定义和控制Job。 集成Quartz2和Spring4的过程主要包括以下几个步骤: 1. **引入依赖**:首先...

    Spring+Quartz 从数据库中获取定时任务和定时时间,动态实现对定时任务的增删改查

    Spring框架结合Quartz库,提供了一种强大而灵活的方式来管理这些定时任务。本项目旨在演示如何利用Spring和Quartz从数据库中动态加载定时任务,并实现对它们的增删改查功能。以下是关于这个主题的详细知识讲解。 ...

    Spring定时任务(多个实例)

    在Spring框架中,定时任务是实现自动化操作的重要手段,它允许开发者在特定时间点执行特定的任务,例如数据清理、报表生成等。本篇文章将详细探讨Spring中的定时...希望这些知识对您在实现Spring定时任务时有所帮助。

    spring 定时任务 示例代码

    在Spring框架中,定时任务是实现周期性任务执行的关键特性,它允许开发者在特定时间间隔内执行特定的代码逻辑,而无需手动干预。本示例将深入探讨如何使用Spring的TaskScheduler或者Quartz Scheduler来创建和管理...

    Spring提供的三种定时任务机制及其比较

    在Spring框架中,有三种主要的定时任务机制可供开发者选择,分别是基于Quartz的定时机制、Spring自己的`@Scheduled`注解以及Java的`java.util.Timer`类。在这篇文章中,我们将重点讨论基于Quartz的定时机制,因为它...

    定时任务spring3.0

    Spring 3.0版本引入了对定时任务的支持,这是一个非常实用的功能,可以帮助开发者实现定期执行的任务,如数据同步、报表生成等。本文将深入探讨Spring 3.0中的定时任务实现,并结合Quartz库来进一步扩展定时任务的...

    spring定时任务之Quartz

    Spring定时任务是企业级应用程序中常见的一种需求,用于在特定时间执行特定的任务,例如数据备份、清理、报表生成等。Quartz是Java领域一个强大的、开源的作业调度框架,它允许开发者灵活地定义任务和调度策略。这篇...

    Spring整合Quartz后的简单定时任务示例

    本资源"Spring整合Quartz后的简单定时任务示例"提供了如何将这两者结合使用的实例,旨在帮助开发者实现基于Spring的定时任务功能。 首先,我们要理解Spring对定时任务的支持。Spring框架通过`@Scheduled`注解提供了...

    Spring定时任务的几种实现

    ### Spring定时任务的几种实现 #### 一、定时任务实现技术分类 在Spring框架中,实现定时任务的主要技术方案有三种: 1. **Java自带的`java.util.Timer`类**:这种实现方式允许用户调度一个`java.util.TimerTask`...

    Quartz+spring定时任务demo

    在 "Quartz+spring定时任务demo" 中,我们首先需要了解如何配置和集成 Quartz 与 Spring。这通常包括以下步骤: 1. **引入依赖**:在项目中添加 Quartz 和 Spring 相关的 jar 包。描述中提到的 "1128266338845696....

    web页面可配置话的动态生成spring定时任务,持续到化数据库

    本项目将Spring与Quartz相结合,实现了一个Web页面可配置的动态生成Spring定时任务的功能,并能持久化这些任务到数据库中,这在实际业务场景中非常实用。 首先,我们要理解Spring的定时任务是如何工作的。Spring...

    spring定时任务配置详解.doc

    在Spring框架中,定时任务是通过Spring的Task执行器或者Quartz库来实现的,用于在指定的时间间隔或特定时间点执行重复性任务。这里我们将深入讲解如何在Spring中配置和使用定时任务。 首先,Spring提供了两种定时...

    spring boot 定时任务源码

    在Spring Boot框架中,定时任务是一项非常重要的功能,它允许开发者在特定的时间间隔执行特定的任务。Spring Boot通过集成Spring Framework的Task Execution和Scheduling模块,提供了便捷的方式来管理和执行定时任务...

    Spring普通定时任务和动态设置定时任务

    Spring 定时任务主要依赖于 Quartz 的 Job 和 Trigger 两个核心组件。其中,Job 代表了需要执行的任务,而 Trigger 用于控制任务何时以及如何执行。 ##### 2.2 实现步骤 1. **定义 Job**:首先,需要定义一个具体...

    spring-timer-quartz.zip_namegnd_spring定时任务

    然而,如果需要更复杂的定时任务调度,如基于Cron表达式的定时,Spring推荐使用第三方的调度库,比如Quartz。 Quartz是一个功能强大的开源作业调度框架,它可以与Java应用程序无缝集成,用于执行计划任务。在Spring...

Global site tag (gtag.js) - Google Analytics