扩展:Spring中的观察者模式(事件监听机制)

Spring观察者模式

1、ApplicationContext事件机制是观察者设计模式的具体实现,通过ApplicationEvent类和ApplicationListener接口,可以实现ApplicationContext事件处理。
2、如果容器中由一个ApplicationListener Bean,当ApplicationContext发布ApplicationEvent时,ApplicationListener Bean会自动触发,这种事件机制需要程序显示的触发,
3、其中Spring有一些内置的事件,当完成某种操作会发出某些事件动作,例如监听ContextRefreshedEvent事件,当所有Bean促使和完成并被成功装载会触发该事件,实现ApplicationListener<‘ContextRefreshedEvent’>接口可以收到监听动作,接着实现自己的业务逻辑,事件Event也可以自己自定义、监听Listener也可以自定义,根据业务逻辑按需处理。



一、对象说明

1、ApplicationContext容器对象
2、ApplicationEvent事件对象
3、ContextRefreshedEvent容器刷新事件
4、ApplicationListener事件监听对象

二、事件模式实体概念

1.事件源:事件的触发的地方,比如我在容器加载的时候需要触发加载数据字典实现缓存预热。
2.事件:描述发生了什么事情的对象,比如上面的:加载数据字典数据
3.事件监听器:监听到事件发生的时候,做一些处理,比如更新缓存实现缓存预热


三、本文场景:容器启动的时候装填缓存数据(加载数据字典)

某个零售电商系统,mysql数据库中有储存系统内设置好的所有省市区信息,现在要将这部分地址数据查询出来放到redis做缓存(缓存预热),方便用户购买的时候自动级联填写快递省市区地址,为了避免直接写到监听的时候对象还未加载完毕,导致缓存预热失败,打算从ContextRefreshedEvent等容器加载完毕的时候进行缓存预热,可以进行以下处理:

1.创建监听器CountryDirectoryEventListener

CountryDirectoryEventListener必须实现ApplicationListener类并指定监听事件ContextRefreshedEvent,如果不指明监听类型则会监听所有事件,因为所有事件继承ApplicationContextEvent类

代码如下(示例):

@Component
public class CountryDirectoryEventListener implements ApplicationListener<ContextRefreshedEvent> {
    
    

    /**
     * 监听发生指定事件后执行
     * @param event
     */
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
    
    
        ApplicationContext applicationContext = event.getApplicationContext();
        System.out.println("---查询数据库省市区数据---");
        System.out.println("---填充到redis缓存---");
        System.out.println("---数据预热结束---");
    }
}

2.运行截图

这里只是打印输出,具体实现思路大致如此
在这里插入图片描述

MyApplicationEventListener类没有指定具体的监听器,则所有事件都会监听到
在这里插入图片描述

思考问题:事件的发布与监听是一个同步的过程是同步的?

是的,事件的发布与监听是一个同步的过程,一般缓存预热这里可以使用异步的方法实现,通过在监听方法前使用@Async,并且工程要支持异步,springboot工程启动类需要加@EnableAsync注解。下面看看实际效果

1.创建自定义事件类:MessageEvent

必须继承ApplicationEvent,可以支持多参数传递

@Component
public class MessageEventListener implements ApplicationListener<MessageEvent> {
    
       @Override
    public void onApplicationEvent(MessageEvent event) {
    
    
        for (int i = 0 ; i < 10 ; i++){
    
    
            System.out.println("---等待一秒钟---" + System.currentTimeMillis());
            try {
    
    
                Thread.sleep(1000);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
        }
        System.out.println("监听到消息事件");
    }
}

2.创建自定义事件监听类:MessageEventListener

@Component
public class MessageEventListener implements ApplicationListener<MessageEvent> {
    
    
    @Override
    public void onApplicationEvent(MessageEvent event) {
    
    
        System.out.println("监听到消息事件");
    }
}

这里偷懒不写接口调用方法,直接在刚刚缓存预热的监听方法之前去发布事件MessageEvent看看会不会是同步的。

@Component
public class CountryDirectoryEventListener implements ApplicationListener<ContextRefreshedEvent> {
    
    

    /**
     * 监听发生指定事件后执行
     * @param event
     */
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
    
    

        ApplicationContext applicationContext = event.getApplicationContext();
        applicationContext.publishEvent(new MessageEvent("我来发消息了"));

        System.out.println("---查询数据库省市区数据---");
        System.out.println("---填充到redis缓存---");
        System.out.println("---数据预热结束---");

    }
}

3.运行效果

事件的发布与监听是一个同步的过程
在这里插入图片描述

4.异步执行效果

使用@Async注解

@Component
public class MessageEventListener implements ApplicationListener<MessageEvent> {
    
    

    @Async
    @Override
    public void onApplicationEvent(MessageEvent event) {
    
    
        for (int i = 0 ; i < 10 ; i++){
    
    
            System.out.println("---等待一秒钟---" + System.currentTimeMillis());
            try {
    
    
                Thread.sleep(1000);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
        }
        System.out.println("监听到消息事件");
    }
}

//启动类
@SpringBootApplication
@EnableAsync
public class DemoApplication {
    
    

	public static void main(String[] args) {
    
    
		SpringApplication.run(DemoApplication.class, args);
	}

}

在这里插入图片描述

高级写法@EventListener注解

@Component
public class CommentSyncEventListener {
    
    

    @Async
    @EventListener
    public void sync(CommentSyncEvent event) {
    
    
            logger.error("评论同步失败", e);
        }
    }


总结

Spring的事件机制简便地将我们的代码解耦从而优化我们的代码,方便我们扩展。

猜你喜欢

转载自blog.csdn.net/weixin_42380504/article/details/122768725
今日推荐