初始化消息资源
根据书中的内容介绍,这个消息资源 messageSource
是跟 Spring
国际化相关。
例如中美之间的中英文差别,在不同地区显示不同的资源。对于有国际化需求的系统,要为每种提供一套相应的资源文件,并以规范化命名的形式保存在特定的目录中,由系统自动根据客户端的语言或者配置选择合适的资源文件。
举个🌰:定义了两个资源文件,简单配置如下
- 中文地区:test=测试
- 英文地区:test=test
所以可以通过 Applicationcontext.getMessage()
方法访问国际化信息,在不同的环境中获取对应的数据。
由于个人感觉这种配置相关的,可以通过 profile
切换来实现,所以没有去细看和使用,具体实现和使用请感兴趣的同学们深入了解吧。
事件监听
事件传播器的使用很像我们设计模式中的观察者模式,被观察者变动后通知观察者进行相应的逻辑处理。
在了解 Spring
如何初始化事件传播器之前,来看下 Spring
监听的简单用法。
定义监听事件 Event
新建一个类,继承于 ApplicationEvent
,并且需要在构造方法中调用父类的构造函数 supre(source)
:
public class CarEvent extends ApplicationEvent { /** * 自定义一个消息 */ private String msg; public CarEvent(Object source) { super(source); } public CarEvent(Object source, String msg) { super(source); this.msg = msg; } }
定义监听器 Listener
新建一个类,引用 ApplicationListener
接口,然后重载 onApplicationEvent
方法:
public class CarEventListener implements ApplicationListener { @Override public void onApplicationEvent(ApplicationEvent event) { if (event instanceof CarEvent) { CarEvent carEvent = (CarEvent) event; System.out.println("source : " + event.getSource() + ", custom message : " + carEvent.getMsg()); } } }
由于 Spring
的消息监听器不像 kafka
等主流 MQ
可以指定发送队列或者监听主题,只要发送消息后,所有注册的监听器都会收到消息进行处理,所以这边加了一个判断,如果是我业务上需要的消息,才会进行处理。
配置文件
<bean id="testListener" class="context.event.CarEventListener"/>
将刚才写的监听器注册到 Spring
容器中
测试代码
public class EventBootstrap { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("factory.bean/bean-post-processor.xml"); // 第一个参数是来源,第二个参数是自定义 CarEvent carEvent = new CarEvent("hello", "world"); context.publishEvent(carEvent); // 消息发送之后,打印以下内容 // source : hello, custom message : world } }
由于在配置文件中注册了监听器,然后在启动代码汇总初始化了监听事件,最终通过 context
发送消息,发现输出结果与预想的一致。
这种观察者模式实现很经典,使用起来也很简单,下面来结合源码分析一下 Spring
是如何实现消息监听的功能。
消息监听代码分析
从源码中分析,发现主要是下面三个步骤:
初始化 ApplicationEvenMulticaster
protected void initApplicationEventMulticaster() { ConfigurableListableBeanFactory beanFactory = getBeanFactory(); // 如有有自己注册class Name 是 applicationEventMulticaster,使用自定义广播器 if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) { this.applicationEventMulticaster = beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class); } } else { // 没有自定义,使用默认的事件广播器 this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory); beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster); } }
广播器的作用是用来广播消息,在默认的广播器 SimpleApplicationEventMulticaster
类中发现了这个方法 multicastEvent
:
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) { ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event)); Executor executor = getTaskExecutor(); // 遍历注册的消息监听器 for (ApplicationListener<?> listener : getApplicationListeners(event, type)) { if (executor != null) { executor.execute(() -> invokeListener(listener, event)); } else { invokeListener(listener, event); } } }
可以看到,在广播事件时,会遍历所有注册的监听器进行调用 invokeListener
方法,底层调用的是监听器重载的 listener.onApplicationEvent(event)
,所以再次强调一次,如果使用 Spring
自带的事件监听,请在业务处理方判断事件来源,避免处理错误。
注册监听器
在上一步中,已经初始化好了广播器,所以下一步来看下,监听器的注册流程,入口方法如下:
org.springframework.context.support.AbstractApplicationContext#registerListeners
protected void registerListeners() { // 这里是硬编码注册的监听器 for (ApplicationListener<?> listener : getApplicationListeners()) { getApplicationEventMulticaster().addApplicationListener(listener); } // 不要在这里初始化 factoryBean : 我们需要保留所有常规 bean 未初始化,以便让后处理程序应用于它们! // 这一步是配置文件中注册的监听器 String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false); for (String listenerBeanName : listenerBeanNames) { getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName); } // 发布早期的应用程序事件,现在我们终于有了一个多播器=-= Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents; this.earlyApplicationEvents = null; if (earlyEventsToProcess != null) { for (ApplicationEvent earlyEvent : earlyEventsToProcess) { getApplicationEventMulticaster().multicastEvent(earlyEvent); } } }
这一个方法代码不多,也没啥嵌套功能,按照注释顺序将流程梳理了一遍,将我们注册的监听器加入到 applicationEventMulticaster
列表中,等待之后调用。
publishEvent
广播器和监听器都准备好了,剩下的就是发送事件,通知监听器做相应的处理:
org.springframework.context.support.AbstractApplicationContext#publishEvent(java.lang.Object, org.springframework.core.ResolvableType)
核心是这行代码:
getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
通过获取事件广播器,调用 multicastEvent
方法,进行广播事件,这一步前面也介绍过了,不再细说。
总结
这次学习,省略了书中的一些内容,有关属性编辑器、SPEL
语言和初始化非延迟加载等内容,请感兴趣的同学继续深入了解~
我们也能从 Spring
提供的这些扩展功能中学习到,通过预留后处理器,可以在 bean
实例化之前修改配置信息,或者做其他的自定义操作,例如替换占位符、过滤敏感信息等;
也可以通过广播事件,定义事件和监听器,在监听器中实现业务逻辑,由于不是直接调用监听器,而是通过事件广播器进行中转,达到了代码解耦的效果。
所以在之后的代码设计和编写中,在整体设计上,有必要的话,考虑在更高的抽象层要预留扩展功能,然后让子类重载或者实现,实现扩展的功能。
由于个人技术有限,如果有理解不到位或者错误的地方,请留下评论,我会根据朋友们的建议进行修正
代码和注释都在里面,小伙伴们可以下载我上传的代码,亲测可运行~
Gitee 地址:https://gitee.com/vip-augus/spring-analysis-note.git