定時執行任務案例
需求:實現一個作業排程的服務,用來定時的執行任務(執行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表示式可以參照
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);//將執行內容和環境繫結
}