使用注解的方式注册 Quartz 的 Job

  1. 1. @ScheduledJob 注解实现
    1. 1.1. 1. 注解声明
    2. 1.2. 2. 处理注解类

Java 程序员最喜欢的就是造轮子了,后端开发中时常要自己造一些轮子,一个工具包、封装一些开源组件、功能定制化等等,太常见了。

在 Spring 的自造轮子最常见的莫过于设计个注解,然后扫描到后在 BeanPostProcessor 中处理该注解对象,并根据注解组装一个新的对象然后注册到 Spring 中。

场景如《Quartz 动态调度 Job》所述那样,现在对文中 @ScheduledJob 注解的实现做详细说明。

@ScheduledJob 注解实现

1. 注解声明

我们需要支持 Quartz 的 Job 中常用的几个属性的配置,如任务的名字、任务分组和任务触发执行的时间。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface ScheduledJob {
String name();

String group() default QuartzConstants.DEFAULT_GROUP;

/**
* Cron 表达式
*
* @return cron expression
*/
String cronExp();
}

注解在 ScheduledJob 上的 @Component 是 Spring 的元注解,该注解的作用相当于使 ScheduledJob 也像 Component 注解一样使类自动被 Spring 发现。

2. 处理注解类

新建一个 BeanPostProcessor 接口的实现类,对有 @ScheduledJob 注解的 bean 进行处理,相类逻辑主要实现在 postProcessAfterInitialization 中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public class QuartzJobAutoDiscovery implements BeanPostProcessor {
/**
* 注册 Job/Trigger
*/
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
val annotation = AnnotationUtils.findAnnotation(bean.getClass(), ScheduledJob.class);
val targetClass = AopProxyUtils.ultimateTargetClass(bean);

if (annotation != null) {
if (!Job.class.isAssignableFrom(targetClass))
return bean;

Map<String, Object> jobData = null;

if (AbstractJob.class.isAssignableFrom(targetClass)) {
AbstractJob job = (AbstractJob)bean;

jobData = job.getInitialJobData();
}

val name = annotation.name();
val group = annotation.group();
val cronExp = annotation.cronExp();

val job = QuartzUtils.createJobDetail(name, group, targetClass);
val trigger = QuartzUtils.createTrigger(QuartzUtils.wrapTriggerName(name),
QuartzUtils.wrapTriggerName(group), job.getKey(), cronExp, jobData);

try {
this.beanFactory.registerSingleton(name, job);
this.beanFactory.registerSingleton(QuartzUtils.wrapTriggerName(name), trigger);
} catch (IllegalStateException e) {
// Pass
}
}

return bean;
}
}

上述代码中使用了 ConfigurableBeanFactoryregisterSingleton 来动态将对象注册到 Spring 中,我们需要实现 BeanFactoryAware 类,让 Spring 给我们注入 BeanFactory 实例。

1
2
3
4
5
6
7
8
9
10
11
12
public class QuartzJobAutoDiscovery implements BeanPostProcessor, BeanFactoryAware {
private ConfigurableBeanFactory beanFactory;

// ...

@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
if (beanFactory instanceof ConfigurableListableBeanFactory) {
this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
}
}
}

总体思路就是通过手动构造 Quartz 需要的 JobDetailTrigger 对象并注册到 Spring 中,Quartz 初始化后调度器通过 Spring 取到这些对象进行管理。