Full Spring与Quartz集成,实现实时通知电子邮件

Full Spring与Quartz集成,实现实时通知电子邮件,spring,spring-boot,notifications,quartz-scheduler,Spring,Spring Boot,Notifications,Quartz Scheduler,我目前正在用spring boot开发一个应用程序,它允许用户创建约会。因此,约会基本上有startDateTime和endDateTime字段+电子邮件。创建约会会在MySql数据库的约会表中添加新行 我想做的是在数据库中定义的startDateTime前一小时通过电子邮件通知用户。我在寻找解决办法,但没有找到。我发现jobs(springbatch)可以做到这一点,但是jobs依赖于频率检查(天、周、月),我要找的是实时通知。欢迎为实现该任务的解决方案提供任何帮助或指导 问候语您可以使用调度

我目前正在用spring boot开发一个应用程序,它允许用户创建约会。因此,约会基本上有startDateTime和endDateTime字段+电子邮件。创建约会会在MySql数据库的约会表中添加新行

我想做的是在数据库中定义的startDateTime前一小时通过电子邮件通知用户。我在寻找解决办法,但没有找到。我发现jobs(springbatch)可以做到这一点,但是jobs依赖于频率检查(天、周、月),我要找的是实时通知。欢迎为实现该任务的解决方案提供任何帮助或指导


问候语

您可以使用调度库,例如,提供与Spring framework的轻松集成

将约会保存到数据库中后,将在理想的时间(例如,开始日期前一小时)安排“发送电子邮件”作业

“发送电子邮件”作业必须实现
org.quartz.job
,更具体地说是
execute
方法,在该方法中,您可以使用您的
Autowired
sendmailservice
实现

@Service
public 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);
    }
}
在下面,您可以找到一个(几乎)完整的示例,说明如何在代码中实现这样的需求

更新-计划作业的代码

首先,我们定义一个
SchedulingService
接口

public interface SchedulingService {

    startScheduler() throws SchedulerException;

    void standbyScheduler() throws SchedulerException;

    void shutdownScheduler() throws SchedulerException;

    void scheduleJob(JobDetail jobDetail, Trigger trigger) throws SchedulerException;
}
以及相关的实施

@Service
public 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()

sendmailjob
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 parameters
Appointment appointment = (Appointment) jobDataMap.get("appointment");
更新-配置代码

Bean
scheduler
在负责初始化的
@Configuration
类中定义

调度配置
类定义为:

@Configuration
public 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 Properties
org.quartz.scheduler.instanceName = AppScheduler
org.quartz.scheduler.instanceId = AUTO

# Configure ThreadPool
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount = 10
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true

# Configure JobStore
org.quartz.jobStore.misfireThreshold = 60000
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass = 
org.quartz.impl.jdbcjobstore.oracle.OracleDelegate
org.quartz.jobStore.tablePrefix = APP.QRTZ_
org.quartz.jobStore.useProperties = false
org.quartz.jobStore.dataSource = appDs
org.quartz.jobStore.isClustered = true
org.quartz.jobStore.clusterCheckinInterval = 20000

# Configure Datasources
org.quartz.dataSource.appDs.driver = oracle.jdbc.driver.OracleDriver
org.quartz.dataSource.appDs.URL = jdbc:oracle:thin:@dbsrv:1521:appdb
org.quartz.dataSource.appDs.user = db_user
org.quartz.dataSource.appDs.password = db_pwd
org.quartz.dataSource.appDs.maxConnections = 5
org.quartz.dataSource.appDs.validationQuery = select 0 from dual
最后一步是在应用程序上下文初始化中调用调度程序方法,如下所示(请注意
SchedulingService
中添加的方法):

注意:
SchedulingContextListener
应在应用程序初始化中的
servletContext
中注册,具体取决于Spring配置的定义方式,可以使用Spring引导,也可以使用传统的Spring MVC配置


希望对您有所帮助。

您可以使用调度库,例如,提供与Spring framework的轻松集成

将约会保存到数据库中后,将在理想的时间(例如,开始日期前一小时)安排“发送电子邮件”作业

“发送电子邮件”作业必须实现
org.quartz.job
,更具体地说是
execute
方法,在该方法中,您可以使用您的
Autowired
sendmailservice
实现

@Service
public 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);
    }
}
在下面,您可以找到一个(几乎)完整的示例,说明如何在代码中实现这样的需求

更新-计划作业的代码

首先,我们定义一个
SchedulingService
接口

public interface SchedulingService {

    startScheduler() throws SchedulerException;

    void standbyScheduler() throws SchedulerException;

    void shutdownScheduler() throws SchedulerException;

    void scheduleJob(JobDetail jobDetail, Trigger trigger) throws SchedulerException;
}
以及相关的实施

@Service
public 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()

sendmailjob
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 parameters
Appointment appointment = (Appointment) jobDataMap.get("appointment");
更新-配置代码

Bean
scheduler
在负责初始化的
@Configuration
类中定义

调度配置
类定义为:

@Configuration
public 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 Properties
org.quartz.scheduler.instanceName = AppScheduler
org.quartz.scheduler.instanceId = AUTO

# Configure ThreadPool
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount = 10
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true

# Configure JobStore
org.quartz.jobStore.misfireThreshold = 60000
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass = 
org.quartz.impl.jdbcjobstore.oracle.OracleDelegate
org.quartz.jobStore.tablePrefix = APP.QRTZ_
org.quartz.jobStore.useProperties = false
org.quartz.jobStore.dataSource = appDs
org.quartz.jobStore.isClustered = true
org.quartz.jobStore.clusterCheckinInterval = 20000

# Configure Datasources
org.quartz.dataSource.appDs.driver = oracle.jdbc.driver.OracleDriver
org.quartz.dataSource.appDs.URL = jdbc:oracle:thin:@dbsrv:1521:appdb
org.quartz.dataSource.appDs.user = db_user
org.quartz.dataSource.appDs.password = db_pwd
org.quartz.dataSource.appDs.maxConnections = 5
org.quartz.dataSource.appDs.validationQuery = select 0 from dual
最后一步是在应用程序上下文初始化中调用调度程序方法,如下所示(请注意
SchedulingService
中添加的方法):

注意:
SchedulingContextListener
应在应用程序初始化中的
servletContext
中注册,具体取决于Spring配置的定义方式,可以使用Spring引导,也可以使用传统的Spring MVC配置


希望对您有所帮助。

是否有源代码示例,以了解在约会中执行save方法后如何安排作业?用建议的示例实现代码更新我的答案将非常有用。看一看。希望能有帮助。谢谢你的回复。现在更清楚了,但我仍然有一个问题:当使用JobBuilder创建jobdetail时,您只定义类型的类job,但是这个类如何知道我们为哪个约会创建job,从而检索要向其发送电子邮件的合适人员的电子邮件?另外,如果你也能发布conf,那也很好,因为在谷歌,他们经常谈论基于属性文件的配置,这非常令人困惑。谢谢,againI添加了您回复中描述的最后一个类,但仍然不起作用。所以我调试了应用程序,发现listenn