业务场景:当用户注册后,发送邮件到其邮箱提示用户进行账号激活。
需要继承 ApplicationEvent:
定义两种事件目的是为下面要说明@EventListener 注解和@TransactionalEventListener 注解的区别。
import org.springframework.context.ApplicationEvent;
public class EventMessage extends ApplicationEvent {
private String content;//内容
public String getContent() { return content; }
public void setContent(String content) { this.content = content; }
public EventMessage(Object source) {
super(source);
}
}
public class EventMessage2 {
private String content;//内容
public String getContent() { return content; }
public void setContent(String content) { this.content = content; }
public EventMessage2() {
}
}
对于 Spring 容器的一些事件,可以监听并且触发相应的方法。通常的方法有 2 种:
1)、ApplicationListener 接口和
2)、@EventListener 注解和@TransactionalEventListener 注解。
ApplicationListener通过监听容器中发布的一些事件event,事件发生就会触发监听器的回调,就完成了事件驱动开发.
ApplicationListener是一个泛型接口,泛型的类型必须是 ApplicationEvent 及其子类,只要实现了这个接口,那么当容器有相应的事件触发时,就能触发 onApplicationEvent 方法。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.EventListener;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.event.TransactionalEventListener;
import org.springframework.transaction.event.TransactionPhase;
@Configuration
public class EventMessageListener
implements ApplicationListener<EventMessage> {
@Autowired
private EventService eventService;
@Override
//@Async("taskExecutor") //异步线程调用
//@Transactional(rollbackFor = Exception.class)
//@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
@TransactionalEventListener(fallbackExecution = true)
public void onApplicationEvent(EventMessage eventMessage) {
System.out.println("onApplicationEvent触发================");
eventService.send(eventMessage);
}
}
Spring内置事件
1、ContextRefreshedEvent
ApplicationContext 被初始化或刷新时,该事件被发布。这也可以在 ConfigurableApplicationContext接口中使用 refresh() 方法来发生。此处的初始化是指:所有的Bean被成功装载,后处理Bean被检测并激活,所有Singleton Bean 被预实例化,ApplicationContext容器已就绪可用
2、ContextStartedEvent
当使用 ConfigurableApplicationContext (ApplicationContext子接口)接口中的 start() 方法启动 ApplicationContext 时,该事件被发布。你可以调查你的数据库,或者你可以在接受到这个事件后重启任何停止的应用程序
3、ContextStoppedEvent
当使用 ConfigurableApplicationContext 接口中的 stop() 停止 ApplicationContext 时,发布这个事件。你可以在接受到这个事件后做必要的清理的工作
4、ContextClosedEvent
当使用 ConfigurableApplicationContext 接口中的 close() 方法关闭 ApplicationContext 时,该事件被发布。一个已关闭的上下文到达生命周期末端;它不能被刷新或重启
5、RequestHandledEvent
这是一个 web-specific 事件,告诉所有 bean HTTP 请求已经被服务。只能应用于使用DispatcherServlet的Web应用。在使用Spring作为前端的MVC控制器时,当Spring处理用户请求结束后,系统会自动触发该事件
1、)使用@EventListener 注解,实现对任意的方法都能监听事件,通过指定 classes,即需要处理的事件类型。 但是事件定义classes必须要继承ApplicationEvent,否则程序会异常。可以设置多项。
2)@TransactionalEventListener 注解的事件无需要继承ApplicationEvent。例如EventMessage2就是独立的bean。
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.EventListener;
import org.springframework.transaction.event.TransactionalEventListener;
@Configuration
public class EventMessageListener2 {
@EventListener(classes={EventMessage.class})
@TransactionalEventListener(fallbackExecution = true)
public void listen(EventMessage event){
System.out.println("EventListener事件触发:"+event.getClass().getName());
}
@TransactionalEventListener(fallbackExecution = true)
public void listen(EventMessage2 event){
System.out.println("TransactionalEventListener事件触发:"+event.getClass().getName());
}
}
@Component
public class EventService {
/**
* 处理业务
* @param eventMessage
*/
public void send(EventMessage eventMessage) {
System.out.println("事件业务逻辑eventMessage content:" + eventMessage.getContent());
}
}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;
@Component
public class EventMessagePublisher {
@Autowired
private ApplicationEventPublisher publisher;
public void send(EventMessage eventMessage) {
publisher.publishEvent(eventMessage);
}
public void send2(EventMessage2 eventMessage) {
publisher.publishEvent(eventMessage);
}
}
@Autowired
EventMessagePublisher eventMessagePublisher;
@RequestMapping("/testevent/")
@ResponseBody
public String testevent() {
EventMessage eventMessage = new EventMessage(this
);
eventMessage.setContent("hello event");
eventMessagePublisher.send(eventMessage);
EventMessage2 EventMessage2 = new EventMessage2();
eventMessagePublisher.send2(EventMessage2);
return "ok";
}
如果我们在事务中完成数据库更新操作后,发布了一个事件EventMessage,如果此时使用的是@EventListener,然后在这个事件EventMessage中,又要对数据进行查询,这时候会发现,查询不到刚才入库的数据,这是因为事务还没提交完成,在同一个事务当中,查询不到才存入的数据,那么就引出了下面的解决方式。
为了解决上述问题,Spring为我们提供了两种方式: (1) @TransactionalEventListener注解。 (2) 事务同步管理器TransactionSynchronizationManager。 以便我们可以在事务提交后再触发某一事件来进行其他操作。
@TransactionalEventListener详解,从命名上直接看出,它就是个EventListener, 在Spring4.2+,通过@TransactionEventListener的方式,能够实现在控制事务的同时,完成对对事件的处理。
我们知道,Spring的事件监听机制(发布订阅模型)实际上并不是异步的(默认情况下),而是同步的来将代码进行解耦。而@TransactionEventListener仍是通过这种方式,但是加入了回调的方式来解决,这样就能够在事务进行Commited,Rollback…等时候才去进行Event的处理,来达到事务同步的目的。
实现方法比较简单,直接在定义的方法上方加@Async()注解就好了。
异步方法可以指定使用某一线程池:如 @Async("taskExecutor"),通过线程池Bean对象名字为taskExecutor来异步执行。