在Spring mvc的开发中,我们可以通过RequestMapping来配,当前方法用于处理哪一个URL的请求.同样我们现在有一个需求,有一个任务调度器,可以按照不同的任务类型路由到不同的任务执行器。其本质就是通过外部参数进行一次路由和Spring mvc做的事情类似。简单看了Spring mvc的实现原理之后,决定使用自定义注解的方式来实现以上功能。
自定义TaskHandler注解
1
2
3
4
5
6
7
8
|
@Target
({ElementType.TYPE})
@Retention
(RetentionPolicy.RUNTIME)
@Documented
@Component
public
@interface
TaskHandler {
String taskType()
default
""
;
}
|
以上定义了任务处理器的注解,其中@Component表示在spring 启动过程中,会扫描到并且注入到容器中。taskType表示类型。
任务处理器定义
1
2
3
4
5
6
7
8
9
10
|
public
abstract
class
AbstractTaskHandler {
/**
* 任务执行器
*
* @param task 任务
* @return 执行结果
*/
public
abstract
BaseResult execute(Task task);
}
|
以上定义了一个任务执行的处理器,其他所有的具体的任务执行器继承实现这个方法。其中Task表示任务的定义,包括任务Id,执行任务需要的参数等。
任务处理器实现
接下来,我们可以实现一个具体的任务处理器。
1
2
3
4
5
6
7
|
@TaskHandler
(taskType =
"UserNameChanged"
)
public
class
UserNameChangedSender
extends
AbstractTaskHandler {
@Override
public
BaseResult execute(Task task) {
return
new
BaseResult();
}
}
|
以上我们就实现一个用户名修改通知的任务处理器,具体的业务逻辑这里没有实现。
其中:@TaskHandler(taskType = "UserNameChanged"),这里我们指定这个Handler用于处理用户名变更的任务
任务处理Handler注册
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
|
public
class
TaskHandlerRegister
extends
ApplicationObjectSupport {
private
final
static
Map<String, AbstractTaskHandler> TASK_HANDLERS_MAP =
new
HashMap<>();
private
static
final
Logger LOGGER = LoggerFactory.getLogger(TaskHandlerRegister.
class
);
@Override
protected
void
initApplicationContext(ApplicationContext context)
throws
BeansException {
super
.initApplicationContext(context);
Map<String, Object> taskBeanMap = context.getBeansWithAnnotation(TaskHandler.
class
);
taskBeanMap.keySet().forEach(beanName -> {
Object bean = taskBeanMap.get(beanName);
Class clazz = bean.getClass();
if
(bean
instanceof
AbstractTaskHandler && clazz.getAnnotation(TaskHandler.
class
) !=
null
) {
TaskHandler taskHandler = (TaskHandler) clazz.getAnnotation(TaskHandler.
class
);
String taskType = taskHandler.taskType();
if
(TASK_HANDLERS_MAP.keySet().contains(taskType)) {
throw
new
RuntimeException(
"TaskType has Exits. TaskType="
+ taskType);
}
TASK_HANDLERS_MAP.put(taskHandler.taskType(), (AbstractTaskHandler) taskBeanMap.get(beanName));
LOGGER.info(
"Task Handler Register. taskType={},beanName={}"
, taskHandler.taskType(), beanName);
}
});
}
public
static
AbstractTaskHandler getTaskHandler(String taskType) {
return
TASK_HANDLERS_MAP.get(taskType);
}
}
|
这里继承了Spring的ApplicationObjectSupport类,具体的注册过程如下
- Spring完成bean的初始化
- 查找spring的容器中,所有带有TaskHandler注解的bean
- 校验bean是否为AbstractTaskHandler类型,获取到taskType
- 把该bean放到TASK_HANDLERS_MAP容器中,即注册完成
任务执行
接下来我们来看下任务执行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
public
class
TaskExecutor
implements
Job {
private
static
final
String TASK_TYPE =
"taskType"
;
@Override
public
BaseResult execute(Task task){
String taskType=task.getTaskType();
if
(TaskHandlerRegister.getTaskHandler(taskType) ==
null
) {
throw
new
RuntimeException(
"can't find taskHandler,taskType="
+ taskType);
}
AbstractTaskHandler abstractHandler = TaskHandlerRegister.getTaskHandler(taskType);
return
abstractHandler.execute(task);
}
}
|
这里发起任务执行的是一个Job,具体过程如下
- 校验该任务类型,有没有在注册中心注册相关Handler
- 从任务注册中心获取到对应的处理的Handelr
- 执行该Handelr
以上过程就完成了,可以实现基于注解的一个任务路由过程。其实现思路来自于Spring mvc的RequestMapping的设计思路.希望对大家的学习有所帮助,也希望大家多多支持脚本之家。