@EventListener也有坑,你用对了吗???

759 阅读4分钟

1.观察者模式

观察者模式是软件设计模式的一种。在此种模式中,一个目标对象管理所有相依于它的观察者对象,并且在它本身的状态改变时主动发出通知。

1.为什么用观察者模式?

解耦。聚焦在自己的业务逻辑,不用管其他后续的逻辑。后续有新的业务逻辑直接添加即可,不用修改之前的代码。例如,停电不用挨家挨户告诉停电了,需要买蜡烛。只要把停电的消息发送出去,后续别人是买蜡烛还是手电筒就不用管了。

2.主题与观察者的关系

主题:存储观察者,调用观察者的行为

观察者:实现自己的行为

3.自己实现观察者模式

//抽象的主题
public interface ISubject {
    //添加观察者
    void addObserver(IObserver iObserver);
    //删除观察者
    void deleteObserver(IObserver iObserver);
    //发布事件
    void publish(Object o);
}

//具体的主题
public class ConcertSubject implements ISubject{
    List<IObserver> list = new ArrayList<>();

    @Override
    public void addObserver(IObserver iObserver) {
        list.add(iObserver);
    }

    @Override
    public void deleteObserver(IObserver iObserver) {
        list.remove(iObserver);
    }

    @Override
    public void publish(Object event) {
        for (IObserver iObserver : list) {
            iObserver.update(event);
        }
    }
}


//抽象的观察者
public interface IObserver {
     //观察者的行为
     void update(Object tip);
}

//具体的观察者
public class ConcertObserver implements IObserver{
    private String name;

    public ConcertObserver(String name) {
        this.name = name;
    }

    @Override
    public void update(Object tip) {
        System.out.println(name + tip.toString());
    }
}


//被观察者发送的事件
public class Article {
    private String author;
    private String title;

    public Article(String author, String title) {
        this.author = author;
        this.title = title;
    }

    public String getAuthor() {
        return author;
    }

    public String getTitle() {
        return title;
    }

    @Override
    public String toString() {
        return "收到了" + author + "发送的标题为‘" + title + "’的文章";
    }
}


//主类运行
public class Test {
    public static void main(String[] args) {
        ISubject iSubject = new ConcertSubject();

        IObserver reader1 = new ConcertObserver("张三");
        IObserver reader2 = new ConcertObserver("李四");

        iSubject.addObserver(reader1);
        iSubject.addObserver(reader2);

        iSubject.publish(new Article("bluesky","观察者模式"));
    }
}

image.png

4.Java中内置类可以直接使用(Observer和Observable),将以上形式的代码进行了封装

//被观察者
public class Uploader extends Observable {

    private String name;

    public Uploader(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    //object是向观察者发送的参数对象
    public void publish(Object object) {
        //changed变量置为true
        setChanged();
         //changed变量为true时,通知观察者
        notifyObservers(object);
    }
}


//观察者
public class Subscriber implements Observer {
    private String name;

    public Subscriber(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    @Override
    //o是被观察对象,arg是被观察者发送的参数对象
    public void update(Observable o, Object arg) {
        Uploader up = (Uploader) o;
        Vlog vlog = (Vlog) arg;
        System.out.println(name + "关注的up:" + up.getName() + "更新了vlog'" + vlog.getName() + "'");
    }
}


//被观察者发送的事件
public class Vlog {
    private String name;

    public Vlog(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}


//主类
public class Test {
    public static void main(String[] args) {
        Uploader uploader = new Uploader("bluesky");

        Subscriber fan1 = new Subscriber("fan1");
        Subscriber fan2 = new Subscriber("fan2");

        uploader.addObserver(fan1);
        uploader.addObserver(fan2);
        Vlog vlog = new Vlog("观察者模式");
        uploader.publish(vlog);

    }
}

image.png

5.SpringBoot中的观察者模式

//要发送的事件继承ApplicationEvent
public class MessageEvent extends ApplicationEvent {
    private String name;

    public MessageEvent(Object source, String name) {
        super(source);
        this.name = name;
    }
    public String getName() {
        return name;
    }
}

@Slf4j
@Component
public class MessagePublisher {
    @Autowired
    //使用该类发送消息
    ApplicationEventPublisher publisher;

    public void publishListener(MessageEvent messageEvent){

        log.info("发送消息");
        publisher.publishEvent(messageEvent);
    }
}


@Slf4j
@Component
//监听消息的类实现ApplicationListener<T>,T为消息实体
public class MessageListener implements ApplicationListener<MessageEvent> {
    @Override
    public void onApplicationEvent(MessageEvent event) {
        log.info("监听到的名字:"+event.getName());
    }
}

image.png

SpringBoot中的观察者形式体现在哪?

public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {

    public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
        ResolvableType type = eventType != null ? eventType : this.resolveDefaultEventType(event);
        Executor executor = this.getTaskExecutor();
        //获取listener方法
        Iterator var5 = this.getApplicationListeners(event, type).iterator();

        while(var5.hasNext()) {
            ApplicationListener<?> listener = (ApplicationListener)var5.next();
            if (executor != null) {
                executor.execute(() -> {
                    //线程池中异步执行
                    this.invokeListener(listener, event);
                });
            } else {
                this.invokeListener(listener, event);
            }
        }

    }

    protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
        //监听出现异常处理
				ErrorHandler errorHandler = this.getErrorHandler();
        if (errorHandler != null) {
            try {
                this.doInvokeListener(listener, event);
            } catch (Throwable var5) {
                errorHandler.handleError(var5);
            }
        } else {
            this.doInvokeListener(listener, event);
        }

    }

    private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
        try {
            //执行listener.onApplicationEvent()方法
            listener.onApplicationEvent(event);
        } catch (ClassCastException var6) {
            String msg = var6.getMessage();
            if (msg != null && !this.matchesClassCastMessage(msg, event.getClass()) && (!(event instanceof PayloadApplicationEvent) || !this.matchesClassCastMessage(msg, ((PayloadApplicationEvent)event).getPayload().getClass()))) {
                throw var6;
            }

            Log loggerToUse = this.lazyLogger;
            if (loggerToUse == null) {
                loggerToUse = LogFactory.getLog(this.getClass());
                this.lazyLogger = loggerToUse;
            }

            if (loggerToUse.isTraceEnabled()) {
                loggerToUse.trace("Non-matching event type for listener: " + listener, var6);
            }
        }
    }
}

上述代码只是做到了同步解耦,大多数时候我们想要做异步解耦。对SpringBoot形式代码改造成异步解耦方式。

@Configuration
@Slf4j
public class EventConfig {
    @Bean("applicationEventMulticaster")
    public SimpleApplicationEventMulticaster customApplicationEventMulticaster(ConfigurableListableBeanFactory beanFactory, TaskExecutor taskExecutor) {
        SimpleApplicationEventMulticaster simpleApplicationEventMulticaster = new SimpleApplicationEventMulticaster();
        simpleApplicationEventMulticaster.setBeanFactory(beanFactory);
        simpleApplicationEventMulticaster.setTaskExecutor(taskExecutor);
        ErrorHandler errorHandler = new ErrorHandler() {
            @Override
            public void handleError(Throwable t) {
                log.info("监听方法中出现了异常");
            }
        };
        simpleApplicationEventMulticaster.setErrorHandler(errorHandler);
        return simpleApplicationEventMulticaster;
    }
}
  1. 配置了线程池,出现错误其他监听器正常执行
  2. 未配置线程池,配置ErrorHandler,出现错误打印日志,其他监听器正常执行。相当于进行了try cache
  3. 未配置线程池,未配置ErrorHandler,出现错误日志,直接崩,其他监听器不会正常执行

类加上@Async注解并配置线程池即可,可以实现发送消息的异步,不配置线程池,执行任务仍然是同步执行。

线程池中执行任务出现异常不会影响其他任务。

@Slf4j
@Component
public class MessageListener implements ApplicationListener<MessageEvent> {
    @Async
    @Override
    public void onApplicationEvent(MessageEvent event) {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        log.info("MessageListener监听到的名字:"+event.getName());
    }
}


@Component
@Slf4j
public class MessageListener1 implements ApplicationListener<MessageEvent> {
    @Async
    @Override
    public void onApplicationEvent(MessageEvent event) {
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        log.info("MessageListener1监听到的名字:"+event.getName());
    }
}

使用@EventListener实现上述的内容

]()``` public class MessageEvent { private String name;

public MessageEvent(String name) {
    this.name = name;
}
public String getName() {
    return name;
}

}

@Slf4j @Component public class MessagePublisher { @Autowired //使用该类发送消息 ApplicationEventPublisher publisher;

public void publishListener(MessageEvent messageEvent){

    log.info("发送消息");
    publisher.publishEvent(messageEvent);
}

}

@Slf4j @Component public class MessageListener {

@Async
@EventListener(value = MessageEvent.class)
public void listener(MessageEvent event){
    log.info("MessageListener监听到的名字:"+event.getName());
}

}

@Slf4j @Component public class MessageListener1 { @Async @EventListener(value = MessageEvent.class) public void listener(MessageEvent event){ log.info("MessageListener1监听到的名字:"+event.getName()); } }

![图片转存失败,建议将图片保存下来直接上传
        

 image.png(https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a21ba4a49d3c4f7a99eafc05a5a856f7~tplv-k3u1fbpfcp-watermark.image?)

# 3.使用@EventListener可能会存在的问题,即无法读取到刚刚插入的数据

问题复现过程:业务逻辑实现需要,在一个有事务的方法中,插入一条数据,需要使用异步监听的方式查询这条数据

]()```
@Slf4j
@Service
public class MessageProblemImpl {
    @Autowired
    private ApplicationEventPublisher publisher;
    @Autowired
    private UserMapper userMapper;

    @Transactional
    public void insertThenQuery(){
        userMapper.insert(new User(1L, "小明", 18));
        publisher.publishEvent(new MessageEvent("测试插入数据后进行查询"));
        log.info("数据插入成功");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}


@Slf4j
@Component
public class MessageListener2 {
    @Autowired
    UserMapper userMapper;
    
    @Async
    @EventListener(value = MessageEvent.class,condition = "#event.name == '测试插入数据后进行查询'")
    public void listener(MessageEvent event){
        log.info("MessageListener1监听到的名字:"+event.getName());
        Integer count = userMapper.selectCount(new LambdaQueryWrapper<>());
        log.info("查询user表中数据的数量:" + count);
    }
}

![图片转存失败,建议将图片保存下来直接上传

image.png(p6-juejin.byteimg.com/tos-cn-i-k3…?)

异步查询的时机在事务之前无法查询到插入的数据。

解决上述问题的方法

@TransactionalEventListener可以通过phase来保证事务提交之后在进行异步的查询 fallbackExecution 决定无事务是否执行,默认为false,即无事务不执行

]()``` //phase取值范围 public enum TransactionPhase { BEFORE_COMMIT, //事务提交之前 AFTER_COMMIT, //事务提交之后 AFTER_ROLLBACK, //回滚之后 AFTER_COMPLETION; //无论事务提交还是回滚之后进行

private TransactionPhase() {
}

}

@Slf4j @Component public class MessageListener2 { @Autowired UserMapper userMapper; @Async //@EventListener(value = MessageEvent.class,condition = "#event.name == '测试插入数据后进行查询'") @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT, value = MessageEvent.class,condition = "#event.name == '测试插入数据后进行查询'") public void listener(MessageEvent event){ log.info("MessageListener1监听到的名字:"+event.getName()); Integer count = userMapper.selectCount(new LambdaQueryWrapper<>()); log.info("查询user表中数据的数量:" + count); } }

![图片转存失败,建议将图片保存下来直接上传
        成功查询出了刚刚放进去的内容

 image.png(https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/94c0cdddcd7843b6bd10178551035fa3~tplv-k3u1fbpfcp-watermark.image?)

]()```
监听类执行的逻辑
public void onApplicationEvent(ApplicationEvent event) {
    if (TransactionSynchronizationManager.isSynchronizationActive() && TransactionSynchronizationManager.isActualTransactionActive()) {
        //有事务执行该逻辑
        TransactionSynchronizationManager.registerSynchronization(new TransactionalApplicationListenerSynchronization(event, this, this.callbacks));
    } else if (this.annotation.fallbackExecution()) {
        //没有事务,判断fallbackExecution的值
        if (this.annotation.phase() == TransactionPhase.AFTER_ROLLBACK && this.logger.isWarnEnabled()) {
            this.logger.warn("Processing " + event + " as a fallback execution on AFTER_ROLLBACK phase");
        }

        this.processEvent(event);
    } else if (this.logger.isDebugEnabled()) {
        this.logger.debug("No transaction is active - skipping " + event);
    }

}