【springboot 事件发布机制源码浅析】

springboot源码分析之事件发布机制浅析

springboot 事件发布机制浅析



前言

事件发布机制在Spring Boot框架中扮演着重要的角色,它是实现模块解耦、扩展和定制以及自动化配置的关键机制之一。通过事件发布机制,开发者可以实现高度可扩展和可定制的应用程序,并更好地利用Spring Boot框架的各种特性和功能。文章主要包含两个部分,一个是如何使用springboot的事件发布,另一个就是分析其运行原理。

事件发布机制也是使用了观察者模式来实现的,如果你没了解过观察者模式建议先了解一下观察者模式


一、自定义事件发布

1.定义事件

自定义实现类,实现ApplicationEvent类

public class UserUpdateEvent extends ApplicationEvent {
   
  public UserUpdateEvent(User updateUser) {
   
    super(updateUser);
  }

  /**
   * 获取事件属性
   * @return
   */
  public User getUser() {
   
    return (User)this.source;
  }
}

2.监听事件

监听事件、执行处理逻辑

方式1:实现ApplicationListener类,需要自己将listener注册到应用中

public class UserUpdateCacheListener implements ApplicationListener<UserUpdateEvent> {
   
  @Override
  public void onApplicationEvent(UserUpdateEvent userUpdateEvent) {
   
    User user = userUpdateEvent.getUser();
    System.out.println(JSON.toJSONString(user));
    System.out.println("获取到最新用户信息 ----更新缓存");
  }
}

启动类中注册

    public static void main(String[] args) throws InterruptedException {
   
        TypeUtils.compatibleWithJavaBean = true;
        SpringApplication app =new SpringApplication(DemoApplication.class);
        app.addListeners(new SpringBootEvnetListenner());//加入自定义的监听类
        app.addListeners(new UserUpdateCacheListener());//加入自定义的监听类
        app.addListeners(new UserUpdateDBListener());//加入自定义的监听类
        app.run(args);
    }

利用SPI机制注册,在resource下面新建META-INF/spring.factories

org.springframework.context.ApplicationListener=\
com.tfzg.program.core.UserUpdateCacheListener

方式2:直接使用@EventListener标记方法,参数为监听的事件

@Configuration
public class EventListenerConfig {
   

  @EventListener
  public void userListener(UserUpdateEvent event){
   
    System.out.println("-------------22----------");
    System.out.println(event.getUser().getName());
  }
}

3.发布事件

    @Autowired
    ApplicationContext applicationContext;
 
    @RequestMapping("/pushEvent")
    public String testPushEvent(){
   
        User user = new User();
        applicationContext.publishEvent(new UserUpdateEvent(user));
        return "s";
    }

二、源码分析

主要从两个方面分析springboot的事件发布机制吧,从监听器注册和事件发布执行这两个方向分析springboot事件发布机制原理。

Listener监听器注册

从springboot启动流程,以及bean的生命周期入手,进行分析Listener注册原理

1.1 SpringApplication 构造方法

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
   
		this.resourceLoader = resourceLoader;
		Assert.notNull(primarySources, "PrimarySources must not be null");
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
		this.webApplicationType = WebApplicationType.deduceFromClasspath();
		this.bootstrapRegistryInitializers = new ArrayList<>(
				getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
		
		//注册Listener的关键入口就在这儿、通过getSpringFactoriesInstances获取到Listener集合,然后set到SpringApplication 的Listeners 属性中
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		
		this.mainApplicationClass = deduceMainApplicationClass();
	}
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
   
		ClassLoader classLoader = getClassLoader();
		// Use names and ensure unique to protect against duplicates
		Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
		List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
		AnnotationAwareOrderComparator.sort(instances);
		return instances;
	}

通过上面的setListeners方法和getSpringFactoriesInstances,可以知道springboot启动的时候,是通过SPI的机制加载spring.factories 文件中配置ApplicationListener,去读取到Listener的,因此我们也可以在spring.factories 中添加自定义Listener或者在SpringApplication 类初始化后直接调用addListeners方法进行添加。

1.2 processBean 阶段会处理有@EventListener注解的方法,对方法解析生成一个ApplicationListenerMethodAdapter Listener


private void processBean(final String beanName, final Class<?> targetType) {
   
		if (!this.nonAnnotatedClasses.contains(targetType) &&
				AnnotationUtils.isCandidateClass(targetType, EventListener.class) &&
				!isSpringContainerClass(targetType)) {
   

			Map<Method, EventListener> annotatedMethods = null;
			try {
   
				annotatedMethods = MethodIntrospector.selectMethods(targetType,
						(MethodIntrospector.MetadataLookup<EventListener>) method ->
								AnnotatedElementUtils.findMergedAnnotation(method, EventListener.class));
			}
			catch (Throwable ex) {
   
				// An unresolvable type in a method signature, probably from a lazy bean - let's ignore it.
				if (logger.isDebugEnabled()) {
   
					logger.debug("Could not resolve methods for bean with name '" + beanName + "'", ex);
				}
			}

			if (CollectionUtils.isEmpty(annotatedMethods)) {
   
				this.nonAnnotatedClasses.add(targetType);
				if (logger.isTraceEnabled()) {
   
					logger.trace("No @EventListener annotations found on bean class: " + targetType.getName());
				}
			}
			else {
   
				// Non-empty set of methods
				ConfigurableApplicationContext context = this.applicationContext;
				Assert.state(context != null, "No ApplicationContext set");
				List<EventListenerFactory> factories = this.eventListenerFactories;
				Assert.state(factories != null, "EventListenerFactory List not initialized");
				for (Method method : annotatedMethods.keySet()) {
   
					for (EventListenerFactory factory : factories) {
   
						if (factory.supportsMethod(method)) {
   
							Method methodToUse = AopUtils.selectInvocableMethod(method, context.getType(beanName));
							// 关键代码,包装成了Listener
							ApplicationListener<?> applicationListener =
									factory.createApplicationListener(beanName, targetType, methodToUse);
							if (applicationListener instanceof ApplicationListenerMethodAdapter) {
   
								((ApplicationListenerMethodAdapter) applicationListener).init(context, this.evaluator);
							}
							// 将Listener 加入Listener中
							context.addApplicationListener(applicationListener);
							break;
						}
					}
				}
				if (logger.isDebugEnabled()) {
   
					logger.debug(annotatedMethods.size() + " @EventListener methods processed on bean '" +
							beanName + "': " + annotatedMethods);
				}
			}
		}
	}


事件发布与执行

通过applicationContext.publishEvent方法入手,进入关键代码AbstractApplicationEventMulticaster.multicastEvent


public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
   
		ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
		Executor executor = getTaskExecutor();
		//过滤Listener,遍历执行监听器方法
		for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
   
			if (executor != null) {
   
				executor.execute(() -> invokeListener(listener, event));
			}
			else {
   
				//执行 Listener onApplicationEvent 方法
				invokeListener(listener, event);
			}
		}
	}

根据事件类型,将指定Listener从所有Listener中获取出来,然后再遍历过滤出来的Listener,再遍历执行onApplicationEvent方法。


总结

本篇文章主要展示了springboot事件发布机制的使用,以及运行原理的关键代码。
(如果大家觉得有帮助,帮忙点点赞啦!!!!!!!!!!!!!!!!!!!)

相关推荐

  1. springboot 事件发布机制浅析

    2024-01-01 09:40:02       37 阅读
  2. springboot事件发布机制之生产运用

    2024-01-01 09:40:02       8 阅读
  3. SpringBoot事务注册原理

    2024-01-01 09:40:02       8 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-01-01 09:40:02       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-01-01 09:40:02       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-01-01 09:40:02       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-01-01 09:40:02       18 阅读

热门阅读

  1. 10-2 HNCST - 多线程4 - 线程同步Condition——python

    2024-01-01 09:40:02       36 阅读
  2. ajax 下载文件(excel导出)

    2024-01-01 09:40:02       40 阅读
  3. vue怎么跨页面传参

    2024-01-01 09:40:02       43 阅读
  4. 纯前端 文件预览方法汇总

    2024-01-01 09:40:02       45 阅读
  5. 一文详解pyspark常用算子与API

    2024-01-01 09:40:02       46 阅读
  6. 八股文打卡day16——计算机网络(16)

    2024-01-01 09:40:02       31 阅读
  7. 元旦假期的第二天:干家务

    2024-01-01 09:40:02       32 阅读
  8. git常用命令详解

    2024-01-01 09:40:02       34 阅读
  9. Debian安装k8s记录

    2024-01-01 09:40:02       31 阅读