Spring-Boot和Spring的监听器模式

最近阅读SpringBoot的源码,发现一个奇怪的现象:项目中有两处地方具有发布事件功能,一个是SpringApplicationRunListener接口;另一个是ApplicationEventPublisher接口的publishEvent方法,ApplicationContext接口实现了该接口。标准监听器模式应该只会有一个地方可以发布事件,这里为什么会有两个呢?

先说结论。

经过仔细分析,发现SpringApplicationRunListener接口是属于SpringBoot包下的,该接口用于发布SpringBoot相关流程的事件;而ApplicationEventPublisher接口属于org.springframework.context包,用于ApplicationContext在refresh的流程中调用,refresh是应用上下文启动的核心方法。

也就是说,SpringApplicationRunListener接口用于监听SpringBoot的启动流程,而ApplicationEventPublisher用于监听Spring应用上下文的启动流程。

那么为什么要使用新的客户端监听SpringBoot的流程呢?

原因是SpringBoot项目是Spring项目的扩展,所以不能够让Spring的代码和SpringBoot的代码产生耦合,为了监听SpringBoot本身的启动流程,于是引入了SpringApplicationRunListener接口。另一个原因是SpringBoot启动过程中,需要用到监听器的地方还没有对ApplicationContext完成refresh,所以也没法使用ApplicationContext的publishEvent功能。

上面就是两个地方会调用到同一个SimpleApplicationEventMulticaster的原因(当然两个地方调用的SimpleApplicationEventMulticaster实例是不同的,以便于分隔监听器)。

1.SpringApplicationRunListener

public interface SpringApplicationRunListener {

	void starting();

	void environmentPrepared(ConfigurableEnvironment environment);

	void contextPrepared(ConfigurableApplicationContext context);

	void contextLoaded(ConfigurableApplicationContext context);

	void finished(ConfigurableApplicationContext context, Throwable exception);

}

该接口唯一的实现类EventPublishingRunListener,调用的SimpleApplicationEventMulticaster进行事件广播,在SpringApplication启动的上面五个流程中,发布不同的事件。

2.AbstractApplicationContext

该类是应用上下文的抽象类,里面的publishEvent方法和监听器有关,这里就不贴代码了,调用的是AbstractApplicationContext的属性applicationEventMulticaster,该属性通过initApplicationEventMulticaster方法进行初始化,实际也是初始化了SimpleApplicationEventMulticaster的实例。下面分析一下广播器相关类。

3.SimpleApplicationEventMulticaster

该实现类继承自AbstractApplicationEventMulticaster,而AbstractApplicationEventMulticaster抽象类实现了ApplicationEventMulticaster接口。

AbstractApplicationEventMulticaster抽象类定义了广播器的规范实现,并且定义了ListenerRetriever内部类,ListenerRetriever将获取监听器的功能独立出来,可以通过多个方式获取监听器,这里是通过applicationListeners属性保存的监听器和applicationListenerBeans保存的监听器beanName来共同获取到监听器。

private class ListenerRetriever {

		public final Set<ApplicationListener<?>> applicationListeners = new LinkedHashSet<ApplicationListener<?>>();

		public final Set<String> applicationListenerBeans = new LinkedHashSet<String>();

		private final boolean preFiltered;

		public ListenerRetriever(boolean preFiltered) {
			this.preFiltered = preFiltered;
		}

		public Collection<ApplicationListener<?>> getApplicationListeners() {
			List<ApplicationListener<?>> allListeners = new ArrayList<ApplicationListener<?>>(
					this.applicationListeners.size() + this.applicationListenerBeans.size());
			allListeners.addAll(this.applicationListeners);
			if (!this.applicationListenerBeans.isEmpty()) {
				BeanFactory beanFactory = getBeanFactory();
				for (String listenerBeanName : this.applicationListenerBeans) {
					try {
						ApplicationListener<?> listener = beanFactory.getBean(listenerBeanName, ApplicationListener.class);
						if (this.preFiltered || !allListeners.contains(listener)) {
							allListeners.add(listener);
						}
					}
					catch (NoSuchBeanDefinitionException ex) {
						// Singleton listener instance (without backing bean definition) disappeared -
						// probably in the middle of the destruction phase
					}
				}
			}
			AnnotationAwareOrderComparator.sort(allListeners);
			return allListeners;
		}
	}
SimpleApplicationEventMulticaster扩展了多线程调用监听器的功能,但如果没有从外部调用setTaskExecutor方法设置线程池,那么默认还是使用的当前线程。
public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {

	private Executor taskExecutor;

	private ErrorHandler errorHandler;

    @Override
	public void multicastEvent(ApplicationEvent event) {
		multicastEvent(event, resolveDefaultEventType(event));
	}

	@Override
	public void multicastEvent(final ApplicationEvent event, ResolvableType eventType) {
		ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
		for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
			Executor executor = getTaskExecutor();
			if (executor != null) {
				executor.execute(new Runnable() {
					@Override
					public void run() {
						invokeListener(listener, event);
					}
				});
			}
			else {
				invokeListener(listener, event);
			}
		}
	}
    protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
    	ErrorHandler errorHandler = getErrorHandler();
		if (errorHandler != null) {
			try {
				doInvokeListener(listener, event);
			}
			catch (Throwable err) {
				errorHandler.handleError(err);
			}
		}
		else {
			doInvokeListener(listener, event);
		}
	}
    private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
		try {
			listener.onApplicationEvent(event);
		}
		catch (ClassCastException ex) {
			String msg = ex.getMessage();
			if (msg == null || matchesClassCastMessage(msg, event.getClass())) {
				Log logger = LogFactory.getLog(getClass());
				if (logger.isDebugEnabled()) {
					logger.debug("Non-matching event type for listener: " + listener, ex);
				}
			}
			else {
				throw ex;
			}
		}
	}
}

4.事件及事件类型

所有实现ApplicationListener接口的监听器,都只能获取到实现ApplicationEvent抽象类的事件,该抽象类保存的信息只有事件的创建时间。另外ApplicationEvent抽象类继承了EventObject普通类,EventObject是java.util包下的,只有一个source属性,表示事件来源。

实现GenericApplicationListener接口的监听器,可以添加是否支持相应ResolvableType

和来源类型的判断。

ResolvableType可以通过ApplicationEvent进行构造,把对应ApplicationEvent的实例放在ResolvableType的resolved属性当中。

    private ResolvableType resolveDefaultEventType(ApplicationEvent event) {
		return ResolvableType.forInstance(event);
	}    
  
    public static ResolvableType forInstance(Object instance) {
		Assert.notNull(instance, "Instance must not be null");
		if (instance instanceof ResolvableTypeProvider) {
			ResolvableType type = ((ResolvableTypeProvider) instance).getResolvableType();
			if (type != null) {
				return type;
			}
		}
		return ResolvableType.forClass(instance.getClass());
	}

    public static ResolvableType forClass(Class<?> clazz) {
		return new ResolvableType(clazz);
	}

    private ResolvableType(Class<?> clazz) {
		this.resolved = (clazz != null ? clazz : Object.class);
		this.type = this.resolved;
		this.typeProvider = null;
		this.variableResolver = null;
		this.componentType = null;
		this.hash = null;
	}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,你的问题是 "rocketmq-spring-boot-parent 2.2.1 手动注册监听"。那么,我来为你解答。 在 RocketMQ 中,可以通过手动注册监听器的方式来实现消息的消费。具体地,在 Spring Boot 中,可以使用 @RocketMQMessageListener 注解来注册 RocketMQ 消息监听器。但是如果你想手动注册监听器,也是可以的。 手动注册监听器需要先创建一个 RocketMQ 的消息监听器,实现 RocketMQListener 接口,并实现 onMessage 方法。然后,在 Spring Boot 中,可以通过创建 SimpleRocketMQListenerContainer 对象来手动注册该监听器。 具体地,可以按照以下步骤来手动注册 RocketMQ 监听器: 1. 创建一个消息监听器,实现 RocketMQListener 接口,并实现 onMessage 方法。 ```java public class MyRocketMQListener implements RocketMQListener<String> { @Override public void onMessage(String message) { System.out.println("Received message: " + message); } } ``` 2. 创建一个 SimpleRocketMQListenerContainer 对象,并设置其属性。 ```java SimpleRocketMQListenerContainer container = new SimpleRocketMQListenerContainer(); container.setConsumerGroup("my-group"); container.setNamesrvAddr("localhost:9876"); container.setTopic("my-topic"); container.setRocketMQListener(new MyRocketMQListener()); ``` 3. 启动监听器容器。 ```java container.start(); ``` 通过以上步骤,就可以手动注册 RocketMQ 消息监听器了。需要注意的是,在手动注册监听器的情况下,需要手动管理监听器容器的生命周期,包括启动和关闭监听器容器。 希望这个回答能够帮助到你,如果还有什么问题,可以继续提出来哦。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值