`
kevin-qingzhan
  • 浏览: 83944 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

一个JAVA后台程序的设计方案

阅读更多
一个JAVA后台程序的设计方案     选择自 glchengang 的 Blog  
关键字   java.util.Timer 定时器 后台
   


       很多系统都需要一个在后台不间断运行的程序,以定期执行某些系统任务。这类似于Windows中自带的计划任务的功能。我一年半前我参与某省联通的网管项目的开发,曾经写过一个这样的后台程序,它要不间隔的从各种类型服务器上,下载各种类型的数据文件(每个文件都有几兆大小),并将这些文件解读成一条条记录插入到数据库中。这个后台程序直接使用java中的线程,由于线程的复杂性,调试也困难,很不幸这个后台程序很不稳定,每周都会有一两次会停在那里不再往下执行,原因至今天未找到,成为我心中永远的痛。

       时隔今日,再次有幸参与IBM一个开发项目,这个项目同样需要一个类似的后台运行程序,这个程序的任务是:每隔一天检查数据库中的数据,并对符合某些条件记录进行某操作。任务很简单,为了今后扩展方便,我将这个设计成了一个多任务可管理的后台程序。周未我设置了两个任务同时执行,一任务每10秒执行一次,另一任务每1秒执行一行,运行了两天,运行较果良好。估计很多朋友会面临与我同样的问题,在此将程序思路和代码公布,希望有兴趣的朋友大家一起研究探讨。

一、程序运行界面:

1、总控制台



2、计划任务的设置界面。



3、控制台输出结果。


二、程序开发环境:
       使用Java(JDK 1.4)开发,图形界面使用Eclipse (2.1.3版)的SWT方式开发。运行主机:P4 2.6+1G内存 windowsXP操作系统

三、预备。
       开发此类程序,最好不要直接使用JAVA的线程来编程,这样会增加不必要的复杂度和难度,吃力不讨好。在JAVA中有一个包 java.util.Timer 这个包封装了对线程的操作,我们可以把它称做定时器类。我们先来看一个简单例子:
import java.util.Timer;
import java.util.TimerTask;
public class Reminder {
     Timer timer;
     public Reminder(int seconds) {
         timer = new Timer();
         timer.schedule(new RemindTask(),seconds*1000); //参数要求转化成毫秒
     }
     public static void main(String args[]) {
         new Reminder(5); //5秒后运行
     }
     /**一个内部类,封装了所要运行的任务*/
     class RemindTask extends TimerTask {
         public void run() {
             System.out.println("任务运行。。。。");
             timer.cancel(); //结束timer中的所有任务
         }
     }
}

  这里涉及于两个JAVA类Timer和TimerTask。我们继承TimerTask类后,将所要运行的任务封装其run方法中;Timer可以管理几千个任务(TimerTask),注意,同一个任务对象不能两次加入到Timer中执行。
对(虽然执行的任务都一样,但是两个任务对象):
timer.schedule(new RemindTask(), seconds * 1000);
timer.schedule(new RemindTask(), seconds * 1000);


RemindTask task= new RemindTask();
timer.schedule(task, seconds * 1000);
timer.schedule(task, seconds * 2000);

四、设计方案。

主要的类图


说明:
  任务类的设计。我们先创建一个抽象类AbstractTimerTask,这个类直接继承至TimerTask类,提供对TimerTask封装。然后所有具体的任务类(如:TimerTask_1)继承自AbstractTimerTask。
import java.util.TimerTask;
public abstract class AbstractTimerTask extends TimerTask {
     TaskEntry taskEntry; //任务记录
     public AbstractTimerTask(TaskEntry taskEntry) {
         this.taskEntry = taskEntry;
     }
     /*
     * 生成一个新的实例相当于克隆自身;原因在于:同一任务对象不能两次加入到Timer
     * 在TaskEntry类将看到它的使用方法
     */
     abstract AbstractTimerTask getCloneObject();
}


下面是它的一个实现类的源代码我们可以将要运行任务的代码写在这个类中。
import java.util.Calendar;
public class TimerTask_1 extends AbstractTimerTask {
     public TimerTask_1(TaskEntry taskEntry) { //构造方法
         super(taskEntry);
     }
     public AbstractTimerTask getCloneObject() {  
         return new TimerTask_1(taskEntry);
     }
     public void run() {
         /*在这里写你要执行的程序。。。。。*/
         System.out.println("??时:"+taskEntry.getName()+"运行了一次");
         this.taskEntry.taskStart(); //运行下一个时间点任务
     }
}



在AbstractTimerTask类有一个TaskEntry字段,这是本设计的一个核心类,它代表一条封装完整的任务记录,每个任务类和它的运行计划都封装在这条类中,源代码如下。Timer和AbstractTimerTask前面都已说过,那么TimePlan是做什么用的呢?
import java.util.Calendar;
import java.util.Date;
import java.util.Timer;
import mytimer.util.Util;
/**任务记录类*/
public class TaskEntry {
     public static final int TASK_START = 0; //定义两个表示任务记录状态常量
     public static final int TASK_STOP = 1;
     private Long oid; //任务ID号,唯一
     private String name; //任务名称
     private int state = TASK_STOP; //任务状态(启动/停止)
     private Timer timer; //JAVA计时器
     private TimePlan timePlan; //时间计划的类型
     private AbstractTimerTask timerTask; //任务类的种子对象,由这个对象来不断克隆
     private AbstractTimerTask runTimerTask; //运行计划的当前任务
     /**
       * ITaskEntry.taskStart()-->TimerTask.run()-->ITaskEntry.taskStart()
       * 形成一个循环回路。本方法负责起动本类代表的任务
       */
     public void taskStart() {
         if (timePlan.haveNext()) {
             Date date = timePlan.nextDate();//得到任务计划时间
             runTimerTask = timerTask.getCloneObject();//得到任务(复制)
             timer.schedule(runTimerTask, date); //加入计划队列
             //打印将要运行的计划任务的信息
             Calendar c = Calendar.getInstance();
             c.setTimeInMillis(runTimerTask.scheduledExecutionTime());
             System.out.println(Util.dateToLongStr(c.getTime())+"将运行"+name);
         } else {
             state = TASK_STOP;
             System.out.println(name + "结束");
         }
     }
     /**停止任务*/
     public void taskStop() {
         if (runTimerTask != null) {
             //打印信息
             Calendar c = Calendar.getInstance();
             c.setTimeInMillis(runTimerTask.scheduledExecutionTime());
             System.out.println("计划于:"+Util.dateToLongStr(c.getTime())+"运行的" + name + "被终止");
             //终止本任务, 调用Timer.cancel()是终止Timer的所有任务。
             runTimerTask.cancel();
         } else {
             System.out.println(name + "未进入执行计划");
         }
     }
 ……… 一些属性的get/set方法(省略)
     /** 监听类(内部类) */
     public static class DateBeforeTodayException extends NullPointerException {
         private Date date;
         public DateBeforeTodayException(Date date) {this.date = date;}
         public String toString() {
             return "计划时间(" + Util.dateToLongStr(date) + ")早于当前时间";
         }
     }
}



1、TimePlan是一个接口(interface),它是表示“运行计划的方案”,这个程序中提供了三种运行计划方案(见前图:计划任务的设置界面):
一次性运行。
每隔一个时间段运行。
一周中选择那几天运行。 
将它设计成一个接口是为了方便今后扩展,如果要新增新的时间方案只需要继承这个接口写一个新的实现即可。三种时间方案的类图如下:


说明:
a) TimePlan封装了五个方法,其它haveNext()和nextDate()最重要,这个两个方法模仿了Java中集合类(Collection)的迭代器(Iterator)的设计形式,代码如下:
import java.util.Date;
//时间计划方案的接口
public interface TimePlan {
boolean haveNext();//判断还有没有下一个计划时间
     Date nextDate();//得到下一个计划时间
     Date getCurrentDate();//得到开始时间
     void setCurrentDate(Date date); //设计开始时间
     String getTimePlanString();//显示运行计划方案的文字说明
}

b) AbstractTimePlan是这个抽象类,主要目的是将一些各子类的公共方法写在这里。代码如下:
import java.util.Date;
public abstract class AbstractTimePlan implements TimePlan {
     //记录计划的第一时间点,除设置新的起始时间,否则不再改变
   protected Date currentDate;
/*
当前计划的时间点,每次计划替换时被更新,
似乎这个才应叫cureentDate,sorry不想再改了
*/
   protected Date planDate;

   public boolean haveNext() {
       return (planDate != null);
   }
   public Date getCurrentDate() {
       return currentDate;
   }
   public void setCurrentDate(Date date) {
       currentDate = date;
       planDate = date; //在赋给currentDate值时,同时也赋给planDate
   }
}

c) 然后我们看看三种计划方案的实现类的源代码:
//“一次性运行”的计划方案类
import java.util.Date;
public class TimePlanOnce extends AbstractTimePlan {
     public Date nextDate() {
//把要当前的计划时间保存在中间变量中
         Date returnDate = this.planDate;
//算出下一个计划时间。没有下一个就设为null
         this.planDate = null;
    //判断一下计划时间合不合条件
         if (returnDate == null)
             throw new NullPointerException("没有下一个计划日期");
         return returnDate;
     }

     public String getTimePlanString() {
         return "一次性运行,运行时间: (打印this.currentDate) ";
     }
}

//“周期性间隔”的时间计划方案类
import java.util.Date;
public class TimePlanPeriod extends AbstractTimePlan {
public static final int HOUR = 0;
public static final int DAY = 1;

private int spaceTime; //间隔时间,单位毫秒
private int timeType;

public Date nextDate() {
//把要当前的计划时间保存在中间变量中
     Date returnDate = this.planDate;
//算出下一个计划时间。没有下一个就设为null
     int milliSecond = 0;
     if (timeType ==HOUR) milliSecond = spaceTime * 1000; //小时*60*60*1000;
     if (timeType ==DAY) milliSecond = spaceTime * 24 * 60 * 60 * 1000; //天
     planDate = Util.DateAddSpaceMilliSecond(planDate, milliSecond);
     //判断一下计划时间合不合条件
     if (returnDate == null)
         throw new NullPointerException("没有下一个计划日期");
     return returnDate;
}
public String getTimePlanString() {
     if (timeType == HOUR)
         return "第一次运行于:currentDate.并每隔spaceTime小时运行一次";
     if (timeType == DAY)
         return "第一次运行于:currentDate.并每隔spaceTime天运行一次";
     return "";
}

public int getSpaceTime() {     return spaceTime; }
public int getTimeType() { return timeType;   }
public void setSpaceTime(int i) { spaceTime = i; }
public void setTimeType(int i) {   timeType = i; }
}


/**选择一周的某几天,让这几天在同一时间点运行任务, 一周内必须选择一天*/
import java.util.Calendar;
import java.util.Date;
public class TimePlanSelectWeek extends AbstractTimePlan {
   private static Calendar c = Calendar.getInstance(); //取得一个日历实例
   private static int spaceMilliSecond = 0; //间隔时间,单位毫秒
   private boolean[] selectWeek = new boolean[7]; //0为星期日 ,1为星期一

   public Date nextDate() {
       Date returnDate = null;
if (!isSelectWeek(planDate)) //如果这一天不是所选周中的一天
           planDate = getNextDate(planDate);
       returnDate = planDate;
       planDate = getNextDate(planDate);
//判断一下计划时间合不合条件
       if (returnDate == null)
           throw new NullPointerException("没有下一个计划日期");
       return returnDate;
   }
//算出下一个计划时间。没有下一个就设为null
   private Date getNextDate(Date date) {
       Date tempDate = date;
       Date returnDate = null;
       for (int i = 0; i < 7; i++) {
           tempDate = Util.DateAddSpaceMilliSecond(tempDate, spaceMilliSecond);
           if (isSelectWeek(tempDate)) {
               returnDate = tempDate;
               break;
           }
       }
       return returnDate;
   }

   /**设置某星期是否被选, 0为星期日 ,1为星期一....6为星期六*/
   public void setSelectWeek(int i, boolean b) {selectWeek[i] = b;}
   /** 判断某星期是否被选*/
   public boolean isSelectWeek(int i) {return selectWeek[i];}
   /**判断某天所属星期几是否被选*/
   public boolean isSelectWeek(Date date) {
       if (date == null) return false;
       c.setTime(date);
       //Calendar.DAY_OF_WEEK:星期日=1,星期六=7 c.get(Calendar.DAY_OF_WEEK)
       return isSelectWeek(c.get(Calendar.DAY_OF_WEEK) - 1);
   }
   public String getTimePlanString() {
       StringBuffer sb = new StringBuffer("");
       if (selectWeek[1]) sb.append("周一,");
       if (selectWeek[2]) sb.append("周二,");
       if (selectWeek[3]) sb.append("周三,");
       if (selectWeek[4]) sb.append("周四,");
       if (selectWeek[5]) sb.append("周五,");
       if (selectWeek[6]) sb.append("周六,");
       if (selectWeek[0]) sb.append("周日,");
       return "每周的"+sb.toString()+"运行";
   }
}



TimerTask的工厂类。将生成TimerTask的代码另起一个类的好处是代码的层次比较清楚,也比较好管理。由于TimerTask包含有几个字段,因此产生一个TimerTask对象还是有一定的复杂度,建立一个专门生成TimerTask的工厂类,这样我们在生成一个TimerTask对象时就可以少掉很多麻烦的代码了。当然由于我的工作任务,只需要一个TimerTask对象就够了,所以最初之前我是将它直接写在图形界面的代码里的。

  这里建立一个TimerTask对象池tasks,它是一个静态变量,这样在getInstance时不必总是要新生成一个TimerTask。还有Timer也是一个静态变量,它是一个全局单例(是最简单的单例模式了),因为前面说了Timer可以管理几千个任务,所以Timer对象一个就够了。
import java.util.HashMap;
import java.util.Timer;
public class TaskEntryFactory {
     private static final HashMap tasks = new HashMap();
     private static final Timer timer = new Timer();
     public static TaskEntry getInstance(Long oid, String name) {
         if (tasks.containsKey(oid)) {
             return (TaskEntry) tasks.get(oid);
         } else {
             TaskEntry entry = new TaskEntry();
             entry.setOid(oid);
             entry.setName(name);
             entry.setTimer(timer);
             entry.setTimerTask(new TimerTask_1(entry));
             tasks.put(oid, entry);
             return entry;
         }
     }
}



起动和停止任务,当“任务设置界面(TaskListDialog.java)”点击OK后处理。界面的编写就不在本文讨论的范围内了。
//任务设置界面中点击”确认(OK)”按钮后的处理
if (dialog.open() == Window.OK) {
     if (taskEntry.getState() == TaskEntry.TASK_START) {
         taskEntry.taskStop();//将旧的停掉
         taskEntry.taskStart();//开始新设置的
     }
if (taskEntry.getState() == TaskEntry.TASK_STOP)
         taskEntry.taskStop();
     tv.refresh(taskEntry);
}
分享到:
评论
3 楼 十井_ 2012-07-24  
最恨不负责任的转载别人的文章,也不给个来源链接!!!图呢 你倒是转过来啊
2 楼 cectsky 2011-02-17  
因为他没接触过
1 楼 seanwon 2009-04-01  
有quartz这么好的框架,为什么不用呢?

相关推荐

    一个java后台程序的解决方案设计(真实项目)

    一个java后台程序的解决方案设计(真实项目)

    基于Java的校园二手物品交易市场小程序后台管理端设计源码

    本项目是基于Java的校园二手物品交易市场小程序后台管理端设计源码,包含270个文件,其中83个Java文件。该系统是一个微信小程序的后台管理系统,采用SpringBoot、Mybatis、Shiro、Maven和MySQL构建后端架构,前端则...

    《JavaWeb程序设计》整体设计.doc

    (一)课程性质 本课程是软件技术专业的主干课程之一,开设于第二学年第一学期,它的前导课程 有《计算机基础》,《Java语言程序设计》,《Java语言高级程序设计》等,后继课程有《框架 技术》。在专业的人才培养...

    《Java-Web程序设计》整体设计.doc

    (一)课程性质 本课程是软件技术专业的主干课程之一,开设于第二学年第一学期,它的前导课程 有《计算机基础》,《Java语言程序设计》,《Java语言高级程序设计》等,后继课程有《框架 技术》。在专业的人才培养...

    前后端分离的个人博客后台管理系统+前台页面 Java+Vue实现

    # 功能简介 - **Docker一键部署**:支持 ...- **完善的 SEO 方案**:自带robots、sitemap 等 seo 模板,实现自动生成 robots 和 sitemap - **实时通讯**:管理员可向在线的用户实时发送消息 - **系统配置支持快速配置*

    基于Java的智能客服系统设计与实现

    Java 的一个智能客服系统,使用 Spring 框架,MySQL 数据库、ActiveMq 消息中间件、Redis 缓存、Elasticsearch 全文搜索等服务,一个智能客服系统包含的开发任务有:主要包括前端的页面开发,后台设计,中间客服与...

    基于JAVA的模拟ATM系统的设计与实现【文献综述】.pdf

    Java 语言由理解和信奉网络计算梦想的一个小巧而专注的开发组设计的,虽然该语言 最初的实施方案有点缺陷,但为了这个梦想,设计者们很少在技术上妥协,结果诞生了一 种专为以相互通信为主要目的的设备而设计的语言...

    基于Java的创创猫多商户电商平台设计源码

    系统采用了Vue、JavaScript和CSS等技术,为用户提供了一个功能丰富的电商解决方案。系统设计注重用户体验和响应速度,采用了模块化的开发方式,便于后续的扩展和维护。创创猫B2B2C多商户电商系统是一款完善且经过...

    基于SSM的食堂订餐小程序(微信小程序+后台管理系统)

    运用了B/S架构,JAVA的SSM框架,前后端分离等设计模式,使用了mysql管理数据库,微信开发者工具开发小程序前端,eclipse开发系统后端,采用了前后端交互技术构建了一个管理员、商家和用户三端共同使用的系统,给同学...

    基于java电脑彩票销售管理系统的设计软件程序源码+WORD毕业论文文档.zip

    系统分析是程序开发中的一个非常重要的环节,为了能够使设计程序更好、更充分的被展现出来,必须在事先进行调查研究。在基础的调查的同时,还要对新系统的功能进行详细的解析分析,这样才能够研究开发出更加完整的...

    本科毕业设计项目-Java电影推荐系统-管理后台.zip

    为了帮助大家更好地完成毕业设计,我们特地整理了一系列Java毕业设计项目参考资源,包括源代码、MD文档、笔记等等,希望能对您的学习与研究提供有力支持。 项目源代码:涵盖了多个Java毕业设计项目的完整代码,包括...

    Java毕业设计-java论坛管理系统设计(源代码+论文).rar

    本资源提供了一个基于Java的论坛管理系统的完整设计与实现方案。它包含了从需求分析、系统设计到编码实现的全部过程,以及一份详细的毕业论文文档,全面阐述了项目的背景、目标、技术路线和实现细节。 系统采用主流...

    JAVA五子棋手机网络对战游戏的设计与实现(源代码+论文).zip

    根据设计目标,结合普通网络游戏的运行流程,给出了系统总体设计方案,并探讨了系统设计时需要用到的关键技术。 2、 手机MIDP客户端的实现。 MIDP客户端是游戏的唯一客户端,主要功能包括连接服务器进行身份验证、...

    信息系统总体设计方案方案.docx

    信息系统总体设计方案方案 目 录 第一章 前言 6 1.1 设计思想 6 1.2 几个术语 6 第二章 总体目标与设计原则 8 2.1 总体目标 8 2.2 设计原则 8 第三章 需求分析及功能设计 10 3.1 子系统划分 10 3.1.1 质量管理子系统...

    基于JAVA的学生成绩管理系统的设计与实现.doc

    系统的需求分析 系统的可行性分析和需求分析应在弄清用户需求的基础上,提出可供选择的方案,进 行调查研究、信息流程分析和数据收集,对开发中出现的各种问题进行估计,便于在开 发中能正确处理。 1. 可行性分析 ...

    基于Vue的电影在线预订与管理系统-后台java代码(ssm)(毕业设计).zip

    为了帮助大家更好地完成毕业设计,我们特地整理了一系列Java毕业设计项目参考资源,包括源代码、MD文档、笔记等等,希望能对您的学习与研究提供有力支持。 项目源代码:涵盖了多个Java毕业设计项目的完整代码,包括...

    Java基于人脸识别的学生宿舍门禁系统的设计与开发

    阅读建议:此资源以开发基于人脸识别的学生宿舍门禁系统学习其原理和内核,不仅是代码编写实现也更注重内容上的需求分析和方案设计,所以在学习的过程要结合这些内容一起来实践,并调试对应的代码。

    基于SpringBoot的医疗管理系统(Java毕业设计)

    【技术说明】后台Java的SSM框架 【运行环境】IDEA + JDK1.8 +数据库MySQL8.0 或者 MySQL5.0 + Tomcat 【功能描述】系统管理+预约管理+病史管理+住院信息管理+管理员管理 等十余个模块 【项目优点】医疗管理系统主要...

    weixin066家政服务系统微信小程序设计+ssm后台毕业源码案例设计

    通过分析微信小程序家政项目小程序管理的不足,创建了一个计算机管理微信小程序家政项目小程序的方案。文章介绍了微信小程序家政项目小程序的系统分析部分,包括可行性分析等,系统设计部分主要介绍了系统功能设计和...

    微信小程序基于JAVA微信点餐小程序设计+ssm.zip

    微信小程序“基于JAVA微信点餐小程序设计Ssm.zip”是一个基于Java语言和Spring、Spring MVC、MyBatis(SSM)框架开发的微信小程序,专为餐饮业提供的一套完整的在线点餐解决方案。该项目结合了微信小程序的便利性和...

Global site tag (gtag.js) - Google Analytics