spring事件监听机制Event 详解
spring事件监听机制Event 详解
一、事件监听的由来
Spring的事件(Application Event)为Bean和Bean之间的消息同步提供了支持。当一个Bean处理完成一个任务之后,希望另外一个Bean知道并能做相应的处理,这时我们就需要让另外一个Bean监听当前Bean所发生的事件
二、如何实现事件
- 我们可以自定义一个事件,继承spring提供的ApplicationEvent
- 定义一个监听器,需要实现 ApplicationListener,监听我们定义的事件
- 使用容器发布事件 applicationContext.publishEvent(ApplicationEvent event)
三、通过实现ApplicationListener同步监听事件
我们可以先自己定义一个事件SpringTestEvent
package com.zeng.spring.event;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.context.event.SpringApplicationEvent;
import org.springframework.context.ApplicationEvent;
public class SpringTestEvent extends ApplicationEvent {
/**
* Create a new {@code ApplicationEvent}.
*
* @param source the object on which the event initially occurred or with
* which the event is associated (never {@code null})
*/
public SpringTestEvent(String source) {
super(source);
}
}
然后定义一个事件监听器,一般我们是在这里做逻辑处理
package com.zeng.spring.listener;
import com.zeng.spring.event.SpringTestEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
@Component
public class SpringTestListener implements ApplicationListener<SpringTestEvent> {
@Override
public void onApplicationEvent(SpringTestEvent event) {
Object source = event.getSource();
System.out.println("SpringTestListener接收到了消息 : "+source);
}
}
使用 ApplicationContext 进行事件的发布
package com.zeng.spring.controller;
import com.zeng.spring.event.SpringTestEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/test")
public class TestDemo {
@Autowired
private ApplicationContext applicationContext;
@GetMapping("/send")
public void sendTestEvent() {
applicationContext.publishEvent(new SpringTestEvent("helloWord"));
}
}
测试结果发现消息已经接收成功了
四、注解的方式实现同步事件监听
我们只需要通过@EventListener这个注解就可以实现
package com.zeng.spring.listener;
import com.zeng.spring.event.SpringTestEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
@Component
public class SpringTestListener01 {
@EventListener(value = SpringTestEvent.class)
public void event(SpringTestEvent springTestEvent) {
System.out.println("SpringTestEvent01接收到的消息"+springTestEvent.getSource());
}
}
可以看到结果了,以及成功监听了消息
五、如何实现异步监听
什么事同步监听呢,举个列子就比如我们洗澡和烧水,同步的意思就是我们必须洗完澡才能进行烧水的,在spring事件发布中,就必须等我们的监听器监听完成代码才可以往下执行。我们可以验证一下。我们可以在消息监听者中设置一个沉睡时间。
package com.zeng.spring.listener;
import com.zeng.spring.event.SpringTestEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
@Component
public class SpringTestListener implements ApplicationListener<SpringTestEvent> {
@Override
public void onApplicationEvent(SpringTestEvent event) {
Object source = event.getSource();
System.out.println("SpringTestListener接收到了消息 : "+source);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
你会发现每次都是等SpringTestListener时间监听器中的代码执行完,代码才会继续往下走流程。每次的结果都是SpringTestListener先完成,针对一些场景这样就比较消耗性能了。所以spring还提供了一个异步的方式。
如何实现呢?我们需要再启动类中添加上@EnableAsync注解开启异步
package com.zeng.spring;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;
@SpringBootApplication
@EnableAsync
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
然后再监听者中添加@Async注解,我们给这里加上一个沉睡时间
package com.zeng.spring.listener;
import com.zeng.spring.event.SpringTestEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
@Component
@Async
public class SpringTestListener implements ApplicationListener<SpringTestEvent> {
@Override
public void onApplicationEvent(SpringTestEvent event) {
Object source = event.getSource();
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("SpringTestListener接收到了消息 : "+source);
}
}
结果发现我们发布时间后代码先执行了。这样就实现了我们异步监听。
六、使用场景
Spring Event 的好处在于,通过事件的发布和监听机制,能够将不同业务之间的耦合度降到最低,这些业务之间并不是直接的调用关系,而是通过事件这种松耦合的方式进行交互,这样业务之间的耦合度就会变得更加灵活和可扩展。
- 可用于收集日志
- 对外api的调用
- 发送邮件信息等