Статьи

Интеграция Spring и Quartz с использованием пользовательских аннотаций @QuartzJob

Мы знаем, что Spring поддерживает интеграцию с платформой Quartz.
Но на данный момент Spring поддерживает только статический декларативный XML-подход.
Если вы хотите узнать, как интегрировать Spring + Quartz, вы можете обратиться:

http://sivalabs.blogspot.com/2011/05/spring-quartz-javamail-integration.html

В рамках требований моего проекта для домашних животных я получил динамическое планирование заданий и подумал о следующих 2 вариантах:
 1. Использование аннотаций для предоставления метаданных заданий
 2. Загрузка метаданных заданий из базы данных

На данный момент я подумал о том, чтобы продолжить подход, основанный на аннотациях, и Я хочу также интегрировать его с Spring.
Вот как я это сделал.

1. Создайте пользовательскую аннотацию QuartzJob

package com.sivalabs.springsamples.jobscheduler;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.springframework.stereotype.Component;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component@Scope("prototype")
public @interface QuartzJob
{   
    String name();
    String group() default "DEFAULT_GROUP";
    String cronExp();
}

2. Создайте ApplicationListener для сканирования всех классов реализации Job и запланируйте их с помощью планировщика Quartz.

package com.sivalabs.springsamples.jobscheduler;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.quartz.Job;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.scheduling.quartz.CronTriggerBean;
import org.springframework.scheduling.quartz.JobDetailBean;

public class QuartJobSchedulingListener implements ApplicationListener<ContextRefreshedEvent>
{    
    @Autowired
    private Scheduler scheduler;

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event)
    {
        try
        {
            ApplicationContext applicationContext = event.getApplicationContext();
            List<CronTriggerBean> cronTriggerBeans = this.loadCronTriggerBeans(applicationContext);
            this.scheduleJobs(cronTriggerBeans);
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }

    private List<CronTriggerBean> loadCronTriggerBeans(ApplicationContext applicationContext)
    {
        Map<String, Object> quartzJobBeans = applicationContext.getBeansWithAnnotation(QuartzJob.class);
        Set<String> beanNames = quartzJobBeans.keySet();
        List<CronTriggerBean> cronTriggerBeans = new ArrayList<CronTriggerBean>();
        for (String beanName : beanNames)
        {
            CronTriggerBean cronTriggerBean = null;
            Object object = quartzJobBeans.get(beanName);
            System.out.println(object);
            try {
                cronTriggerBean = this.buildCronTriggerBean(object);
            } catch (Exception e) {
                e.printStackTrace();
            }

            if(cronTriggerBean != null)
            {
                cronTriggerBeans.add(cronTriggerBean);
            }
        }
        return cronTriggerBeans;
    }

    public CronTriggerBean buildCronTriggerBean(Object job) throws Exception
    {
        CronTriggerBean cronTriggerBean = null;
        QuartzJob quartzJobAnnotation = AnnotationUtils.findAnnotation(job.getClass(), QuartzJob.class);
        if(Job.class.isAssignableFrom(job.getClass()))
        {
            System.out.println("It is a Quartz Job");
            cronTriggerBean = new CronTriggerBean();
            cronTriggerBean.setCronExpression(quartzJobAnnotation.cronExp());                
            cronTriggerBean.setName(quartzJobAnnotation.name()+"_trigger");
            //cronTriggerBean.setGroup(quartzJobAnnotation.group());
            JobDetailBean jobDetail = new JobDetailBean();
            jobDetail.setName(quartzJobAnnotation.name());
            //jobDetail.setGroup(quartzJobAnnotation.group());
            jobDetail.setJobClass(job.getClass());
            cronTriggerBean.setJobDetail(jobDetail);            
        }
        else
        {
            throw new RuntimeException(job.getClass()+" doesn't implemented "+Job.class);
        }
        return cronTriggerBean;
    }

    protected void scheduleJobs(List<CronTriggerBean> cronTriggerBeans)
    {
        for (CronTriggerBean cronTriggerBean : cronTriggerBeans) {
            JobDetail jobDetail = cronTriggerBean.getJobDetail();
            try {
                scheduler.scheduleJob(jobDetail, cronTriggerBean);
            } catch (SchedulerException e) {
                e.printStackTrace();
            }            
        }
    }
}

3. Создайте настроенный JobFactory для использования бинов Spring в качестве объектов реализации Job.

 

package com.sivalabs.springsamples.jobscheduler;

import org.quartz.Job;
import org.quartz.spi.TriggerFiredBundle;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.PropertyAccessorFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.scheduling.quartz.SpringBeanJobFactory;

public class SpringQuartzJobFactory extends SpringBeanJobFactory
{
@Autowired
private ApplicationContext ctx;

@Override
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception 
{
    @SuppressWarnings("unchecked")
Job job = ctx.getBean(bundle.getJobDetail().getJobClass());
    BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(job);
    MutablePropertyValues pvs = new MutablePropertyValues();
    pvs.addPropertyValues(bundle.getJobDetail().getJobDataMap());
    pvs.addPropertyValues(bundle.getTrigger().getJobDataMap());
    bw.setPropertyValues(pvs, true);
    return job;
}
}

4. Создайте классы реализации Job и аннотируйте их, используя @QuartzJob

package com.sivalabs.springsamples.jobscheduler;import java.util.Date;import org.quartz.JobExecutionContext;import org.quartz.JobExecutionException;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.scheduling.quartz.QuartzJobBean;@QuartzJob(name="HelloJob", cronExp="0/5 * * * * ?")public class HelloJob extends QuartzJobBean{@Overrideprotected void executeInternal(JobExecutionContext context)throws JobExecutionException{System.out.println("Hello Job is running @ "+new Date());System.out.println(this.hashCode());}}

5. Настройте SchedulerFactoryBean и QuartJobSchedulingListener в applicationContext.xml.

<beans>
    <context:annotation-config/>
    <context:component-scan base-package="com.sivalabs"/>

    <bean class="com.sivalabs.springsamples.jobscheduler.QuartJobSchedulingListener"></bean>
    <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean"><property name="jobFactory"><bean class="com.sivalabs.springsamples.jobscheduler.SpringQuartzJobFactory"></bean></property>    </bean>

</beans>

6. Тестовый клиент

 

package com.sivalabs.springsamples;

import org.quartz.Job;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.sivalabs.springsamples.jobscheduler.HowAreYouJob;
import com.sivalabs.springsamples.jobscheduler.InvalidJob;

public class TestClient
{
    public static void main(String[] args)
    {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        System.out.println(context);        
    }

}

Ссылка: http://sivalabs.blogspot.com/2011/10/spring-and-quartz-integration-using.html