当前位置: 代码迷 >> Web前端 >> quartz的应用(二)
  详细解决方案

quartz的应用(二)

热度:450   发布时间:2012-09-11 10:49:03.0
quartz的使用(二)


1.   MyMethodInvokingBean

package cn.xue.Scheduler.service;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.quartz.Job;
import org.quartz.JobDetail;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.Scheduler;
import org.quartz.StatefulJob;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.util.MethodInvoker;

public class MyMethodInvokingBean implements FactoryBean, BeanNameAware, InitializingBean
{
	private Log logger = LogFactory.getLog(getClass());

	/**
	 * The JobDetail produced by the <code>afterPropertiesSet</code> method of this Class will be assigned to the Group specified by this property.  Default: Scheduler.DEFAULT_GROUP 
	 * @see #afterPropertiesSet()
	 * @see Scheduler#DEFAULT_GROUP
	 */
	private String group = Scheduler.DEFAULT_GROUP;

	/**
	 * Indicates whether or not the Bean Method should be invoked by more than one Scheduler at the specified time (like when deployed to a cluster, and/or when there are multiple Spring ApplicationContexts in a single JVM<i> - Tomcat 5.5 creates 2 or more instances of the DispatcherServlet (a pool), which in turn creates a separate Spring ApplicationContext for each instance of the servlet</i>) 
	 * <p>
	 * Used by <code>afterPropertiesSet</code> to set the JobDetail.jobClass to MethodInvokingJob.class or StatefulMethodInvokingJob.class when true or false, respectively.  Default: true 
	 * @see #afterPropertiesSet()
	 */
	private boolean concurrent = true;
	
	/** Used to set the JobDetail.durable property.  Default: false
	 * <p>Durability - if a job is non-durable, it is automatically deleted from the scheduler once there are no longer any active triggers associated with it.
	 * @see <a href="http://www.opensymphony.com/quartz/wikidocs/TutorialLesson3.html">http://www.opensymphony.com/quartz/wikidocs/TutorialLesson3.html</a> 
	 * @see #afterPropertiesSet() 
	 */
	private boolean durable = false;
	
	/**
	 * Used by <code>afterPropertiesSet</code> to set the JobDetail.volatile property.  Default: false
	 * <p>Volatility - if a job is volatile, it is not persisted between re-starts of the Quartz scheduler.
	 * <p>I set the default to false to be the same as the default for a Quartz Trigger.  An exception is thrown 
	 * when the Trigger is non-volatile and the Job is volatile.  If you want volatility, then you must set this property, and the Trigger's volatility property, to true.
	 * @see <a href="http://www.opensymphony.com/quartz/wikidocs/TutorialLesson3.html">http://www.opensymphony.com/quartz/wikidocs/TutorialLesson3.html</a>
	 * @see #afterPropertiesSet() 
	 */
	private boolean volatility = false;
	
	/** 
	 * Used by <code>afterPropertiesSet</code> to set the JobDetail.requestsRecovery property.  Default: false<BR>
	 * <p>RequestsRecovery - if a job "requests recovery", and it is executing during the time of a 'hard shutdown' of the scheduler (i.e. the process it is running within crashes, or the machine is shut off), then it is re-executed when the scheduler is started again. In this case, the JobExecutionContext.isRecovering() method will return true. 
	 * @see <a href="http://www.opensymphony.com/quartz/wikidocs/TutorialLesson3.html">http://www.opensymphony.com/quartz/wikidocs/TutorialLesson3.html</a> 
	 * @see #afterPropertiesSet() 
	 */
	private boolean shouldRecover = false;
	
	/**
	 * A list of names of JobListeners to associate with the JobDetail object created by this FactoryBean.
	 *
	 * @see #afterPropertiesSet() 
	 **/
	private String[] jobListenerNames;
	
	/** The name assigned to this bean in the Spring ApplicationContext.
	 * Used by <code>afterPropertiesSet</code> to set the JobDetail.name property.
	 * @see afterPropertiesSet()
	 * @see JobDetail#setName(String)
	 **/
	private String beanName;
	
	/**
	 * The JobDetail produced by the <code>afterPropertiesSet</code> method, and returned by the <code>getObject</code> method of the Spring FactoryBean interface.
	 * @see #afterPropertiesSet()
	 * @see #getObject()
	 * @see FactoryBean
	 **/
	private JobDetail jobDetail;
	
	/**
	 * The name of the Class to invoke.
	 **/
	private String targetClass;

	/**
	 * The Object to invoke.
	 * <p>
	 * {@link #targetClass} or targetObject must be set, but not both.
	 * <p>
	 * This object must be Serializable when {@link #concurrent} is set to false.
	 */
	private Object targetObject;
	
	/**
	 * The instance method to invoke on the Class or Object identified by the targetClass or targetObject property, respectfully.
	 * <p>
	 * targetMethod or {@link #staticMethod} should be set, but not both. 
	 **/
	private String targetMethod;
	
	/**
	 * The static method to invoke on the Class or Object identified by the targetClass or targetObject property, respectfully.
	 * <p>
	 * {@link #targetMethod} or staticMethod should be set, but not both. 
	 */
	private String staticMethod;

	/**
	 * Method arguments provided to the {@link #targetMethod} or {@link #staticMethod} specified.
	 * <p>
	 * All arguments must be Serializable when {@link #concurrent} is set to false.
	 * <p>
	 * I strongly urge you not to provide arguments until Quartz 1.6.1 has been released if you are using a JDBCJobStore with
	 * Microsoft SQL Server. There is a bug in version 1.6.0 that prevents Quartz from Serializing the Objects in the JobDataMap
	 * to the database.  The workaround is to set the property "org.opensymphony.quaryz.useProperties = true" in your quartz.properties file,
	 * which tells Quartz not to serialize Objects in the JobDataMap, but to instead expect all String compliant values.
	 */
	private Object[] arguments;

	/**
	 * Get the targetClass property.
	 * @see #targetClass
	 * @return targetClass
	 */
	public String getTargetClass()
	{
		return targetClass;
	}

	/**
	 * Set the targetClass property.
	 * @see #targetClass
	 */
	public void setTargetClass(String targetClass)
	{
		this.targetClass = targetClass;
	}

	/**
	 * Get the targetMethod property.
	 * @see #targetMethod
	 * @return targetMethod
	 */
	public String getTargetMethod()
	{
		return targetMethod;
	}

	/**
	 * Set the targetMethod property.
	 * @see #targetMethod
	 */
	public void setTargetMethod(String targetMethod)
	{
		this.targetMethod = targetMethod;
	}

	/**
	 * @return jobDetail - The JobDetail that is created by the afterPropertiesSet method of this FactoryBean
	 * @see #jobDetail
	 * @see #afterPropertiesSet()
	 * @see FactoryBean#getObject()
	 */
	public Object getObject() throws Exception
	{
		return jobDetail;
	}

	/**
	 * @return JobDetail.class
	 * @see FactoryBean#getObjectType()
	 */
	public Class getObjectType()
	{
		return JobDetail.class;
	}

	/**
	 * @return true
	 * @see FactoryBean#isSingleton()
	 */
	public boolean isSingleton()
	{
		return true;
	}

	/**
	 * Set the beanName property.
	 * @see #beanName
	 * @see BeanNameAware#setBeanName(String)
	 */
	public void setBeanName(String beanName)
	{
		this.beanName = beanName;
	}

	/**
	 * Invoked by the Spring container after all properties have been set.
	 * <p>
	 * Sets the <code>jobDetail</code> property to a new instance of JobDetail
	 * <ul>
	 * <li>jobDetail.name is set to <code>beanName</code><br>
	 * <li>jobDetail.group is set to <code>group</code><br>
	 * <li>jobDetail.jobClass is set to MethodInvokingJob.class or StatefulMethodInvokingJob.class depending on whether the <code>concurrent</code> property is set to true or false, respectively.<br>
	 * <li>jobDetail.durability is set to <code>durable</code>
	 * <li>jobDetail.volatility is set to <code>volatility</code>
	 * <li>jobDetail.requestsRecovery is set to <code>shouldRecover</code>
	 * <li>jobDetail.jobDataMap["targetClass"] is set to <code>targetClass</code>
	 * <li>jobDetail.jobDataMap["targetMethod"] is set to <code>targetMethod</code>
	 * <li>Each JobListener name in <code>jobListenerNames</code> is added to the <code>jobDetail</code> object.
	 * </ul>
	 * <p>
	 * Logging occurs at the DEBUG and INFO levels; 4 lines at the DEBUG level, and 1 line at the INFO level.
	 * <ul>
	 * <li>DEBUG: start
	 * <li>DEBUG: Creating JobDetail <code>{beanName}</code>
	 * <li>DEBUG: Registering JobListener names with JobDetail object <code>{beanName}</code>
	 * <li>INFO: Created JobDetail: <code>{jobDetail}</code>; targetClass: <code>{targetClass}</code>; targetMethod: <code>{targetMethod}</code>;
	 * <li>DEBUG: end
	 * </ul>
	 * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
	 * @see JobDetail
	 * @see #jobDetail
	 * @see #beanName
	 * @see #group
	 * @see MethodInvokingJob
	 * @see StatefulMethodInvokingJob
	 * @see #durable
	 * @see #volatility
	 * @see #shouldRecover
	 * @see #targetClass
	 * @see #targetMethod
	 * @see #jobListenerNames 
	 */
	public void afterPropertiesSet() throws Exception
	{
		try
		{
			logger.debug("start");
			
			logger.debug("Creating JobDetail "+beanName);
			jobDetail = new JobDetail();
			jobDetail.setName(beanName);
			jobDetail.setGroup(group);
			jobDetail.setJobClass(concurrent ? MethodInvokingJob.class : StatefulMethodInvokingJob.class);
			jobDetail.setDurability(durable);
			jobDetail.setVolatility(volatility);
			jobDetail.setRequestsRecovery(shouldRecover);
			if(targetClass!=null)
				jobDetail.getJobDataMap().put("targetClass", targetClass);
			if(targetObject!=null)
				jobDetail.getJobDataMap().put("targetObject", targetObject);
			if(targetMethod!=null)
				jobDetail.getJobDataMap().put("targetMethod", targetMethod);
			if(staticMethod!=null)
				jobDetail.getJobDataMap().put("staticMethod", staticMethod);
			if(arguments!=null)
				jobDetail.getJobDataMap().put("arguments", arguments);
			logger.debug("Registering JobListener names with JobDetail object "+beanName);
			if (this.jobListenerNames != null) {
				for (int i = 0; i < this.jobListenerNames.length; i++) {
					this.jobDetail.addJobListener(this.jobListenerNames[i]);
				}
			}
			logger.info("Created JobDetail: "+jobDetail+"; targetClass: "+targetClass+"; targetObject: "+targetObject+"; targetMethod: "+targetMethod+"; staticMethod: "+staticMethod+"; arguments: "+arguments+";");
		}
		finally
		{
			logger.debug("end");
		}
	}

	/**
	 * Setter for the concurrent property.
	 * 
	 * @param concurrent
	 * @see #concurrent
	 */
	public void setConcurrent(boolean concurrent)
	{
		this.concurrent = concurrent;
	}

	/**
	 * setter for the durable property.
	 * 
	 * @param durable
	 * 
	 * @see #durable
	 */
	public void setDurable(boolean durable)
	{
		this.durable = durable;
	}

	/**
	 * setter for the group property.
	 * 
	 * @param group
	 * 
	 * @see #group
	 */
	public void setGroup(String group)
	{
		this.group = group;
	}

	/**
	 * setter for the {@link #jobListenerNames} property.
	 * 
	 * @param jobListenerNames
	 * @see #jobListenerNames
	 */
	public void setJobListenerNames(String[] jobListenerNames)
	{
		this.jobListenerNames = jobListenerNames;
	}

	/**
	 * setter for the {@link #shouldRecover} property.
	 * 
	 * @param shouldRecover
	 * @see #shouldRecover
	 */
	public void setShouldRecover(boolean shouldRecover)
	{
		this.shouldRecover = shouldRecover;
	}

	/**
	 * setter for the {@link #volatility} property.
	 * 
	 * @param volatility
	 * @see #volatility
	 */
	public void setVolatility(boolean volatility)
	{
		this.volatility = volatility;
	}

	/**
	 * This is a cluster safe Job designed to invoke a method on any bean defined within the same Spring
	 * ApplicationContext.
	 * <p>
	 * The only entries this Job expects in the JobDataMap are "targetClass" and "targetMethod".<br>
	 * - It uses the value of the <code>targetClass</code> entry to get the desired bean from the Spring ApplicationContext.<br>
	 * - It uses the value of the <code>targetMethod</code> entry to determine which method of the Bean (identified by targetClass) to invoke.
	 * <p>
	 * It uses the static ApplicationContext in the MethodInvokingJobDetailFactoryBean,
	 * which is ApplicationContextAware, to get the Bean with which to invoke the method.
	 * <p>
	 * All Exceptions thrown from the execute method are caught and wrapped in a JobExecutionException.
	 * 
	 * @see MyMethodInvokingBean#applicationContext
	 * @see #execute(JobExecutionContext)
	 * 
	 * @author Stephen M. Wick
	 */
	public static class MethodInvokingJob implements Job
	{
		protected Log logger = LogFactory.getLog(getClass());
		
		/**
		 * When invoked by a Quartz scheduler, <code>execute</code> invokes a method on a Class or Object in the JobExecutionContext provided.
		 * <p>
		 * <b>Implementation</b><br>
		 * The Class is identified by the "targetClass" entry in the JobDataMap of the JobExecutionContext provided.  If targetClass is specified, then targetMethod must be a static method.<br>
		 * The Object is identified by the 'targetObject" entry in the JobDataMap of the JobExecutionContext provided.  If targetObject is provided, then targetClass will be overwritten.  This Object must be Serializable when <code>concurrent</code> is set to false.<br>
		 * The method is identified by the "targetMethod" entry in the JobDataMap of the JobExecutionContext provided.<br>
		 * The "staticMethod" entry in the JobDataMap of the JobExecutionContext can be used to specify a Class and Method in one entry (ie: "example.ExampleClass.someStaticMethod")<br>
		 * The method arguments (an array of Objects) are identified by the "arguments" entry in the JobDataMap of the JobExecutionContext.  All arguments must be Serializable when <code>concurrent</code> is set to false.
		 * <p>
		 * Logging is provided at the DEBUG and INFO levels; 8 lines at the DEBUG level, and 1 line at the INFO level.
		 * @see Job#execute(JobExecutionContext)
		 */
		public void execute(JobExecutionContext context) throws JobExecutionException
		{
			try
			{
				logger.debug("start");
				String targetClass = context.getMergedJobDataMap().getString("targetClass");
				//logger.debug("targetClass is "+targetClass);
				Class targetClassClass = null;
				if(targetClass!=null){
					targetClassClass = Class.forName(targetClass); // Could throw ClassNotFoundException
				}
				Object targetObject = context.getMergedJobDataMap().get("targetObject");
				ApplicationContext ac = (ApplicationContext)context.getScheduler().getContext().get("applicationContext");

				//logger.debug("targetObject is "+targetObject);
				String targetMethod = context.getMergedJobDataMap().getString("targetMethod");
				//logger.debug("targetMethod is "+targetMethod);
				String staticMethod = context.getMergedJobDataMap().getString("staticMethod");
				//logger.debug("staticMethod is "+staticMethod);
				Object[] arguments = (Object[])context.getMergedJobDataMap().get("arguments");
				//logger.debug("arguments are "+arguments);
				arguments=new Object[1];
				arguments[0]=ac;
					
				//logger.debug("creating MethodInvoker");
				MethodInvoker methodInvoker = new MethodInvoker();
				methodInvoker.setTargetClass(targetClassClass);
				methodInvoker.setTargetObject(targetObject);
				methodInvoker.setTargetMethod(targetMethod);
				methodInvoker.setStaticMethod(staticMethod);
				methodInvoker.setArguments(arguments);
				methodInvoker.prepare();
				//logger.info("Invoking: "+methodInvoker.getPreparedMethod().toGenericString());
				methodInvoker.invoke();

			}
			catch(Exception e)
			{
				throw new JobExecutionException(e);
			}
			finally
			{
				logger.debug("end");
			}
		}
	}

	public static class StatefulMethodInvokingJob extends MethodInvokingJob implements StatefulJob
	{
		// No additional functionality; just needs to implement StatefulJob.
	}

	public Object[] getArguments()
	{
		return arguments;
	}

	public void setArguments(Object[] arguments)
	{
		this.arguments = arguments;
	}

	public String getStaticMethod()
	{
		return staticMethod;
	}

	public void setStaticMethod(String staticMethod)
	{
		this.staticMethod = staticMethod;
	}

	public void setTargetObject(Object targetObject)
	{
		this.targetObject = targetObject;
	}
}


2.   SchedulerManager

 

package cn.xue.Scheduler.service;

import java.text.ParseException;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;

import org.quartz.CronTrigger;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SimpleTrigger;
import org.quartz.Trigger;
import org.quartz.TriggerUtils;
import org.quartz.impl.calendar.AnnualCalendar;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.stereotype.Component;

@Component("schedulerManager")
public class SchedulerManager {
	
	private SchedulerFactoryBean schedulerFactoryBean;
	@Autowired
	public void setSchedulerFactoryBean(SchedulerFactoryBean schedulerFactoryBean) {
		this.schedulerFactoryBean = schedulerFactoryBean;
	}
	public void addCalendarJob(Long l) throws ParseException, SchedulerException{
		String jobName=l+"add_Job";
		String triggerName=l+"add_Trigger";
		
        Scheduler scheduler = schedulerFactoryBean.getScheduler(); 
        JobDetail jobDetail = scheduler.getJobDetail(jobName, Scheduler.DEFAULT_GROUP);
		if(jobDetail==null){
			AnnualCalendar holidays = new AnnualCalendar();  //①法定节日是以每年为周期的,所以使用AnnualCalendar
			Calendar laborDay = new GregorianCalendar();  //②五一劳动节
			laborDay.add(Calendar.MONTH,5);
			laborDay.add(Calendar.DATE,1);
			holidays.setDayExcluded(laborDay, true);     //②-1:排除的日期,如果设置为false则为包含
			Calendar nationalDay = new GregorianCalendar();  //③国庆节
			nationalDay.add(Calendar.MONTH,10);
			nationalDay.add(Calendar.DATE,1);
			holidays.setDayExcluded(nationalDay, true);	//③-1:排除该日期
			
			scheduler.addCalendar("holidays", holidays, false, false);	//④向Scheduler注册日历
			
			Date runDate = TriggerUtils.getDateOf(0,0, 10, 1, 4);	//⑤4月1号 上午10点
			JobDetail job = new JobDetail(jobName, Scheduler.DEFAULT_GROUP, MyJob1.class);
			SimpleTrigger trigger = new SimpleTrigger(triggerName, Scheduler.DEFAULT_GROUP, runDate, null, SimpleTrigger.REPEAT_INDEFINITELY, 60L * 60L * 1000L);
			trigger.setCalendarName("holidays");	//⑥让Trigger应用指定的日历规则
			
			scheduler.scheduleJob(job, trigger);
			scheduler.start();
		}
		/*
		      由于节日是每年重复的,所以使用org.quartz.Calendar的AnnualCalendar实现类,通过②、③的代码,指定五一和国庆两个节日并通过AnnualCalendar#setDayExcluded(Calendar day, boolean exclude)方法添加这两个日期。
		  exclude为true时表示排除指定的日期,如果为false时表示包含指定的日期。
		    在定制好org.quartz.Calendar后,还需要通过Scheduler#addCalendar(String calName, Calendar calendar, boolean replace, boolean updateTriggers)进行注册,
		    如果updateTriggers为true,Scheduler中已引用Calendar的Trigger将得到更新,如④所示。
		  在⑥处,我们让一个Trigger指定使用Scheduler中代表节日的Calendar,这样Trigger就会避开五一和国庆这两个特殊日子了。
		*/
	}
	
	public void addJob(Long l) throws ParseException, SchedulerException{
		String jobName=l+"add_Job";
		String triggerName=l+"add_Trigger";
		
        Scheduler scheduler = schedulerFactoryBean.getScheduler(); 
        JobDetail jobDetail = scheduler.getJobDetail(jobName, Scheduler.DEFAULT_GROUP);
		if(jobDetail==null){
            jobDetail = new JobDetail(jobName, Scheduler.DEFAULT_GROUP,MyJob1.class);  
            jobDetail.getJobDataMap().put("xueyong", l);   
			Trigger trigger=new CronTrigger(triggerName,Scheduler.DEFAULT_GROUP, "0/5 * * * * ?" );
  
            scheduler.scheduleJob(jobDetail, trigger);     //注册并进行调度     
            scheduler.start();
		}
	}
	
	//移除某一个项目的某一个周期性活动的job
	public  void removeJob(Long l) throws SchedulerException{
		String jobName=l+"add_Job";
		String triggerName=l+"add_Trigger";
		 
		 Scheduler scheduler = schedulerFactoryBean.getScheduler(); 
		 scheduler.pauseTrigger(triggerName,Scheduler.DEFAULT_GROUP);//停止触发器
		 scheduler.unscheduleJob(triggerName,Scheduler.DEFAULT_GROUP);//移除触发器
		 scheduler.deleteJob(jobName,Scheduler.DEFAULT_GROUP);//删除任务
	}

	//修改job的触发时间
	public  void modifyJobTime(Long l ) throws SchedulerException, ParseException{
		String jobName=l+"add_Job";
		String triggerName=l+"add_Trigger";
		
		Scheduler scheduler = schedulerFactoryBean.getScheduler(); //获取一个调度器实例
		JobDetail jobDetail = scheduler.getJobDetail(jobName,Scheduler.DEFAULT_GROUP);
		if(jobDetail != null){	//update time
			CronTrigger cronTrigger = (CronTrigger)scheduler.getTrigger(triggerName,Scheduler.DEFAULT_GROUP);
			cronTrigger.setCronExpression( "0/20 * * * * ?" );
			
			scheduler.resumeTrigger(triggerName,Scheduler.DEFAULT_GROUP);  //重启触发器
			scheduler.rescheduleJob(cronTrigger.getName(), cronTrigger.getGroup(), cronTrigger);
		}
	}
}

3.  MyJob1

package cn.xue.Scheduler.service;

import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.SchedulerContext;
import org.quartz.SchedulerException;
import org.springframework.context.ApplicationContext;
import org.springframework.scheduling.quartz.QuartzJobBean;
import cn.xue.docModel.dao.LoopbkmkTreeDao;

public class MyJob1 extends QuartzJobBean  {

	@Override
	protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
        JobDataMap dataMap = context.getJobDetail().getJobDataMap();   
        String jobName = context.getJobDetail().getName();
		String jobGroup = context.getJobDetail().getGroup();
		Long projectID =  (Long)dataMap.get("xueyong");   
		//获取JobExecutionContext中的service对象  
        try {
			SchedulerContext schCtx = context.getScheduler().getContext();     
			ApplicationContext appCtx = (ApplicationContext)schCtx.get("applicationContext"); //获取Spring中的上下文  
			LoopbkmkTreeDao loopbkmkTreeDao= (LoopbkmkTreeDao)appCtx.getBean("loopbkmkTreeDao");
		
			System.out.println(projectID+"****777*****"+loopbkmkTreeDao.getAll().size());
		} catch (SchedulerException e1) {
			e1.printStackTrace();
		} 
	} 
};

/*
Cron 表达式包括以下 7 个字段:
* 秒
* 分
* 小时
* 月内日期
* 月
* 周内日期
* 年(可选字段)

特殊字符
Cron 触发器利用一系列特殊字符,如下所示:
●星号(*):可用在所有字段中,表示对应时间域的每一个时刻,例如,*在分钟字段时,表示“每分钟”;
●问号(?):该字符只在日期和星期字段中使用,它通常指定为“无意义的值”,相当于点位符;
●减号(-):表达一个范围,如在小时字段中使用“10-12”,则表示从10到12点,即10,11,12;
●逗号(,):表达一个列表值,如在星期字段中使用“MON,WED,FRI”,则表示星期一,星期三和星期五;
●斜杠(/):x/y表达一个等步长序列,x为起始值,y为增量步长值。如在分钟字段中使用0/15,则表示为0,15,30和45秒,而5/15在分钟字段中表示5,20,35,50,你也可以使用0/y;
●L:该字符只在日期和星期字段中使用,代表“Last”的意思,但它在两个字段中意思不同。L在日期字段中,表示这个月份的最后一天,如一月的31号,非闰年二月的28号;如果L用在星期中,
	则表示星期六,等同于7。但是,如果L出现在星期字段里,而且在前面有一个数值X,则表示“这个月的最后X天”,例如,6L表示该月的最后星期五;
●W:该字符只能出现在日期字段里,是对前导日期的修饰,表示离该日期最近的工作日。例如15W表示离该月15号最近的工作日,如果该月15号是星期六,则匹配14号星期五;如果15日是星期日,
	则匹配16号星期一;如果15号是星期二,那结果就是15号星期二。但必须注意关联的匹配日期不能够跨月,如你指定1W,如果1号是星期六,结果匹配的是3号星期一,而非上个月最后的那天。W字符串只能指定单一日期,而不能指定日期范围;
●LW组合:在日期字段可以组合使用LW,它的意思是当月的最后一个工作日;
●井号(#):该字符只能在星期字段中使用,表示当月某个工作日。如6#3表示当月的第三个星期五(6表示星期五,#3表示当前的第三个),而4#5表示当月的第五个星期三,假设当月没有第五个星期三,忽略不触发;
● C:该字符只在日期和星期字段中使用,代表“Calendar”的意思。它的意思是计划所关联的日期,如果日期没有被关联,则相当于日历中所有日期。例如5C在日期字段中就相当于日历5日以后的第一天。1C在星期字段中相当于星期日后的第一天。
Cron表达式对特殊字符的大小写不敏感,对代表星期的缩写英文大小写也不敏感。

*/

/*
字段 允许值 允许的特殊字符
秒 0-59 , - * /
分 0-59 , - * /
小时 0-23 , - * /
日期 1-31 , - * ? / L W C
月份 1-12 或者 JAN-DEC , - * /
星期 1-7 或者 SUN-SAT , - * ? / L C #
年(可选) 留空, 1970-2099 , - * /

表达式意义
"0 0 12 * * ?" 每天中午12点触发
"0 15 10 ? * *" 每天上午10:15触发
"0 15 10 * * ?" 每天上午10:15触发
"0 15 10 * * ? *" 每天上午10:15触发
"0 15 10 * * ? 2005" 2005年的每天上午10:15触发
"0 * 14 * * ?" 在每天下午2点到下午2:59期间的每1分钟触发
"0 0/5 14 * * ?" 在每天下午2点到下午2:55期间的每5分钟触发
"0 0/5 14,18 * * ?" 在每天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发
"0 0-5 14 * * ?" 在每天下午2点到下午2:05期间的每1分钟触发
"0 10,44 14 ? 3 WED" 每年三月的星期三的下午2:10和2:44触发
"0 15 10 ? * MON-FRI" 周一至周五的上午10:15触发
"0 15 10 15 * ?" 每月15日上午10:15触发
"0 15 10 L * ?" 每月最后一日的上午10:15触发
"0 15 10 ? * 6L" 每月的最后一个星期五上午10:15触发
"0 15 10 ? * 6L 2002-2005" 2002年至2005年的每月的最后一个星期五上午10:15触发
"0 15 10 ? * 6#3" 每月的第三个星期五上午10:15触发

晚上11点到早上8点之间每两个小时,早上八点
0 23-7/2,8 * * *
每个月的4号和每个礼拜的礼拜一到礼拜三的早上11点
0 11 4 * 1-3
1月1日早上4点
0 4 1 1 *

*/

/*
有些子表达式能包含一些范围或列表 
例如:子表达式(天(星期))可以为 “MON-FRI”,“MON,WED,FRI”,“MON-WED,SAT” 
“*”字符代表所有可能的值 
因此,“*”在子表达式(月)里表示每个月的含义,“*”在子表达式(天(星期))表示星期的每一天 

“/”字符用来指定数值的增量 
例如:在子表达式(分钟)里的“0/15”表示从第0分钟开始,每15分钟 
         在子表达式(分钟)里的“3/20”表示从第3分钟开始,每20分钟(它和“3,23,43”)的含义一样 
“?”字符仅被用于天(月)和天(星期)两个子表达式,表示不指定值 
当2个子表达式其中之一被指定了值以后,为了避免冲突,需要将另一个子表达式的值设为“?” 
“L” 字符仅被用于天(月)和天(星期)两个子表达式,它是单词“last”的缩写 
但是它在两个子表达式里的含义是不同的。 
在天(月)子表达式中,“L”表示一个月的最后一天 
在天(星期)自表达式中,“L”表示一个星期的最后一天,也就是SAT 
如果在“L”前有具体的内容,它就具有其他的含义了 
例如:“6L”表示这个月的倒数第6天,“FRIL”表示这个月的最一个星期五 
注意:在使用“L”参数时,不要指定列表或范围,因为这会导致问题 

字段   允许值   允许的特殊字符 
秒    0-59    , - * / 
分    0-59    , - * / 
小时    0-23    , - * / 
日期    1-31    , - * ? / L W C 
月份    1-12 或者 JAN-DEC    , - * / 
星期    1-7 或者 SUN-SAT    , - * ? / L C # 
年(可选)    留空, 1970-2099    , - * / 

*/


 

  相关解决方案