最近做一个小项目的时候需要用到动态发定时通知的功能,本人小菜鸡,找了半天也没找到合适又便捷的解决办法,刚开始写业务的时候觉得就是一行@Scheduled(cron=" ")应该就可以解决的事情,业务写完了来调用 的时候才傻眼了,这个注解方式只能写死并且对应的函数不能带参数,老惨了。
废话就不多说了,直接上步骤:
import org.quartz.spi.TriggerFiredBundle;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.scheduling.quartz.AdaptableJobFactory;
import org.springframework.stereotype.Component;
@Component
public class TaskJobFactory extends AdaptableJobFactory {
@Autowired
AutowireCapableBeanFactory capableBeanFactory;
@Override
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
//调用父类的方法
Object jobInstance = super.createJobInstance(bundle);
//进行注入
capableBeanFactory.autowireBean(jobInstance);
return jobInstance;
}
}
import com.gongfeng.workbeeprj.system.management09.quartz.management.job.TaskJobFactory;
import org.quartz.Scheduler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.PropertiesFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import java.io.IOException;
@Configuration
public class QuartzConfigurer {
@Autowired
TaskJobFactory jobFactory;
@Bean(name = "SchedulerFactory")
public SchedulerFactoryBean schedulerFactoryBean() throws IOException {
//获取配置属性
PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties"));
//在quartz.properties中的属性被读取并注入后再初始化对象
propertiesFactoryBean.afterPropertiesSet();
//创建SchedulerFactoryBean
SchedulerFactoryBean factory = new SchedulerFactoryBean();
factory.setQuartzProperties(propertiesFactoryBean.getObject());
factory.setJobFactory(jobFactory);
return factory;
}
/*
* 通过SchedulerFactoryBean获取Scheduler的实例
*/
@Bean(name = "scheduler")
public Scheduler scheduler() throws IOException {
return schedulerFactoryBean().getScheduler();
}
}
quartz.properties文件内容如下:
#quartz集群配置
# ===========================================================================
# Configure Main Scheduler Properties 调度器属性
# ===========================================================================
#调度标识名 集群中每一个实例都必须使用相同的名称
org.quartz.scheduler.instanceName=DefaultQuartzScheduler
#ID设置为自动获取 每一个必须不同
org.quartz.scheduler.instanceid=AUTO
#============================================================================
# Configure ThreadPool
#============================================================================
#线程池的实现类(一般使用SimpleThreadPool即可满足几乎所有用户的需求)
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
#指定线程数,至少为1(无默认值)(一般设置为1-100直接的整数合适)
org.quartz.threadPool.threadCount = 100
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true
#设置线程的优先级(最大为java.lang.Thread.MAX_PRIORITY 10,最小为Thread.MIN_PRIORITY 1,默认为5)
org.quartz.threadPool.threadPriority = 5
import org.quartz.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Map;
@Component
public class QuartzJobManager {
private static final Logger logger = LoggerFactory.getLogger(QuartzJobManager.class);
private static QuartzJobManager jobUtil;
@Autowired
private Scheduler scheduler;
public QuartzJobManager() {
jobUtil = this;
}
public static QuartzJobManager getInstance() {
return QuartzJobManager.jobUtil;
}
/**
* 添加通知任务
* 此处jobName不能相同
* @param clazz
* @param jobName
* @param jobGroupName
* @param cronExpression
* @param argMap
*/
public void addMessageSendJob(Class clazz, String jobName, String jobGroupName, String cronExpression, Map<String, Object> argMap){
try {
// 启动调度器
scheduler.start();
//构建job信息
JobDetail jobDetail = JobBuilder.newJob(((Job) clazz.newInstance()).getClass()).withIdentity(jobName, jobGroupName).build();
//表达式调度构建器(即任务执行的时间)
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression);
//按新的cronExpression表达式构建一个新的trigger
CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(jobName, jobGroupName).withSchedule(scheduleBuilder).build();
//获得JobDataMap,写入数据
if (argMap != null) {
trigger.getJobDataMap().putAll(argMap);
}
scheduler.scheduleJob(jobDetail, trigger);
} catch (Exception e) {
e.printStackTrace();
}
}
}
@Component
public class TaskMessageSendJob implements Job {
@Resource
public MessageSendService messageSendService; // 自定义的业务类
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
String taskName = context.getJobDetail().getKey().getName(); // 获取任务名
Map<String, Object> map = context.getTrigger().getJobDataMap(); // 获取构建任务时的参数列表
String cron= (String) map.get("cron"); // 获取cron表达式(执行时间)
// 任务内容开始
List<String> customerList= (List<String>) map.get("customerList");
String content= (String) map.get("content");
messageSendService.sendMessage(customerList,cron,content);
// 任务内容结束
}
}
@Service
@Transactional
public class TaskMessageSendService {
@Autowired
private QuartzJobManager quartzJobManager;
/**
* 定时发送消息
* 其实这个方法就是用来给定时任务装参数的,具体装配过程参考上一个类
* @param cron
* @param customerList
* @param content
*/
public void execute(String cron, List<String> customerList, String content) {
Map<String,Object> map=new HashMap<>();
map.put("cron",cron); // cron字符串
map.put("customerList",customerList);
map.put("content",content);
// TaskMessageSendJob是自己创建的job类,具体见上一个类
quartzJobManager.addMessageSendJob(TaskMessageSendJob.class,"MessageSend"+(new Date()).getTime(),"MessageSend",cron,map);
}
}
@Resource
private TaskMessageSendService taskMessageSendService;
/**
* 获取项目后发送消息
* @param projectList
* @param time
* @param content
* @return
*/
private CommonResult sendMessageFromProject(List<MessageProject> projectList,String time,String content){
// 获取cron字符串
String cronTime=ConvertTimeToCron(time);
if(cronTime==null||cronTime=="")
return new CommonResult(400,"时间格式不正确,\"yyyy-MM-dd HH:mm\"");
Set<String> customerSet=new HashSet<>();
if(projectList.size()>0){
for (MessageProject project : projectList) {
List<String> memberList = messageSendDao.getCustomerByProject(project.getProjectId());
for (String str : memberList) {
customerSet.add(str);
}
}
}
List<String> customerList=new ArrayList<>(customerSet);
CommonResult commonResult=null;
try {
// 设置定时任务
taskMessageSendService.execute(cronTime,customerList,content);
commonResult=new CommonResult(200,"定时消息设置成功!");
}catch (Exception e){
commonResult=new CommonResult(400,"定时通知设置异常!");
}
return commonResult;
}
/**
* 将时间字符串转为cron字符串
* @param time
* @return
*/
private String ConvertTimeToCron(String time){
if(!isValidDate(time)) return null;
StringBuilder str=new StringBuilder();
// time 是yyyy-MM-dd HH:mm格式的
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm");
try {
str.append("0 ");//秒
String[] strings1=time.split(":");
str.append(strings1[1]+" "); // 分
String[] strings2=strings1[0].split(" ");
str.append(strings2[1]+" "); // 时
String[] strings3=strings2[0].split("-");
str.append(strings3[2]+" ");// 日
str.append(strings3[1]+" ");// 月
str.append(" ? ");
str.append(strings3[0]);// 年
}catch (Exception e){
e.printStackTrace();
return null;
}
return str.toString();
}
/**
* 判断字符串是否是时间格式
* @param str
* @return
*/
public boolean isValidDate(String str) {
boolean convertSuccess=true;
// 指定日期格式为四位年/两位月份/两位日期,注意yyyy/MM/dd区分大小写;
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm");
try {
// 设置lenient为false. 否则SimpleDateFormat会比较宽松地验证日期,比如2007/02/29会被接受,并转换成2007/03/01
format.setLenient(false);
format.parse(str);
} catch (ParseException e) {
e.printStackTrace();
// 如果throw java.text.ParseException或者NullPointerException,就说明格式不对
convertSuccess=false;
}
return convertSuccess;
}