在spring中动态删除@eventlistener

li9yvcax  于 2021-07-13  发布在  Java
关注(0)|答案(2)|浏览(500)

我想在(测试)代码中动态删除或添加spring applicationlisteners。
使用Spring ApplicationEventMulticaster 正如在这个问题中所概述的那样,无论具体的 ApplicationListener 我感兴趣的是使用 @EventListener 带注解的方法,结果 ApplicationListenerMethodAdapter -运行时的对象。
如何获得这个bean的名称,以便用 ApplicationEventMulticaster ?

eventMulticaster.removeApplicationListenerBean("NAME_OF_APPLICATION_METHOD_ADAPTER_BEAN")
vltsax25

vltsax251#

不幸的是,spring的公共api只支持添加或删除以编程方式定义的 ApplicationListener s。
不过,通过将spring的子类化,我可以解决我的问题 SimpleApplicationEventMulticaster 还有一些反映。
这并不理想,但如果其他人有类似的问题,我的可配置multicaster就是这样的:

package at.codemonkey.event;

import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ApplicationListenerMethodAdapter;
import org.springframework.context.event.SimpleApplicationEventMulticaster;
import org.springframework.lang.NonNull;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Field;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
import java.util.List;

import static java.util.Optional.empty;
import static org.springframework.util.ReflectionUtils.findField;
import static org.springframework.util.ReflectionUtils.makeAccessible;

public class FilteringApplicationEventMulticaster extends SimpleApplicationEventMulticaster {

  private final Field beanNameField;
  private final Set<String> disabledListeners = new HashSet<>();

  FilteringApplicationEventMulticaster() {
    beanNameField = findField(ApplicationListenerMethodAdapter.class, "beanName");
    assert beanNameField != null;
    makeAccessible(beanNameField);
  }

  @Override
  protected void invokeListener(@NonNull ApplicationListener<?> listener, @NonNull ApplicationEvent event) {
    if (!filter(listener)) {
      super.invokeListener(listener, event);
    }
  }

  private boolean filter(ApplicationListener<?> listener) {
    return getBeanName(listener).filter(disabledListeners::contains).isPresent();
  }

  @SuppressWarnings("ConstantConditions")
  private Optional<String> getBeanName(ApplicationListener<?> listener) {
    return (listener instanceof ApplicationListenerMethodAdapter) ? Optional.of((String) ReflectionUtils.getField(beanNameField, listener)) : empty();
  }

  public void enable(String... listenerBeans) {
    disabledListeners.removeAll(List.of(listenerBeans));
  }

  public void disable(String... listenerBeans) {
    disabledListeners.addAll(List.of(listenerBeans));
  }

}

在springboot应用程序中配置multicaster相当简单:

package at.codemonkey.event;

import org.springframework.boot.SpringBootConfiguration;
import org.springframework.context.annotation.Bean;

@SpringBootConfiguration
class ApplicationEventConfiguration {

  @Bean
  FilteringApplicationEventMulticaster applicationEventMulticaster() {
    return new FilteringApplicationEventMulticaster();
  }

}

现在可以使用bean的名称动态地启用/禁用侦听器,其中 @EventListener 已定义。
假设您有以下侦听器:

@Component
class MyEventListener {

  @EventListener
  public void handle(MyEvent event) { ... } 

}

现在可以通过注入 FilteringApplicationEventMulticaster :

eventMulticaster.enable("myEventListener");
eventMulticaster.disable("myEventListener");

当前实现将禁用所有 @EventListeners 豆子的,对我来说很好。但是,如果您需要更细粒度的控制,那么扩展现有解决方案应该很容易。

wn9m85ua

wn9m85ua2#

从Spring5.3.5开始 @EventListener 得到一个 id 属性,然后可以使用该属性以编程方式删除侦听器:

eventMulticaster.removeApplicationListeners(l -> l instanceof SmartApplicationListener &&
    ((SmartApplicationListener) l).getListenerId().startsWith("myrootpackage.mymodule"))

看到了吗https://github.com/spring-projects/spring-framework/issues/26638

相关问题