您可以使用调度库(例如quartz)来提供与Spring框架的轻松集成。
将约会保存在数据库中后,将在所需的时间(例如,开始日期前一小时)安排“发送电子邮件”作业。
“发送电子邮件”作业必须实现
org.quartz.Job,更具体地讲
execute,您可以在其中使用
Autowired
SendEmailService实现的方法。
您可以在下面找到(几乎)完整的示例,以了解如何在代码中实现这种要求。
更新-计划作业的代码
首先,我们定义一个
SchedulingService接口。
public interface SchedulingService { startScheduler() throws SchedulerException; void standbyScheduler() throws SchedulerException; void shutdownScheduler() throws SchedulerException; void scheduleJob(JobDetail jobDetail, Trigger trigger) throws SchedulerException;}和相关的执行。
@Servicepublic class SchedulingServiceImpl implements SchedulingService { @Autowired private Scheduler scheduler; @Override public void startScheduler() throws SchedulerException { if (!scheduler.isStarted()) { scheduler.start(); } } @Override public void standbyScheduler() throws SchedulerException { if (!scheduler.isInStandbyMode()) { scheduler.standby(); } } @Override public void shutdownScheduler() throws SchedulerException { if (!scheduler.isShutdown()) { scheduler.shutdown(); } } @Override public void scheduleJob(JobDetail jobDetail, Trigger trigger) throws SchedulerException { scheduler.scheduleJob(jobDetail, trigger); }}然后,
AppointmentServiceImpl我们有一个
createAppointment()调用的方法
scheduleSendEmailJob()。
@Servicepublic class AppointmentServiceImpl implements AppointmentService { @Autowired private SchedulingService schedulingService; public void createAppointment(Appointment appointment) throws SchedulerException { // Save appointment to database // ... // Schedule send email job if appointment has been successfully saved scheduleSendEmailJob(appointment); return; } private void scheduleSendEmailJob(Appointment appointment) throws SchedulerException { JobDetail jobDetail = JobBuilder.newJob().ofType(SendEmailJob.class) .storeDurably() .withIdentity(UuidUtils.generateId(), "APPOINTMENT_NOTIFICATIONS") .withDescription("Send email notification for appointment") .build(); jobDetail.getJobDataMap().put("appointmentId", appointment.getId()); Date scheduleDate = appointment.computeDesiredScheduleDate(); String cronexpression = convertDateToCronexpression(scheduleDate); CronTrigger trigger = TriggerBuilder.newTrigger().forJob(jobDetail) .withIdentity(UuidUtils.generateId(), "APPOINTMENT_NOTIFICATIONS") .withDescription("Trigger description") .withSchedule(CronScheduleBuilder.cronSchedule(cronexpression)) .build(); schedulingService.scheduleJob(jobDetail, trigger); } private String convertDateToCronexpression(Date date) { Calendar calendar = new GregorianCalendar(); if (date == null) return null; calendar.setTime(date); int year = calendar.get(java.util.Calendar.YEAR); int month = calendar.get(java.util.Calendar.MONTH) + 1; int day = calendar.get(java.util.Calendar.DAY_OF_MONTH); int hour = calendar.get(java.util.Calendar.HOUR_OF_DAY); int minute = calendar.get(java.util.Calendar.MINUTE); return String.format("0 %d %d %d %d ? %d", minute, hour, day, month, year); }}类
SendEmailJob是
Job接口的实现,负责使用相关服务发送电子邮件。
更新-将参数从调度方法传递到实际作业执行的代码
为了传递参数,正在使用jobDataMap。例如:
public class SendEmailJob implements Job { @Autowired private AppointmentService appointmentService; @Autowired private SendEmailService sendEmailService; @Override public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException { JobDataMap jobDataMap = jobExecutionContext.getJobDetail().getJobDataMap(); // Retrieving passed parameters Long appointmentId = (Long) jobDataMap.get("appointmentId"); Appointment appointment = appointmentService.findById(appointmentId); // Send email sendEmailService.sendEmail(appointment); }}注意: 约会对象也可以从调度方法传递到实际的作业执行,您可以传递:
jobDetail.getJobDataMap().put("appointment", appointment);得到:
// Retrieving passed parametersAppointment appointment = (Appointment) jobDataMap.get("appointment");更新-配置代码
Bean
scheduler是在
@Configuration负责Quartz初始化的类中定义的。
SchedulingConfiguration类定义为:
@Configurationpublic class SchedulingConfiguration { @Autowired private ApplicationContext applicationContext; @Bean public Scheduler scheduler() throws SchedulerException, IOException { StdSchedulerFactory factory = new StdSchedulerFactory(); factory.initialize(new ClassPathResource("properties/quartz.properties").getInputStream()); Scheduler scheduler = factory.getScheduler(); scheduler.setJobFactory(springBeanJobFactory()); return scheduler; } @Bean public SpringBeanJobFactory springBeanJobFactory() { AutowiringSpringBeanJobFactory jobFactory = new AutowiringSpringBeanJobFactory(); jobFactory.setApplicationContext(applicationContext); return jobFactory; }}我们的
quartz.properties文件位于
resources/properties文件夹中。请注意,作业持久性数据库是Oracle实例。
# Configure Main Scheduler Propertiesorg.quartz.scheduler.instanceName = AppSchedulerorg.quartz.scheduler.instanceId = AUTO# Configure ThreadPoolorg.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPoolorg.quartz.threadPool.threadCount = 10org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true# Configure JobStoreorg.quartz.jobStore.misfireThreshold = 60000org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTXorg.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.oracle.OracleDelegateorg.quartz.jobStore.tablePrefix = APP.QRTZ_org.quartz.jobStore.useProperties = falseorg.quartz.jobStore.dataSource = appDsorg.quartz.jobStore.isClustered = trueorg.quartz.jobStore.clusterCheckinInterval = 20000# Configure Datasourcesorg.quartz.dataSource.appDs.driver = oracle.jdbc.driver.OracleDriverorg.quartz.dataSource.appDs.URL = jdbc:oracle:thin:@dbsrv:1521:appdborg.quartz.dataSource.appDs.user = db_userorg.quartz.dataSource.appDs.password = db_pwdorg.quartz.dataSource.appDs.maxConnections = 5org.quartz.dataSource.appDs.validationQuery = select 0 from dual
最后一步是在应用程序上下文初始化中调用调度程序方法,如下所示(请注意在中添加的方法
SchedulingService):
public class SchedulingContextListener implements ServletContextListener { private static final Logger logger = LogManager.getLogger(SchedulingContextListener.class); private SchedulingService schedulingService(ServletContextEvent sce) { WebApplicationContext springContext = WebApplicationContextUtils.getWebApplicationContext(sce.getServletContext()); return springContext.getBean(SchedulingService.class); } @Override public void contextInitialized(ServletContextEvent sce) { try { this.schedulingService(sce).startScheduler(); } catch (SchedulerException e) { logger.error("Error while Scheduler is being started", e); } } @Override public void contextDestroyed(ServletContextEvent sce) { try { this.schedulingService(sce).shutdownScheduler(); } catch (SchedulerException e) { logger.error("Error while Scheduler is being shutdown", e); } }}注意:
SchedulingContextListener应该
servletContext在应用程序初始化中注册,这取决于使用Spring
Boot或传统的Spring MVC Configuration定义Spring配置的方式。
希望能有所帮助。



