SpringBoot下使用自定义监听事件的流程分析
作者:枫叶梨花
事件机制是Spring的一个功能,目前我们使用了SpringBoot框架,所以记录下事件机制在SpringBoot框架下的使用,同时实现异步处理。事件机制其实就是使用了观察者模式(发布-订阅模式)。
Spring的事件机制经过如下流程:
- 1、自定义事件,继承org.springframework.context.ApplicationEvent抽象类
- 2、定义事件监听器,实现org.springframework.context.ApplicationListener接口
- 3、在Spring容器中发布事件
SpringBoot的实例程序
实现一个保存用户的时候,向用户提供的邮箱发送一封邮件的功能,同时采用异步处理。
自定义事件
import org.springframework.context.ApplicationEvent; public class EmailEvent extends ApplicationEvent { private static final long serialVersionUID = 3733891603598996786L; private String emailAddress; public EmailEvent(String emailAddress) { super(emailAddress); this.emailAddress = emailAddress; } public String getEmailAddress() { return emailAddress; } public void setEmailAddress(String emailAddress) { this.emailAddress = emailAddress; } }
定义事件监听器
import java.util.concurrent.TimeUnit; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.ApplicationListener; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Component; @Component public class EmailEventListener implements ApplicationListener<EmailEvent> { private static Logger log = LoggerFactory.getLogger(EmailEventListener.class); // 异步处理 @Async @Override public void onApplicationEvent(EmailEvent event) { log.info("监听到事件--邮箱地址:" + event.getEmailAddress()); //模拟处理的耗时3s try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } log.info("事件处理完成"); } }
发布事件
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.stereotype.Component; @Component public class EmailEventPublish { @Autowired private ApplicationContext applicationContext; public void publishEvent(String emailAddress) { EmailEvent event = new EmailEvent(emailAddress); applicationContext.publishEvent(event); } }
调用事件
import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import com.example.demo.event.EmailEventPublish; @RestController public class EventController { private static Logger log = LoggerFactory.getLogger(EventController.class); @Autowired private EmailEventPublish emailEventPublish; @RequestMapping("/event") public void publishEvent(@RequestParam String emailAddress) { // 发布事件 -- 采用异步处理 emailEventPublish.publishEvent(emailAddress); // 正常该语句先执行 log.info("Controller业务处理"); } }
结果
访问如下地址
http://localhost:8080/event?emailAddress=plf@163.com
结果为
2023-08-04 21:21:14.338 INFO 6400 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring FrameworkServlet 'dispatcherServlet'
2023-08-04 21:21:14.338 INFO 6400 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : FrameworkServlet 'dispatcherServlet': initialization started
2023-08-04 21:21:14.370 INFO 6400 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : FrameworkServlet 'dispatcherServlet': initialization completed in 32 ms
2023-08-04 21:21:14.429 INFO 6400 --- [nio-8080-exec-1] .s.a.AnnotationAsyncExecutionInterceptor : No task executor bean found for async processing: no bean of type TaskExecutor and no bean named 'taskExecutor' either
2023-08-04 21:21:14.534 INFO 6400 --- [nio-8080-exec-1] c.e.demo.controller.EventController : Controller业务处理
2023-08-04 21:21:14.535 INFO 6400 --- [cTaskExecutor-1] c.example.demo.event.EmailEventListener : 监听到事件--邮箱地址:plf@163.com
2023-08-04 21:21:17.536 INFO 6400 --- [cTaskExecutor-1] c.example.demo.event.EmailEventListener : 事件处理完成
上述结果可知是实现了异步处理,先打印了事件之后的程序,等时间到再执行监听程序的代码。
实现异步处理就是在监听事件执行业务代码的方法上添加 @Async
注解,同时在启动类上添加 @EnableAsync
即可。
上面的日志还提到了 TaskExecutor
,这是如果有自定义的线程池就会去调用,如果没有就用默认的。我们也可以自己定义一个 TaskExecutor
。
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.lang.Nullable; import org.springframework.scheduling.annotation.AsyncConfigurer; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import java.util.concurrent.Executor; import java.util.concurrent.ThreadPoolExecutor; @EnableAsync @Configuration public class ThreadPool implements AsyncConfigurer { @Nullable @Override @Bean("taskExecutor") public Executor getAsyncExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); // 线程池创建时候初始化的线程数 executor.setCorePoolSize(10); // 线程池最大的线程数,只有在缓冲队列满了之后才会申请超过核心线程数的线程 executor.setMaxPoolSize(20); // 用来缓冲执行任务的队列 executor.setQueueCapacity(200); // 允许线程的空闲时间60秒 executor.setKeepAliveSeconds(60); // 线程池名的前缀 executor.setThreadNamePrefix("taskExecutor-"); // 线程池对拒绝任务的处理策略 executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); executor.initialize(); return executor; } @Nullable @Override public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { return null; } }
结果
2023-08-04 21:27:36.507 INFO 7848 --- [nio-8080-exec-2] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring FrameworkServlet 'dispatcherServlet'
2023-08-04 21:27:36.507 INFO 7848 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet : FrameworkServlet 'dispatcherServlet': initialization started
2023-08-04 21:27:36.537 INFO 7848 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet : FrameworkServlet 'dispatcherServlet': initialization completed in 30 ms
2023-08-04 21:27:36.757 INFO 7848 --- [nio-8080-exec-2] c.e.demo.controller.EventController : Controller业务处理
2023-08-04 21:27:36.757 INFO 7848 --- [ taskExecutor-1] c.example.demo.event.EmailEventListener : 监听到事件--邮箱地址:plf@163.com
2023-08-04 21:27:39.757 INFO 7848 --- [ taskExecutor-1] c.example.demo.event.EmailEventListener : 事件处理完成
可知是使用我们定义的线程池[ taskExecutor-1]
。
总结
Spring的事件机制是一个很实用的一个功能,在监听和异步处理相关的功能比较适合。
到此这篇关于SpringBoot下使用自定义监听事件的文章就介绍到这了,更多相关SpringBoot自定义监听事件内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!