前言
在spring框架中,提供了很多动态灵活且可扩展的机制,开发者可以利用这些机制完成一些巧妙的业务,实现一些业务中的解耦,
引导案例
下面看一个简单的案例,
@Configuration
public class SelfBusiness {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SelfBusiness.class);
context.getBean(MyService.class).doBusiness();
context.close();
}
@Component
static class MyService {
private static final Logger logger = LoggerFactory.getLogger(MyService.class);
@Autowired
private ApplicationEventPublisher publisher;
public void doBusiness (){
logger.debug("主线业务");
logger.debug("发送短信");
logger.debug("发送邮件");
}
}
运行上面的代码,观察效果
结合输出结果,这这段代码要实现的逻辑是,在主线业务执行完成后,需要执行发短信,发邮件等操作,这样写也没毛病,但不够优雅,从后续的业务可扩展性上来讲,不够友好,如果后续主线业务执行完毕,还需再增加一个其他的审计操作,则需要新增代码逻辑,这就将主线业务和支线逻辑紧密的耦合了起来;
就是说,我们期待的效果是,主线业务根本不关心其他的业务操作,只需要完成自身的逻辑就ok了,这就需要使用到spring提供的事件监听器功能;
使用事件监听器改造过程
springboot(spring)的事件监听器使用主要有两种方式,通过实现ApplicationListener接口,另一个就是在类上添加 @EventListener 注解来实现,接下来将对这两种方式逐一说明;
一、通过实现ApplicationListener接口实现步骤
1、自定义一个事件类(对象),继承ApplicationEvent
static class MyEvent extends ApplicationEvent {
public MyEvent(Object source) {
super(source);
}
}
可以这么理解,在代码中,可能有很多种类型的事件,不同的业务对应着不同的事件,对于某个具体的监听器来说,它只想监听A这种类型的事件;
2、自定义业务类实现ApplicationListener 接口
@Data
static class Params {
private String id ;
private String name;
private String phone;
}
@Component
static class SmsApplicationListener implements ApplicationListener<MyEvent> {
private static final Logger logger = LoggerFactory.getLogger(SmsApplicationListener.class);
@Override
public void onApplicationEvent(MyEvent myEvent) {
Object source = myEvent.getSource();
try {
Params params = objectMapper.readValue(source.toString(), Params.class);
logger.debug("userId : {}",params.getId());
} catch (JsonProcessingException e) {
e.printStackTrace();
}
logger.debug("执行 sms 发短信业务");
}
}
@Component
static class EmailApplicationListener implements ApplicationListener<MyEvent> {
private static final Logger logger = LoggerFactory.getLogger(SmsApplicationListener.class);
@Override
public void onApplicationEvent(MyEvent myEvent) {
Object source = myEvent.getSource();
logger.debug("执行 email 发邮件业务");
}
}
显然,这里的监听器要监听的事件类型,正是上面我们定义的MyEvent ,这样,当业务被触发的时候,就可以在onApplicationEvent中拿到传递过来的参数,从而执行发短信(发邮件)业务操作了
3、主线业务发布事件
@Component
static class MyService {
private static final Logger logger = LoggerFactory.getLogger(MyService.class);
@Autowired
private ApplicationEventPublisher publisher;
public void doBusiness (){
Params params = new Params();
params.setId("001");
params.setName("xiaoma");
params.setPhone("133******");
logger.debug("主线业务");
try {
publisher.publishEvent(new MyEvent(objectMapper.writeValueAsString(params)));
} catch (JsonProcessingException e) {
e.printStackTrace();
}
}
}
对主线业务来说,这时候就不再需要写发送短信或邮件逻辑了,只需要一个publisher将事件发布出去即可,如果需要传递参数,将参数一起传递过去
完整的代码
@Configuration
public class SelfBusiness {
private static ObjectMapper objectMapper = new ObjectMapper();
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SelfBusiness.class);
context.getBean(MyService.class).doBusiness();
context.close();
}
@Data
static class Params {
private String id ;
private String name;
private String phone;
}
static class MyEvent extends ApplicationEvent {
public MyEvent(Object source) {
super(source);
}
}
@Component
static class MyService {
private static final Logger logger = LoggerFactory.getLogger(MyService.class);
@Autowired
private ApplicationEventPublisher publisher;
public void doBusiness (){
Params params = new Params();
params.setId("001");
params.setName("xiaoma");
params.setPhone("133******");
logger.debug("主线业务");
try {
publisher.publishEvent(new MyEvent(objectMapper.writeValueAsString(params)));
} catch (JsonProcessingException e) {
e.printStackTrace();
}
}
}
@Component
static class SmsApplicationListener implements ApplicationListener<MyEvent> {
private static final Logger logger = LoggerFactory.getLogger(SmsApplicationListener.class);
@Override
public void onApplicationEvent(MyEvent myEvent) {
Object source = myEvent.getSource();
try {
Params params = objectMapper.readValue(source.toString(), Params.class);
logger.debug("userId : {}",params.getId());
} catch (JsonProcessingException e) {
e.printStackTrace();
}
logger.debug("执行 sms 发短信业务");
}
}
@Component
static class EmailApplicationListener implements ApplicationListener<MyEvent> {
private static final Logger logger = LoggerFactory.getLogger(SmsApplicationListener.class);
@Override
public void onApplicationEvent(MyEvent myEvent) {
Object source = myEvent.getSource();
logger.debug("执行 email 发邮件业务");
}
}
}
再次运行上面的代码,观察效果,可以看到,仍然能满足预期的效果
二、通过添加 @EventListener 注解来实现
这种方式不再需要实现ApplicationListener 接口,而是直接在监听类的方法上面添加 @EventListener注解即可,相对要简化了一些,下面直接贴出完整的代码
package com.congge.config;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
@Configuration
public class SelfBusiness2 {
private static ObjectMapper objectMapper = new ObjectMapper();
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SelfBusiness2.class);
context.getBean(MyService.class).doBusiness();
context.close();
}
@Data
static class Params {
private String id ;
private String name;
private String phone;
}
static class MyEvent extends ApplicationEvent {
public MyEvent(Object source) {
super(source);
}
}
@Component
static class MyService {
private static final Logger logger = LoggerFactory.getLogger(MyService.class);
@Autowired
private ApplicationEventPublisher publisher;
public void doBusiness (){
Params params = new Params();
params.setId("001");
params.setName("xiaoma");
params.setPhone("133******");
logger.debug("主线业务");
try {
publisher.publishEvent(new MyEvent(objectMapper.writeValueAsString(params)));
} catch (JsonProcessingException e) {
e.printStackTrace();
}
}
}
@Component
static class SmsListenerService {
private static final Logger logger = LoggerFactory.getLogger(SmsListenerService.class);
@EventListener
public void smsListener(MyEvent myEvent){
Object source = myEvent.getSource();
try {
SelfBusiness2.Params params = objectMapper.readValue(source.toString(), SelfBusiness2.Params.class);
logger.debug("userId : {}",params.getId());
} catch (JsonProcessingException e) {
e.printStackTrace();
}
logger.debug("执行 sms 发短信业务");
}
}
@Component
static class EmailListenerService {
private static final Logger logger = LoggerFactory.getLogger(EmailListenerService.class);
@EventListener
public void emailListener(MyEvent myEvent){
Object source = myEvent.getSource();
try {
SelfBusiness2.Params params = objectMapper.readValue(source.toString(), SelfBusiness2.Params.class);
logger.debug("userId : {}",params.getId());
} catch (JsonProcessingException e) {
e.printStackTrace();
}
logger.debug("执行 email 发邮件业务");
}
}
}
运行上面的代码,观察效果,同样可以达到预期的效果
三、使用异步
更进一步来说,为了提升主线业务的逻辑执行效率,我们希望发布事件的业务逻辑异步执行,这个该如何做呢?
翻阅源码可以知道,ApplicationEventPublisher 默认发布事件时候采用单线程同步发送,如果需要使用异步,需要自定义 ThreadPoolTaskExecutor ,以及SimpleApplicationEventMulticaster ,因此我们只需要覆盖一下这两个组件的bean即可,在上面的业务类中将下面的这两个bean添加进去;
@Bean
public ThreadPoolTaskExecutor executor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(100);
return executor;
}
@Bean
public SimpleApplicationEventMulticaster applicationEventMulticaster(ThreadPoolTaskExecutor executor) {
SimpleApplicationEventMulticaster eventMulticaster = new SimpleApplicationEventMulticaster();
eventMulticaster.setTaskExecutor(executor);
return eventMulticaster;
}
这时候再次运行代码,反复运行多次,就可以看到效果
对比下上面单线程效果
自定义事件发布器
|