【Spring框架】Spring监听器的源码分析

news2025/7/8 2:51:34

目录

一、Spring监听器模型

二、源码分析

2.1 initApplicationEventMulticaster():事件广播器的初始化

2.1.1 Spring默认的事件广播器SimpleApplicationEventMulticaste

2.2 registerListeners():注册事件监听器

2.3 finishRefresh():完成refresh后发布相应的内置事件

2.3.1 publishEvent(ApplicationEvent)方法:用于发布事件

2.3.1.1 multicastEvent()方法

2.3.1.1.1 getApplicationListeners(event,type)方法:获取所有监听event事件的监听器

2.3.1.1.1.1 retrieveApplicationListeners()

2.3.1.1.1.1.1 supportsEvent(listener,eventType) :判断当前监听器是否用来监听该事件

2.3.1.1.2 invokeListener()方法:执行监听器的监听逻辑

三、同步与异步

3.1 默认同步通知

3.2 异步通知设置

3.2.1 第一种,直接自定义一个多播器,然后注册到容器中顶替掉Spring自动创建的多播器

3.2.2 第二种,为现成的多播器设置设置一个线程池

3.2.3 第三种,在监听器方法上加@Async注解


一、Spring监听器模型

前面我们讲了观察者模式的模型,它的模型主要是由观察者实体和主题实体构成,而Spring的监听器模式则结合了Spring本身的特征,也就是容器化。在Spring中,监听器实体全部放在ApplicationContext中,事件也是通过ApplicationContext来进行发布(ApplicationContext就相当于一个事件发布器),具体模型如下:

我们不难看到,虽说是通过ApplicationContext发布的事件,但其并不是ApplicationContext自己进行事件的发布,而是引入了一个处理器—— EventMulticaster,直译就是事件多播器,用它来进行事件发布,它负责在大量的监听器中,针对每一个要广播的事件,找到事件对应的监听器,然后调用该监听器的响应方法,图中就是调用了监听器1、3、6。

PS: 只有在某类事件第一次广播时,EventMulticaster才会去做遍历所有监听器的事,当它针对该类事件广播过一次后,就会把对应监听器保存起来了,最后会形成一个缓存Map,下一次就能直接找到这些监听器

// 缓存
final Map<ListenerCacheKey, ListenerRetriever> retrieverCache = new ConcurrentHashMap<>(64);

二、源码分析

在实际的面试过程中,关于Spring监听器的内容问的很少,一般只需要答出来下面的四个点即可,不用太深入的扣源码细节,明白它的设计思路就可以。

Spring在ApplicationContext接口的抽象实现类AbstractApplicationContext中完成了事件体系的搭建。

AbstractApplicationContext拥有一个applicationEventMulticaster成员变量,applicationEventMulticaster提供了容器监听器的注册表。

当Spring容器启动的时候会调用refresh初始化容器,AbstractApplicationContext在refresh()这个容器启动方法中搭建了事件的基础设施。其中AbstractApplicationContext的refresh方法实现如下:

AbstractApplicationContext.java

public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        // Prepare this context for refreshing.
        prepareRefresh();
        // Tell the subclass to refresh the internal bean factory.
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
        // Prepare the bean factory for use in this context.
        prepareBeanFactory(beanFactory);
        try {
            // Allows post-processing of the bean factory in context subclasses.
            postProcessBeanFactory(beanFactory);

            // Invoke factory processors registered as beans in the context.
            invokeBeanFactoryPostProcessors(beanFactory);

            // Register bean processors that intercept bean creation.
            registerBeanPostProcessors(beanFactory);

            // Initialize message source for this context.
            initMessageSource();

            // Initialize event multicaster for this context.
            // 事件多播器的初始化
            initApplicationEventMulticaster();

            // Initialize other special beans in specific context subclasses.
            onRefresh();

            // Check for listener beans and register them.
            // 将监听器注册到事件多播器中
            registerListeners();

            // Instantiate all remaining (non-lazy-init) singletons.
            finishBeanFactoryInitialization(beanFactory);

            // Last step: publish corresponding event.
            // 完成Spring容器的refresh后,会发布Spring的一些内置事件
            finishRefresh();
        }
        catch (BeansException ex) {
            if (logger.isWarnEnabled()) {
                logger.warn("Exception encountered during context initialization - " +
                        "cancelling refresh attempt: " + ex);
            }
            // Destroy already created singletons to avoid dangling resources.
            destroyBeans();
            // Reset 'active' flag.
            cancelRefresh(ex);
            // Propagate exception to caller.
            throw ex;
        }
        finally {
            // Reset common introspection caches in Spring's core, since we
            // might not ever need metadata for singleton beans anymore...
            resetCommonCaches();
        }
    }
}

其中和监听器相关的方法就是initApplicationEventMulticaster()、registerListeners()和finishRefresh()

2.1 initApplicationEventMulticaster():事件广播器的初始化

AbstractApplicationContext.java

protected void initApplicationEventMulticaster() {x
    // 获取ConfigurableListableBeanFactory工厂
    ConfigurableListableBeanFactory beanFactory = getBeanFactory();
    // 首先看spring容器中是不是已经有名字为applicationEventMulticaster的对象,
    // 如果有就使用容器中已有的对象并赋值给AbstractApplicationContext类的成员属性applicationEventMulticaster 
    if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
        this.applicationEventMulticaster =
                beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
        if (logger.isTraceEnabled()) {
            logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
        }
    }
    else {
        // 如果没有则新建一个SimpleApplicationEventMulticaster对象,并赋值给applicationEventMulticaster成员属性
        this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
        beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
        if (logger.isTraceEnabled()) {
            logger.trace("No '" + APPLICATION_EVENT_MULTICASTER_BEAN_NAME + "' bean, using " +
                    "[" + this.applicationEventMulticaster.getClass().getSimpleName() + "]");
        }
    }
}

用户可以在配置文件中为容器定义一个自定义的事件广播器,只要实现ApplicationEventMulticaster就可以了,Spring会通过反射的机制将其注册成容器的事件广播器,如果没有找到配置的外部事件广播器, Spring默认使用 SimpleApplicationEventMulticaster作为事件广播器。

上面代码的逻辑最要是判断容器中是否有名字叫applicationEventMulticaster的事件广播器,如果有则使用已有的(这也算是Spring的一个扩展点,也就是如果我们创建了个名字是applicationEventMulticaster的事件广播器,那么就会使用我们自定义的),如果没有则新建默认的事件广播器SimpleApplicationEventMulticaster。这里会赋值给AbstractApplicationContext的applicationEventMulticaster成员变量。AbstractApplicationContext有一个名为parent的成员属性,是ApplicationContext类型,ApplicationContext也实现了ApplicationEventPublisher接口,所以它拥有事件相关的所有方法,但ApplicationContext只是一个代理,当我们调用applicationContext.publishEvent方法时,其实调用的就是applicationEventMulticaster 这个成员变量对象

2.1.1 Spring默认的事件广播器SimpleApplicationEventMulticaste

/**
 * SimpleApplicationEventMulticaster是Spring中的事件广播器类。
 * 它实现了事件的多播,即将事件通知给所有注册的监听器。
 * 这个类继承自AbstractApplicationEventMulticaster。
 */
public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {
    /**
     * 事件多播器可以绑定线程池,以实现发布事件和执行监听器逻辑的异步执行
     *
    @Nullable
    private Executor taskExecutor;

    @Nullable
    private ErrorHandler errorHandler;

    /**
     * 创建一个新的SimpleApplicationEventMulticaster实例。
     */
    public SimpleApplicationEventMulticaster() {
    }

    /**
     * 使用给定的BeanFactory创建一个新的SimpleApplicationEventMulticaster实例。
     * @param beanFactory 给定的BeanFactory
     */
    public SimpleApplicationEventMulticaster(BeanFactory beanFactory) {
        setBeanFactory(beanFactory);
    }

    /**
     * 设置任务执行器,用于异步执行监听器的事件处理。
     * @param taskExecutor 任务执行器
     */
    public void setTaskExecutor(@Nullable Executor taskExecutor) {
        this.taskExecutor = taskExecutor;
    }

    /**
     * 获取当前的任务执行器。
     * @return 当前的任务执行器
     */
    @Nullable
    protected Executor getTaskExecutor() {
        return this.taskExecutor;
    }

    /**
     * 设置错误处理器,用于处理监听器抛出的异常。
     * @param errorHandler 错误处理器
     */
    public void setErrorHandler(@Nullable ErrorHandler errorHandler) {
        this.errorHandler = errorHandler;
    }

    /**
     * 获取当前的错误处理器。
     * @return 当前的错误处理器
     */
    @Nullable
    protected ErrorHandler getErrorHandler() {
        return this.errorHandler;
    }

    /**
     * 广播给定的应用事件到所有适当的监听器。
     * @param event 要广播的应用事件
     */
    @Override
    public void multicastEvent(ApplicationEvent event) {
        multicastEvent(event, resolveDefaultEventType(event));
    }

    /**
     * 广播给定的应用事件到所有适当的监听器,可以指定事件的类型。
     * @param event 要广播的应用事件
     * @param eventType 事件的类型,如果为null,则根据事件实例自动确定类型
     */
    @Override
    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);
            }
        }
    }

    /**
     * 解析默认的事件类型。
     * @param event 应用事件
     * @return 事件类型
     */
    private ResolvableType resolveDefaultEventType(ApplicationEvent event) {
        return ResolvableType.forInstance(event);
    }

    /**
     * 调用给定的监听器处理给定的事件。
     * @param listener 要调用的应用监听器
     * @param event 当前的事件
     * @since 4.1
     */
    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);
        }
    }

    /**
     * 调用监听器的事件处理方法,处理事件监听器可能抛出的异常。
     * @param listener 要调用的应用监听器
     * @param event 当前的事件
     */
    @SuppressWarnings({"rawtypes", "unchecked"})
    private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
        try {
            // 调用监听器的事件处理方法
            listener.onApplicationEvent(event);
        } catch (ClassCastException ex) {
            // 处理ClassCastException异常,可能是Lambda表达式定义的监听器,无法解析泛型事件类型
            String msg = ex.getMessage();
            if (msg == null || matchesClassCastMessage(msg, event.getClass())) {
                // 忽略异常,记录调试日志
                Log logger = LogFactory.getLog(getClass());
                if (logger.isTraceEnabled()) {
                    logger.trace("Non-matching event type for listener: " + listener, ex);
                }
            } else {
                // 抛出其他类型的ClassCastException异常
                throw ex;
            }
        }
    }

    /**
     * 判断ClassCastException异常的消息是否匹配给定的事件类。
     * @param classCastMessage ClassCastException异常的消息
     * @param eventClass 事件的类
     * @return 如果消息匹配事件类,返回true,否则返回false
     */
    private boolean matchesClassCastMessage(String classCastMessage, Class<?> eventClass) {
        // 在Java 8中,消息以类名开头:"java.lang.String cannot be cast..."
        if (classCastMessage.startsWith(eventClass.getName())) {
            return true;
        }
        // 在Java 11中,消息以"class ..."开头,即Class.toString()的返回值
        if (classCastMessage.startsWith(eventClass.toString())) {
            return true;
        }
        // 在Java 9中,消息包含模块名:"java.base/java.lang.String cannot be cast..."
        int moduleSeparatorIndex = classCastMessage.indexOf('/');
        if (moduleSeparatorIndex != -1 && classCastMessage.startsWith(eventClass.getName(), moduleSeparatorIndex + 1)) {
            return true;
        }
        // 假设是不相关的ClassCastException异常,返回false
        return false;
    }
}

遍历注册的每个监听器,并启动来调用每个监听器的onApplicationEvent方法。由于 SimpleApplicationEventMulticaster的taskExecutor的实现类是SyncTaskExecutor,因此,该事件监听器对事件的处理是同步进行的。

2.2 registerListeners():注册事件监听器

通过registerListeners()方法将事件监听器注册到事件多播器中。在上一步事件多播器已经创建出来了,而事件监听器此时也已经在Spring容器中了,具体是什么时候创建的事件监听器我们后面会讲到。

AbstractApplicationContext.java

protected void registerListeners() {
    // Register statically specified listeners first.
    // 获取容器中所有的监听器对象
    // 这个时候正常流程是不会有监听器的,除非手动调用addApplicationListeners()
    for (ApplicationListener<?> listener : getApplicationListeners()) {
        // 把监听器挨个的注册到我们的多播器上去
        getApplicationEventMulticaster().addApplicationListener(listener);
    }

    // 这里是为了防止懒加载监听器漏网,因为懒加载的Bean只有在调用的时候才会创建,所以容器中不会用这个Bean,只能从Bean定义中去获取
    // 获取bean定义中的监听器对象    接口方式实现的监听就是在这里注册到多播器里面的
    // Do not initialize FactoryBeans here: We need to leave all regular beans
    // uninitialized to let post-processors apply to them!
    String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
    // 把监听器的名称注册到我们的事件多播器上
    for (String listenerBeanName : listenerBeanNames) {
        getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
    }

    // 在这里获取早期事件
    // Publish early application events now that we finally have a multicaster...
    Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
    // 在这里赋null。 也就是在此之后都就没有早期事件了
    this.earlyApplicationEvents = null;
    if (!CollectionUtils.isEmpty(earlyEventsToProcess)) {
        // 通过多播器进行播发早期事件
        for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
            getApplicationEventMulticaster().multicastEvent(earlyEvent);
        }
    }
}

Spring根据反射机制,使用ListableBeanFactory的getBeansOfType方法,从BeanDefinitionRegistry中找出所有实现 org.springframework.context.ApplicationListener的Bean,将它们注册为容器的事件监听器,实际的操作就是将其添加到事件广播器所提供的监听器注册表中。

2.3 finishRefresh():完成refresh后发布相应的内置事件

AbstractApplicationContext.java

protected void finishRefresh() {
    // Clear context-level resource caches (such as ASM metadata from scanning).
    clearResourceCaches();

    // Initialize lifecycle processor for this context.
    initLifecycleProcessor();

    // Propagate refresh to lifecycle processor first.
    getLifecycleProcessor().onRefresh();

    // Publish the final event.
    // 发布容器启动完毕事件ContextRefreshedEvent
    publishEvent(new ContextRefreshedEvent(this));

    // Participate in LiveBeansView MBean, if active.
    LiveBeansView.registerApplicationContext(this);
}

2.3.1 publishEvent(ApplicationEvent)方法:用于发布事件

事件发布的关键方法流程如下:

org.springframework.context.support.AbstractApplicationContext#publishEvent   //发布事件
  -->org.springframework.context.support.AbstractApplicationContext#publishEvent  //发布事件
      -->org.springframework.context.event.SimpleApplicationEventMulticaster#multicastEvent  //发布事件
        -->org.springframework.context.event.AbstractApplicationEventMulticaster#getApplicationListeners //找到感兴趣的事件
          -->org.springframework.context.event.AbstractApplicationEventMulticaster#retrieveApplicationListeners//找到感兴趣的事件
            -->org.springframework.context.event.AbstractApplicationEventMulticaster#supportsEvent //判断是否是感兴趣的事件

我们先跟着 finishRefresh()方法进入publishEvent(ApplicationEvent )方法,这个方法就是用于发布事件的(传入方法的就是要发布的事件),源码如下:

AbstractApplicationContext.java

public void publishEvent(ApplicationEvent event) {
    publishEvent(event, null);
}

protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
    Assert.notNull(event, "Event must not be null");
    // Decorate event as an ApplicationEvent if necessary
    ApplicationEvent applicationEvent;
    if (event instanceof ApplicationEvent) {
        applicationEvent = (ApplicationEvent) event;
    }
    else {
        applicationEvent = new PayloadApplicationEvent<>(this, event);
        if (eventType == null) {
            eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType();
        }
    }
    // Multicast right now if possible - or lazily once the multicaster is initialized
    if (this.earlyApplicationEvents != null) {
        this.earlyApplicationEvents.add(applicationEvent);
    }
    else {
        getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
    }

    // Publish event via parent context as well...
    if (this.parent != null) {
        if (this.parent instanceof AbstractApplicationContext) {
            ((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
        }
        else {
            this.parent.publishEvent(event);
        }
    }
}

在AbstractApplicationContext的publishEvent方法中, Spring委托ApplicationEventMulticaster将事件通知给所有的事件监听器。

2.3.1.1 multicastEvent()方法

所有的真正发布动作都是由multicastEvent()完成的。multicastEvent 方法很简单,调用getApplicationListeners方法找到所有对当前事件感兴趣的监听器,如果存在线程池,则异步执行监听逻辑,否则就同步执行。默认情况下执行事件的逻辑是跟发布事件是同一个线程。

SimpleApplicationEventMulticaster.java

@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
    ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
    // 获取当前事件多播器的线程池。事件多播器类中有一个线程池成员属性,用来记录和事件多播器绑定在一起的线程池
    Executor executor = getTaskExecutor();
    // getApplicationListeners 方法找到所有的对当前事件感兴趣的监听器
    for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
        if (executor != null) {
            // 如果有配置线程池,则使用线程池执行监听逻辑,也就是触发监听器对事件发生后的执行逻辑
            executor.execute(() -> invokeListener(listener, event));
        }
        else {
            // 没有线程池,就用发布事件的线程去执行监控器在事件发生后触发的逻辑
            invokeListener(listener, event);
        }
    }
}

整个代码逻辑很简单,就是判断是否有Executor,如果有的话异步加载invokeListener方法,没有的话同步调用invokeListener方法

2.3.1.1.1 getApplicationListeners(eventtype)方法:获取所有监听event事件的监听器

getApplicationListeners方法,主要是判断缓存中有没有已经缓存好的事件,有则直接返回,没有则调用方法retrieveApplicationListeners检索监听器。

AbstractApplicationEventMulticaster.java

protected Collection<ApplicationListener<?>> getApplicationListeners(
        ApplicationEvent event, ResolvableType eventType) {

    Object source = event.getSource();
    Class<?> sourceType = (source != null ? source.getClass() : null);

    // 生成一个用于表示当前这个事件event的缓存key
    ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType);
    // Potential new retriever to populate
    CachedListenerRetriever newRetriever = null;
    // Quick check for existing entry on ConcurrentHashMap
    // 判断缓存中是不是已经缓存了对该事件感兴趣的监听器
    CachedListenerRetriever existingRetriever = this.retrieverCache.get(cacheKey);
    // 没有的话,新建一个键值对,并放到map中。这个键值对就用来存放监听这个事件的所有监听器
    if (existingRetriever == null) {
        // Caching a new ListenerRetriever if possible
        if (this.beanClassLoader == null ||
                (ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) &&
                        (sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader)))) {
            newRetriever = new CachedListenerRetriever();
            existingRetriever = this.retrieverCache.putIfAbsent(cacheKey, newRetriever);
            if (existingRetriever != null) {
                newRetriever = null;  // no need to populate it in retrieveApplicationListeners
            }
        }
    }

    // 缓存中有,直接从缓存中获取该事件的监听器并返回
    if (existingRetriever != null) {
        Collection<ApplicationListener<?>> result = existingRetriever.getApplicationListeners();
        if (result != null) {
            return result;
        }
        // If result is null, the existing retriever is not fully populated yet by another thread.
        // Proceed like caching wasn't possible for this current local attempt.
    }

    // 缓存中没有,就调用retrieveApplicationListeners方法查找事件ApplicationEvent。
    return retrieveApplicationListeners(eventType, sourceType, newRetriever);
}

2.3.1.1.1.1 retrieveApplicationListeners()

this.defaultRetriever.applicationListeners和this.defaultRetriever.applicationListenerBeans存放了所有的Spring事件,最终会调用supportsEvent方法过滤出对当期事件感兴趣的监听器。

AbstractApplicationEventMulticaster.java

private Collection<ApplicationListener<?>> retrieveApplicationListeners(
            ResolvableType eventType, @Nullable Class<?> sourceType, @Nullable CachedListenerRetriever retriever) {

    List<ApplicationListener<?>> allListeners = new ArrayList<>();
    Set<ApplicationListener<?>> filteredListeners = (retriever != null ? new LinkedHashSet<>() : null);
    Set<String> filteredListenerBeans = (retriever != null ? new LinkedHashSet<>() : null);
    Set<ApplicationListener<?>> listeners;
    Set<String> listenerBeans;

    synchronized (this.defaultRetriever) {
        // this.defaultRetriever.applicationListeners存放了所有的applicationListeners
        listeners = new LinkedHashSet<>(this.defaultRetriever.applicationListeners);
        // this.defaultRetriever.applicationListenerBeans存放的是PayloadApplicationEvent事件
        listenerBeans = new LinkedHashSet<>(this.defaultRetriever.applicationListenerBeans);
    }
    // Add programmatically registered listeners, including ones coming
    // from ApplicationListenerDetector (singleton beans and inner beans).
    for (ApplicationListener<?> listener : listeners) {
        // 判断遍历到的监听器是否对当前事件感兴趣
        if (supportsEvent(listener, eventType, sourceType)) {
            if (retriever != null) {
                filteredListeners.add(listener);
            }
            allListeners.add(listener);
        }
    }
    ...............省略
}

事件监听器是什么时候创建并加入到defaultRetriever.applicationListeners中的呢?

我们将监听器注册到事件多播器就会引出一个问题,这里以this.defaultRetriever.applicationListeners为例,this.defaultRetriever.applicationListeners中所有的监听器是什么时候加入进去的呢?

this.defaultRetriever.applicationListeners中包含两种监听器:

  1. 实现了ApplicationListener接口。
  2. @EventListener 注解

1 查找实现了ApplicationListener接口的监听器

这里关键调用ApplicationListenerDetector的postProcessMergedBeanDefinition方法和postProcessAfterInitialization方法(这个方法也就是常说的bean初始化后)。这两个都是BeanPostProcessor的子类提供的方法。

1.1 postProcessMergedBeanDefinition()方法

postProcessMergedBeanDefinition方法的作用很简单,就是判断这个Java类是否实现了ApplicationListener接口,是的话就是一个监听器。然后将这个监听器加入到singletonNames的记录单例Bean的Map中。

ApplicationListenerDetector.java

@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
    if (ApplicationListener.class.isAssignableFrom(beanType)) {
        this.singletonNames.put(beanName, beanDefinition.isSingleton());
    }
}

1.2 postProcessAfterInitialization()方法

postProcessAfterInitialization方法也很简单,从singletonNames拿到所有的单例ApplicationListener。

并调用this.applicationContext.addApplicationListener((ApplicationListener<?>) bean)方法,前面提到过applicationContext也实现ApplicationEventPublisher接口,拥有事件发布的所有方法,但实际执行的是成员变量applicationEventMulticaster对象。最终就将监听器添加到了this.defaultRetriever.applicationListeners中。

ApplicationListenerDetector.java

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
    if (bean instanceof ApplicationListener) {
        // potentially not detected as a listener by getBeanNamesForType retrieval
        Boolean flag = this.singletonNames.get(beanName);
        if (Boolean.TRUE.equals(flag)) {
            // 拿到所有的单例ApplicationListener并添加到defaultRetriever.applicationListeners
            this.applicationContext.addApplicationListener((ApplicationListener<?>) bean);
        }
        else if (Boolean.FALSE.equals(flag)) {
            if (logger.isWarnEnabled() && !this.applicationContext.containsBean(beanName)) {
                // inner bean with other scope - can't reliably process events
                logger.warn("Inner bean '" + beanName + "' implements ApplicationListener interface " +
                        "but is not reachable for event multicasting by its containing ApplicationContext " +
                        "because it does not have singleton scope. Only top-level listener beans are allowed " +
                        "to be of non-singleton scope.");
            }
            this.singletonNames.remove(beanName);
        }
    }
    return bean;
}

this.applicationContext.addApplicationListener((ApplicationListener<?>) bean)最终执行的方法其实是AbstractApplicationEventMulticaster类的addApplicationListener方法。

其会最终调用this.defaultRetriever.applicationListeners.add(listener)方法。

AbstractApplicationContext.java

public void addApplicationListener(ApplicationListener<?> listener) {
    Assert.notNull(listener, "ApplicationListener must not be null");
    if (this.applicationEventMulticaster != null) {
        // 调用applicationEventMulticaster.addApplicationListener,来进行监听器的注册
        this.applicationEventMulticaster.addApplicationListener(listener);
    }
    this.applicationListeners.add(listener);
}

 

AbstractApplicationEventMulticaster.java

// 将监听器加入到defaultRetriever.applicationListeners中
public void addApplicationListener(ApplicationListener<?> listener) {
    synchronized (this.defaultRetriever) {
        // Explicitly remove target for a proxy, if registered already,
        // in order to avoid double invocations of the same listener.
        Object singletonTarget = AopProxyUtils.getSingletonTarget(listener);
        if (singletonTarget instanceof ApplicationListener) {
            this.defaultRetriever.applicationListeners.remove(singletonTarget);
        }
        // 加入监听器
        this.defaultRetriever.applicationListeners.add(listener);
        this.retrieverCache.clear();
    }
}

2 查找加了@EventListener 注解的监听器

我们知道,在生成流程中,会对每个Bean都使用PostProcessor来进行加工。查找加了@EventListener 注解的监听器主要是通过EventListenerMethodProcessor类,由方法名可以看到,这也是一个bean的后置处理器,这个类会在Bean实例化后进行一系列操作。EventListenerMethodProcessor类实现了SmartInitializingSingleton接口,查找@EventListener注解的监听器的功能是通过调用EventListenerMethodProcessor类实现的SmartInitializingSingleton接口的afterSingletonsInstantiated方法来完成,这个方法会在容器初始化完成之后执行。

查找的方式也很简单粗暴,直接调用beanFactory.getBeanNamesForType(Object.class)方法获取Spring容器中的所有对象,然后再去判断这个Bean的方法是否加了@EventListener注解。

EventListenerMethodProcessor.java

@Override
public void afterSingletonsInstantiated() {
    ConfigurableListableBeanFactory beanFactory = this.beanFactory;
    Assert.state(this.beanFactory != null, "No ConfigurableListableBeanFactory set");
    // 获取Spring容器中的所有Bean的name
    String[] beanNames = beanFactory.getBeanNamesForType(Object.class);

    for (String beanName : beanNames) {
        ................省略
        try {
            // 查找该Bean的方法中是加了@EventListener注解
            processBean(beanName, type);
        }
        catch (Throwable ex) {
            throw new BeanInitializationException("Failed to process @EventListener " +
                    "annotation on bean with name '" + beanName + "'", ex);
        }
    }
                    
}

然后调用processBean()方法,遍历对象中的方法是否有加了EventListener注解的方法。有则调用DefaultEventListenerFactory的createApplicationListener方法创建一个是配置器对象ApplicationListenerMethodAdapter,构造器参数是方法对象,bean类对象,bean对象。最后调用context.addApplicationListener方法把监听器放到this.defaultRetriever.applicationListeners中

EventListenerMethodProcessor.java

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 {
            // 遍历对象中的方法是否有加了EventListener注解的方法,将它们都存到annotatedMethods中
            annotatedMethods = MethodIntrospector.selectMethods(targetType,
                    (MethodIntrospector.MetadataLookup<EventListener>) method ->
                            AnnotatedElementUtils.findMergedAnnotation(method, EventListener.class));
        }
        ..............省略..................
        // 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");
        // 遍历加了EventListener注解的方法
        for (Method method : annotatedMethods.keySet()) {
            // 遍历监听器工厂,这类工厂是专门用来创建监听器的,我们以起作用的是DefaultEventListenerFactory默认工厂为例
            for (EventListenerFactory factory : factories) {
                // DefaultEventListenerFactory是永远返回true的
                if (factory.supportsMethod(method)) {
                    Method methodToUse = AopUtils.selectInvocableMethod(method, context.getType(beanName));
                    // 调用DefaultEventListenerFactory的createApplicationListener的方法,利用该Bean名、Bean类型、方法来创建监听器(其实是适配器类)
                    ApplicationListener<?> applicationListener =
                        factory.createApplicationListener(beanName, targetType, methodToUse);
                    if (applicationListener instanceof ApplicationListenerMethodAdapter) {
                        ((ApplicationListenerMethodAdapter) applicationListener).init(context, this.evaluator);
                    }
                    // 把监听器添加到this.defaultRetriever.applicationListeners容器中
                    context.addApplicationListener(applicationListener);
                    break;
                }
            }
        }
    }
}

              

以DefaultEventListenerFactory为例,用默认工厂类来创建监听器(适配器类)ApplicationListenerMethodAdapter。

public class DefaultEventListenerFactory implements EventListenerFactory, Ordered {
    // 省略其余代码
    @Override
    public ApplicationListener<?> createApplicationListener(String beanName, Class<?> type, Method method) {
        // 可以看到,每次都是返回一个新对象,每一个加了@EventListener注解的方法就会对应生成一个监听器。例如同一个类中有两个加了@EventListener注解的方法,那么就会生成两个监听器对象
        return new ApplicationListenerMethodAdapter(beanName, type, method);
    }
}

2.3.1.1.1.1.1 supportsEvent(listenereventType) 判断当前监听器是否用来监听该事件

Spring发布事件之后,所有注册的事件监听器,都会收到该事件,因此,事件监听器在处理事件时,需要先判断该事件是否是自己关心的。

所以梳理完了监听器的查找过程,下面我们继续来看看Spring监听器是如何查找感兴趣的事件的。

判断监听器是否用来监听指定事件的逻辑代码还是比较清晰的,先判断当前监听器是不是继承了GenericApplicationListener。如果不是继承GenericApplicationListener的监听器,将会被GenericApplicationListenerAdapter适配器再次包装。

GenericApplicationListener 实现SmartApplicationListener,SmartApplicationListener实现了ApplicationListener接口。但是GenericApplicationListener 的作用是实现了SmartApplicationListener接口的两个方法supportsEventType()和supportsSourceType()。

这两个方法的作用是:

  • supportsEventType(eventType):检查监听器是否支持指定的事件类型。在事件广播过程中,系统会遍历所有的监听器,但并非所有的监听器都对所有类型的事件感兴趣。这个方法用来检查给定的监听器是否对特定类型的事件感兴趣。
  • supportsSourceType(sourceType):检查监听器是否支持指定的事件源类型。有时候,监听器只对特定类型的事件源感兴趣,这个方法用来确定监听器是否支持特定类型的事件源。

这两个方法通常在事件广播器内部使用,用于确定哪些监听器应该接收特定类型的事件。

protected boolean supportsEvent(
        ApplicationListener<?> listener, ResolvableType eventType, @Nullable Class<?> sourceType) {
    
    // 如果listener监听器没有继承GenericApplicationListener,就再用GenericApplicationListener将其封装一层
    GenericApplicationListener smartListener = (listener instanceof GenericApplicationListener ?
        (GenericApplicationListener) listener : new GenericApplicationListenerAdapter(listener));

    // 判断监听器是否支持指定的事件类型和事件源类型
    return (smartListener.supportsEventType(eventType) && smartListener.supportsSourceType(sourceType));
}

我们来看一下GenericApplicationListener接口源码:

public interface GenericApplicationListener extends SmartApplicationListener {
    @Override
    default boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
        return supportsEventType(ResolvableType.forClass(eventType));
    }
    boolean supportsEventType(ResolvableType eventType);
}

从接口方法可以看到GenericApplicationListener 的子类如果没重写supportsEventType方法的话,就表示对所有的事件感兴趣。通过supportsEventType方法,我们可以自定义我们感兴趣的事件。

而相对于GenericApplicationListener接口,适配类GenericApplicationListenerAdapter实现了supportsEventType方法,它的实现就是判断当前监听器的泛型是不是当前事件或者是当前事件的子类(this.declaredEventType.isAssignableFrom(eventType))。

GenericApplicationListenerAdapter.java

public boolean supportsEventType(ResolvableType eventType) {
    if (this.delegate instanceof GenericApplicationListener) {
        return ((GenericApplicationListener) this.delegate).supportsEventType(eventType);
    }
    else if (this.delegate instanceof SmartApplicationListener) {
        Class<? extends ApplicationEvent> eventClass = (Class<? extends ApplicationEvent>) eventType.resolve();
        return (eventClass != null && ((SmartApplicationListener) this.delegate).supportsEventType(eventClass));
    }
    // 默认实现
    else {
        return (this.declaredEventType == null || this.declaredEventType.isAssignableFrom(eventType));
    }
}

2.3.1.1.2 invokeListener()方法:执行监听器的监听逻辑

SimpleApplicationEventMulticaster.java

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())) {
            // Possibly a lambda-defined listener which we could not resolve the generic event type for
            // -> let's suppress the exception and just log a debug message.
            Log logger = LogFactory.getLog(getClass());
            if (logger.isTraceEnabled()) {
                logger.trace("Non-matching event type for listener: " + listener, ex);
            }
        }
        else {
            throw ex;
        }
    }
}

在doInvokeListener()方法中,终于看到了熟悉的onApplicationEvent方法,这个方法就是我们在自定义监听器时重写的方法,也正是在这里监听器调用了我们自己定义的onApplicationEvent(),实现了当监听器监听到事件发生后,就执行一些自定义的功能。

三、同步与异步

通过模型,我们不难看出,事件的发布其实由业务线程来发起,那么那些监听器的触发呢,是仍由业务线程一个个同步地去通知监听器,还是有专门的线程接手,收到事件后,再转手通知监听器们?

3.1 默认同步通知

其实,因为Spring默认的多播器没有设置执行器,所以默认采用的是第一种情况,即哪个线程发起的事件,则由哪个线程去通知监听器们,关键代码上面已经讲过了,如下所示:

// SimpleApplicationEventMulticaster.java
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);
        }
    }
}

@Nullable
protected Executor getTaskExecutor() {
    return this.taskExecutor;
}

我们可以看到,对于每个监听器的调用是同步还是异步,取决于多播器内部是否含有一个执行器,如果有则交给执行器去执行,如果没有,只能让来源线程一一去通知了。

3.2 异步通知设置

两种方式,一种是在多播器创建时内置一个线程池,使多播器能够调用自身的线程池去执行事件传播。另一种是不在多播器上做文章,而是在每个监视器的响应方法上标注异步@Async注解,毫无疑问,第一种才是正道,我们来看看如何做到。其实有多种方法,我们这里说几种。

3.2.1 第一种,直接自定义一个多播器,然后注册到容器中顶替掉Spring自动创建的多播器

@Configuration
public class EventConfig {
    @Bean("taskExecutor")
    public Executor getExecutor() {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(10,
                15,
                60,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue(2000));
        return executor;
    }
    
    // 这其实就是spring-boot自动配置的雏形,所谓的自动配置其实就是通过各种配置类,顶替原有的简单配置
    @Bean(AbstractApplicationContext.APPLICATION_EVENT_MULTICASTER_BEAN_NAME)
    public ApplicationEventMulticaster initEventMulticaster(@Qualifier("taskExecutor") Executor taskExecutor) {
        SimpleApplicationEventMulticaster simpleApplicationEventMulticaster = new SimpleApplicationEventMulticaster();
        simpleApplicationEventMulticaster.setTaskExecutor(taskExecutor);
        return simpleApplicationEventMulticaster;
    }
}

3.2.2 第二种,为现成的多播器设置设置一个线程池

@Component
public class WindowsCheck implements ApplicationContextAware {
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        SimpleApplicationEventMulticaster caster = (SimpleApplicationEventMulticaster)applicationContext
                .getBean(AbstractApplicationContext.APPLICATION_EVENT_MULTICASTER_BEAN_NAME);
        ThreadPoolExecutor executor = new ThreadPoolExecutor(10,
                15,
                60,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue(2000));
        caster.setTaskExecutor(executor);
    }
}

当然,这里推荐第一种,第二种方法会在Spring启动初期的一些事件上,仍采用同步的方式。直至被注入一个线程池后,其才能使用线程池来响应事件。而第一种方法则是官方暴露的位置,让我们去构建自己的多播器。

3.2.3 第三种,在监听器方法上加@Async注解

@Component
public class OrderEventListener {

    @Async
    @EventListener(OrderEvent.class)
    public void onApplicationEvent(OrderEvent event) {
        if(event.getName().equals("减库存")){
            System.out.println("减库存.......");
        }
    }
}

方法增加@Async注解,表示该方法为异步方法。


相关文章:【Spring框架】Spring监听器的简介和基本使用

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1094114.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

京东店铺所有商品数据接口,京东整店所有商品数据接口,京东店铺商品接口,京东API接口

京东店铺所有商品数据接口是开放平台提供的一种API接口&#xff0c;通过调用API接口&#xff0c;开发者可以获取京东整店的商品的标题、价格、库存、月销量、总销量、库存、详情描述、图片、价格信息等详细信息。 京东店铺所有商品数据接口可以用于不同的业务场景&#xff0c;…

【HHO-KELM预测】基于哈里斯鹰算法优化核极限学习机回归预测研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

博图数值按照特定格式(“T000000”)转换成字符串

一、前言 数值按照协议格式&#xff0c;转成字符串。方便和第三方厂家对接。如码垛线使用字符串数据&#xff0c;立库厂家使用dint数据类型&#xff0c;上位机使用DINT数据类型&#xff0c;为了判断数据传输、与动作流程&#xff0c;需要条码的比较&#xff0c;此时可以将数值转…

Kotlin vs Java:为什么Springboot官方教程选择了Kotlin?

导语 作为Java开发者的你&#xff0c;是否在为寻找Java的替代品而烦恼&#xff1f;担心受知识产权问题困扰&#xff1f;别担心&#xff0c;Kotlin来了&#xff01;它是你的救星&#xff0c;也是Springboot官网教程的选择。想知道为什么吗&#xff1f;那就往下翻吧&#xff01;…

“通胀噩梦:恶梦继续还是即将终结?经济前景备受关注!“

尽管美联储采取了激进的利率策略&#xff0c;昨天公布的 9 月份 CPI 数据显示&#xff0c;整体同比增长 3.7%&#xff0c;而预期为 3.6%&#xff0c;高于预期。环比预期&#xff0c;为 0.4%&#xff0c;而预期为 0.3%。核心 CPI 环比上涨 0.3%&#xff0c;同比上涨 4.1%&#x…

极限号可以拿到函数的内部吗?【复合函数中极限的进入】

极限号无脑直接拿进来 1.1 如果f&#xff08;极限值&#xff09;在该点连续&#xff0c;ojbk&#xff0c;拿进来。 1.2 如果f&#xff08;极限值&#xff09;不存在或不连续&#xff0c;不能拿进来&#xff0c;出去。

Flask (Jinja2) 服务端模板注入漏洞复现

文章目录 Flask (Jinja2) 服务端模板注入漏洞1.1 漏洞描述1.2 漏洞原理1.3 漏洞危害1.4 漏洞复现1.4.1 漏洞利用 1.5 漏洞防御 Flask (Jinja2) 服务端模板注入漏洞 1.1 漏洞描述 说明内容漏洞编号漏洞名称Flask (Jinja2) 服务端模板注入漏洞漏洞评级高危影响版本使用Flask框架…

【剑指Offer】27.二叉树的镜像

题目 操作给定的二叉树&#xff0c;将其变换为源二叉树的镜像。 数据范围&#xff1a;二叉树的节点数 0≤n≤1000 &#xff0c; 二叉树每个节点的值 0≤val≤1000 要求&#xff1a; 空间复杂度 O(n) 。本题也有原地操作&#xff0c;即空间复杂度O(1) 的解法&#xff0c;时间…

Servlet--Request请求对象

1.请求对象的概述 请求&#xff1a;获取资源。在BS架构中&#xff0c;就是客户端浏览器向服务器端发出询问 请求对象&#xff1a;就是在项目当中用于发送请求的对象 2.获取各种路径的方法 返回值方法名说明StringgetContextPath()获取虚拟目录名称StringgetServletPath()获…

C++前缀和算法:构造乘积矩阵

题目 给你一个下标从 0 开始、大小为 n * m 的二维整数矩阵 grid &#xff0c;定义一个下标从 0 开始、大小为 n * m 的的二维矩阵 p。如果满足以下条件&#xff0c;则称 p 为 grid 的 乘积矩阵 &#xff1a; 对于每个元素 p[i][j] &#xff0c;它的值等于除了 grid[i][j] 外所…

Django使用Token认证(simplejwt库的配置)

目录 官网文档安装项目配置拓展配置 官网文档 https://django-rest-framework-simplejwt.readthedocs.io/en/latest/ 安装 pip install djangorestframework-simplejwt项目配置 REST_FRAMEWORK {...DEFAULT_AUTHENTICATION_CLASSES: (...rest_framework_simplejwt.authent…

【斗破年番】彩鳞换装美翻,雁落天惨死,萧炎暗杀慕兰三老遇险,彩鳞霸气护夫

Hello,小伙伴们&#xff0c;我是小郑继续为大家深度解析斗破苍穹年番资讯。 斗破苍穹动画已经更新了&#xff0c;小医仙与萧炎相认&#xff0c;三国联军撤退&#xff0c;随后彩鳞与萧炎以及小医仙夜晚相会&#xff0c;一起制定了刺杀行动。从官方公布的第68集预告&#xff0c;彩…

ST‐LINK V2 使用说明(安装,调试,烧录)

目录 1. 初识 ST-LINK V2 1.1 ST-LINK V2 简介 2. ST-LINK V2 驱动的安装与固件升级 2.1 驱动的安装 2.2 固件的升级 3. 使用 STM32 ST-LINK Utility 烧写目标板 hex 3.1 ST-LINK 烧写 hex 文件 4.使用 ST-LINK V2 调试 STM8 4.1 ST‐LINK 调试 STM8 5.…

【GA-ACO-RFR预测】基于混合遗传算法-蚁群算法优化随机森林回归预测研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

时序分解 | Matlab实现EEMD集合经验模态分解时间序列信号分解

时序分解 | Matlab实现EEMD集合经验模态分解时间序列信号分解 目录 时序分解 | Matlab实现EEMD集合经验模态分解时间序列信号分解效果一览基本介绍程序设计参考资料 效果一览 基本介绍 Matlab实现EEMD集合经验模态分解时间序列信号分解 1.分解效果图 &#xff0c;效果如图所示&…

4.1 继承性

知识回顾 &#xff08;1&#xff09;类和对象的理解&#xff1f; 对象是现实世界中的一个实体&#xff0c;如一个人、一辆汽车。一个对象一般具有两方面的特征&#xff0c;状态和行为。状态用来描述对象的静态特征&#xff0c;行为用来描述对象的动态特征。 类是具有相似特征…

前端TypeScript学习day04-交叉类型与泛型

(创作不易&#xff0c;感谢有你&#xff0c;你的支持&#xff0c;就是我前行的最大动力&#xff0c;如果看完对你有帮助&#xff0c;请留下您的足迹&#xff09; 目录 交叉类型 泛型 创建泛型函数 调用泛型函数&#xff1a; 简化调用泛型函数&#xff1a; 泛型约束 指定…

为什么在华为DSTE战略管理中,需要三次战略解码?

添加图片注释&#xff0c;不超过 140 字&#xff08;可选&#xff09; 【导语&#xff1a;在战略管理课程中&#xff0c;不少同学对“战略解码”这个术语有诸多疑惑。谢宁老师认为如果同学们详细阅读谢宁专著《华为战略管理法&#xff1a;DSTE实战体系》关于“三次战略解码”的…

动态规划:918. 环形子数组的最大和

个人主页 &#xff1a; 个人主页 个人专栏 &#xff1a; 《数据结构》 《C语言》《C》《算法》 文章目录 前言一、题目解析二、解题思路解题思路状态表示状态转移方程初始化填表顺序返回值 三、代码实现总结 前言 本篇文章仅是作为小白的我的一些理解&#xff0c;&#xff0c;…

蓝桥杯双周赛算法心得——数树数(dfs)

大家好&#xff0c;我是晴天学长&#xff0c;一个简单的dfs思想&#xff0c;需要的小伙伴可以关注支持一下哦&#xff01;后续会继续更新的。 1) .数树数 2) .算法思路 代码的主要逻辑是&#xff1a; 1.使用Scanner读取输入的整数n和q&#xff0c;其中n表示测试用例的数量&am…