在软件开发过程中,有时候我们会遇到需要把具体业务解耦出来,让各个模块只负责相应的处理逻辑.比如说,在我们的抽象业务逻辑中有一个负责开往目的地的司机driver,现在我们的业务系统中不仅要处理司机去往目的地的逻辑,也有其他业务模块需要关注司机具体的目的地是什么地方。然而司机只负责把乘客送到具体地方。不需要负责告诉业务他去哪里。这时候我们可以采用spring的事件机制,让业务解耦。
spring的事件机制包括:
事件源,对应具体的事件。
事件监听器,负责接收具体的事件源。
事件广播器, 负责发布事件源。
相应的处理逻辑为:事件监听器会注册具体的事件源,当事件广播器广播具体的事件源后,事件监听器能接到该事件的发布消息,然后处理相关事件。
事件源
定义事件源需要继承spring的ApplicationEvent类,该类有两子类ApplicationContextEvent,RequestHandledEvent.
ApplicationContextEvent有4个子类,ContextStartedEvent,ContextRefreshedEvent,ContextClosedEvent,ContextStoppedEvent,分别表示容器启动,刷新,关闭,停止的事件。
RequestHandledEvent只有定义了DispatcherServlet时才会产生该事件,它的子类ServletRequestHandledEvent代表Servlet请求事件。
这里选择定义一个司机行驶的事件类:DriverEvent,它继承于ApplicationContextEvent。它有一个目的地属性destination,然后这里需要实现它的构造函数,提供一个spring的上下文。
package spring;
import lombok.Data;
import org.springframework.context.ApplicationContext;
import org.springframework.context.event.ApplicationContextEvent;
@Data
public class DriveEvent extends ApplicationContextEvent {
private String destination;
public DriveEvent(ApplicationContext source, String destination) {
super(source);
this.destination = destination;
}
}
事件监听器
定于事件监听器需要实现ApplicationListener接口,我们定义一个DriveEventListener事件监听器,通过泛型注册了它关注的事件DriveEvent,然后实现接口的onApplicationEvent(DriveEvent event),这里我们把事件的目的地打印出来。
package spring;
import org.springframework.context.ApplicationListener;
public class DriveEventListener implements ApplicationListener<DriveEvent> {
@Override
public void onApplicationEvent(DriveEvent event) {
System.out.println("本次哈哈列车开往目的地:" + event.getDestination());
}
}
事件广播器
接下来就需要定义事件广播器了,事件的广播器其实是通过spring的上下文ApplicationContext调用它的publishEvent(ApplicationEvent event)方法的,参数就是具体的继承于ApplicationEvent的事件源,这里我们定义一个Driver类,该类负责具体驱车前往目的地,然后发布一个DriveEvent。
package spring;
import org.springframework.context.ApplicationContext;
public class Driver {
private ApplicationContext applicationContext = SpingContextHolder.getApplicationContext();
public void drive(String destionation) {
System.out.println("司机开车前往目的地");
DriveEvent driveEvent = new DriveEvent(applicationContext, destionation);
applicationContext.publishEvent(driveEvent);
}
}
这里可以看到Driver有一个drive(String desctination)方法,它负责具体的驱车逻辑,然后他会通过springcontext发布一个驱车事件,出于方便,我们再定义一个SpingContextHolder来获取spring的上下文。
在程序中获取spring上下文可以通过指定具体的xml文件,也可以通过实现ApplicationContextAware 接口,然后实现它的setApplicationContext(ApplicationContext ctx)方法,spring会传递当先程序的上下文,我们给该类定义一个静态的上下文变量context,把spring的上下文分配给它。代码如下:
package spring;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
public class SpingContextHolder implements ApplicationContextAware {
private static ApplicationContext context;
@Override
public void setApplicationContext(ApplicationContext ctx) throws BeansException {
context = ctx;
}
public static ApplicationContext getApplicationContext(){
return context;
}
}
启动类
现在相应的事件机制组件都已经定义好了,我们这里再定义一个启动类。
启动类的逻辑流程为:我们在spring的配置文件context.xml中装配一个司机组件driver;程序启动,通过ClassPathXmlApplicationContext来获取司机的bean,司机driver调用drive方法驱车前往目的地。这时候自己的drive方法会发布DriveEvent事件,DriveEventListner会监听到然后打印出司机前往的目的地。这里司机只是在drive方法中发布一个驱车事件,然后驱车前往目的地,具体的处理逻辑通过事件机制交给了监听器处理逻辑,达到了程序解耦的效果。
package spring;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-context.xml");
Driver driver = (Driver) applicationContext.getBean("driver");
driver.drive("上海浦东");
}
}
spring配置文件:
<bean id="spingContextHolder" class="spring.SpingContextHolder"/>
<bean id="driver" class="spring.Driver"/>
<bean id="driveListener" class="spring.DriveEventListener"/>
总结
我们通过继承ApplicationEvent类来定义具体的事件,然后实现ApplicationListener来注册具体的事件以及相应的处理逻辑,事件的广播是通过spring的上下文ApplicationContext调用它的publishEvent(ApplicationEvent event)方法来实现的。当事件广播器广播具体的事件源后,事件监听器能接到该事件的发布消息,然后处理相关事件。