定时执行任务案例

时间:2022-08-08 07:55:41

需求:实现一个作业调度的服务,用来定时的执行任务(执行jar包和表达式)。
框架:quartz2.2.1,spring和hibernate。
设计:
1,在web.xml文件中配置一个对spring框架的监听,一旦服务启动完成则运行监听器,扫描数据库执行任务。(也可以写一个servlet类,配置在web.xml文件中,随服务一起启动。)注意配置时监听器的顺序,不要把定时器的监听写在了容器监听之前,这样就没有效果了

    <listener>
<listener-class>文件</listener-class>
</listener>

2,在监听器考虑到要上传jar,会在这里获取服务器的相对地址,但是这里用spring的注解标签不起作用,可以通过直接取bean的方法来获取地址和获取定时器调度服务。

public class xxxx implements ServletContextListener {

@Override//容器初始化调用方法
public void contextInitialized(ServletContextEvent sce) {
ServletContext servletContext=sce.getServletContext();
WebApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(servletContext);
jService=ctx.getBean(xxxxx);
String jobRootPath=servletContext.getRealPath("/");
if(jService.loadJob(jobRootPath)){
j.startSchedule();
}

}
//spring容器销毁时调用
@Override
public void contextDestroyed(ServletContextEvent sce) {

}

}

3,调度服务编写:
1),Scheduler,它是调度器,地层是一个多线程的封装,每开启一个任务的执行,就相当于会启用一个线程。
2),JobDetail,它代表的是一个具体任务,其中必须要包括任务名,组名。如果任务中想要有参数的传递可以将JobDataMap绑定到任务上,这个map会随任务一起传递。
3),Trigger,它是定时器,其中定时又分两种如果是简单的执行单次任务或是简单的重复任务可以使用SimpleScheduleBuilder。如果是复杂的定时任务可以使用CronScheduleBuilder。core表达式可以参照core表达式

public static Scheduler sched;
static {
try {
sched = (new StdSchedulerFactory()).getScheduler();
} catch (SchedulerException e) {
e.printStackTrace();
}
}
JobDetail job=null;
JobDataMap newJobDataMap = new JobDataMap();
newJobDataMap.put("TryTime",tryTime);
Class<xxx> clas = xxxx.class;
job = JobBuilder.newJob(clas).withIdentity(rid, "group1").setJobData(newJobDataMap).build();
Trigger trigger = null;
if(triggerTime==null || triggerTime.equals("")){
trigger = TriggerBuilder.newTrigger().withIdentity(rid, "group1")
.withSchedule(SimpleScheduleBuilder.simpleSchedule())
.startAt(starttime).build();
}else{
trigger = TriggerBuilder.newTrigger().withIdentity(rid, "group1")
.withSchedule(CronScheduleBuilder.cronSchedule(triggerTime))
.startAt(starttime).build();

}

此时任务和定时器都写完了,你也可以在这时加入对任务的监听器:
写一个类实现JobListener接口。

public class MyJobListener implements JobListener {

@Override // 相当于为我们的监听器命名
public String getName() {
return "myJobListener";
}

// 触发监听
@Override
public void jobToBeExecuted(JobExecutionContext context) {
// System.out.println(getName() +
// "触发对"+context.getJobDetail().getJobClass()+"的开始执行的监听工作,这里可以完成任务前的一些资源准备工作或日志记录");
}

@Override // “否决JobDetail”是在Triiger被其相应的监听器监听时才具备的能力
public void jobExecutionVetoed(JobExecutionContext context) {
// System.out.println("被否决执行了,可以做些日志记录。");

}


/**
* 任务执行完成后执行,jobException如果它不为空则说明任务在执行过程中出现了异常,
* 这里的即使任务在执行过程中出现异常也会进入到该方法,因为我们可能回在任务的执行类中
* 捕获异常,所以可以利用JobDataMap获取任务中的异常,当然这需要你在任务执行中将异常存到JobDataMap。
*/

@Override
public void jobWasExecuted(JobExecutionContext context,
JobExecutionException jobException)
{

}

}

这样将监听绑定到调度器:

        sched.getListenerManager().addJobListener(new MyJobListener());

将任务和定时器绑定到调度器:

sched.scheduleJob(job, trigger);

4,具体的任务执行器,这里你需要实现Job,在execute()方法中写你要执行的内容即可。
1),执行jar包的类。这里规定jar包中需要执行的类的必须带参数,参数可以写成xxx=xxx&xxx=xxx….,这样在加载类时不要需要关心你有多少个参数,你参数的类型了。

    public class xxx implements Job
{

static class MyClassLoader extends URLClassLoader
{

public MyClassLoader(URL[] urls, ClassLoader parent) {
super(urls, parent);
}

public void addJar(URL url) {
this.addURL(url);
}
}

static Class<?> clazz=null;

@Override
public void execute(JobExecutionContext context){
// System.out.println("******执行器********");
//获取要执行任务的参数信息
JobDataMap jdm = context.getJobDetail().getJobDataMap();
String path = jdm.getString(xxx);//执行类的jar包路径
String className = jdm.getString("pakgeandclassname");//执行类的全名
String methodname = jdm.getString(xxxx);//要执行的方法
String params = jdm.getString(xxx);//要传递给执行方法的参数(参数格式由执行方法自己处理)
//执行方法原型规定为:void method(String param)
//所以需要一个调用参数
Class<?>[] typeA = new Class<?>[1];
typeA[0] = String.class;
Object[] sparams = new Object[1];
sparams[0] = params;
URL[] urls = new URL[] {};
MyClassLoader classLoader = null;
try {
//动态加载类
if(clazz==null){
classLoader = new MyClassLoader(urls, ClassLoader.getSystemClassLoader());
classLoader.addJar(new File(path).toURI().toURL());
clazz = classLoader.loadClass(className);
}
Method method = null;
method = clazz.getDeclaredMethod(methodname, typeA);

if (Modifier.isStatic(method.getModifiers()))
method.invoke(null, sparams);
else {
Object inst = clazz.newInstance();
method.invoke(inst, sparams);
}
} catch (Exception e) {
e.printStackTrace();
jdm.put("exception", e.getLocalizedMessage());
}
finally{
try{
if(classLoader!=null)
classLoader.close();
}catch (Exception e){
e.printStackTrace();
//jdm.put("exception", "occur");
}
}

}

}

执行表达式的类:

    public class xxxx implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException
{
System.out.println("执行了表达式任务。。。");
JobDataMap jdm = context.getJobDetail().getJobDataMap();
ScriptExchangeObject obj = new ScriptExchangeObject("你想要加载的自定义脚本环境");
ScriptEngineManager scriptEngineManager = new ScriptEngineManager();
ScriptEngine nashorn = scriptEngineManager.getEngineByName("nashorn");
SimpleBindings bindings = new SimpleBindings();
bindings.put("$", obj);
String sts = jdm.getString("CodeContext");//执行内容
nashorn.eval(sts,bindings);//将执行内容和环境绑定
}