前言
Spring的发布订阅事件,可以用来做成类似流水线制作的形式。一旦信号一发布,流水线就正式启动。当然单个模块也可以独立执行,只不过通过发布订阅模式,这些模块会顺序地执行下去。
以下,打个比方。制作一个玩具,需要先生产零件,然后组合零件,最后进行包装操作,就可以出厂了。
业务代码
首先创建一个抽象父类Toy。
public abstract class Toy {
//顺序执行的关键步骤
public void executeTask(int num){
doTask(num);
if (hasNextTask()){
String nextTask = getNextTask();
Toy bean = SpringUtil.getBean(nextTask);
bean.executeTask(num+1);
}
}
public abstract String doTask(int num);//执行业务逻辑
public abstract boolean hasNextTask();//是否有下一步骤
public abstract String getNextTask();//获得下一步骤
}
接下来,创建接口类。
由于作者较懒,所以将业务层的东西也写在了接口层里面,日常开发接口类还是只做接口,业务方面写在业务层Service里面。
生产接口类
@RestController
@Slf4j
public class ProductController extends Toy{
@RequestMapping("/product/{num}")
public String doTask(@PathVariable int num){
log.info("----------");
log.info("步骤{}:",num);
log.info("生产零部件");
return "生产";
}
public boolean hasNextTask(){
return true;
}
public String getNextTask(){
return "assembleController";
}
}
组合接口类
@RestController
@Slf4j
public class AssembleController extends Toy{
@RequestMapping("/assemble/{num}")
public String doTask(@PathVariable int num){
log.info("----------");
log.info("步骤{}:",num);
log.info("组合零部件");
return "组合";
}
public boolean hasNextTask(){
return true;
}
public String getNextTask(){
return "packController";
}
}
包装接口类
@RestController
@Slf4j
public class PackController extends Toy{
@RequestMapping("/pack/{num}")
public String doTask(@PathVariable int num){
log.info("----------");
log.info("步骤{}:",num);
log.info("包装零部件");
return "包装";
}
public boolean hasNextTask(){
return false; //后面无步骤了
}
public String getNextTask(){
return null;
}
}
以上三个模块,都可以单独执行。接下来,我们使用spring的发布订阅模式来“发信号”。
发布订阅的代码
通过ApplicationContext
的publishEvent
方法来发布事件。
@RestController
@Slf4j
public class TaskPublisher {
@Autowired
private ApplicationContext context;
@RequestMapping("/process/{num}")
public void process(@PathVariable("num") Integer num){
log.info("传递参数:"+num);
context.publishEvent(num);
}
}
通过@EventListener
注解来监听事件。
@Component
@Slf4j
public class TheTaskListener {
@Autowired
private ProductController productController;
@EventListener
public void listen(Integer num){
productController.executeTask(num);
}
}
消息一发布,我们的listen方法就会监听到。
然后执行”玩具生产“环节,也就是先进入抽象父类的executeTask
方法,执行方法中的内容,进行判断后续是否还有步骤。如果有,就执行下一模块。
注意,我们的参数num一直在这条流水线上传递,说明只要在最开始发布事件的时候写好参数,到最后一个模块,也能用到最开始的参数。