【问题标题】:Scoped Spring events possible?范围内的 Spring 事件可能吗?
【发布时间】:2018-12-26 00:39:43
【问题描述】:

Spring事件机制支持通过@EventListener注解在Spring组件内发布应用事件和监听这些事件。但是,我在documentation 中找不到有关在特定范围内发送事件的任何信息。我对 Vaadin 的具体需求是:

  • 在用户交互的上下文中,发送事件(例如登录事件)
  • 此事件只能由同一 @UIScope 中的 bean 使用,即不应影响其他用户 UI

这可能吗? 注意:这并不是 Vaadin 所特有的。我还可以询问如何使用 Spring web mvc 请求范围来完成。

【问题讨论】:

  • 您有特定的用例吗?在我看来,这是不可能的,因为事件系统是用来分离生产者和消费者的。在这里你想要一个联轴器,为什么要使用它?即使您想使用@Async 进行异步处理,将处理数据的线程也不会是您用于启动进程的线程,因此上下文不会相同。我得到的解决方案是在事件中设置您需要的所有数据(来自任何上下文的 bean)并在流程端使用它。但请注意,当您异步处理数据时,某些 bean 可能已经调用了 @PreDestroy 方法。
  • 您或许可以尝试使用@EventListener 注释的condition 属性(有关详细信息,请参阅here)。您基本上可以在 SpEL 中定义一个条件,该条件需要满足才能调用侦听器。
  • 只是一个潜在的线索:你不能在事件中放置一个 UI-id 并有一个 UI 范围的过滤器来拒绝所有 ui_id 与过滤器预期不匹配的事件吗?
  • @SteffenHarbich 是的,我从来没有遇到过你的用例,但我觉得它很有趣,而且远非分裂头发;我很惊讶它还没有在标准 Spring 中处理。不过,也许有办法获得ApplicationEventPublisher ui-scoped。但我不确定 UI 范围的侦听器注册是否是防止太多侦听器执行单个检查的良好折衷。
  • @SteffenHarbich 顺便说一句,我测试了从 RestController 发布一个事件,并让一个请求范围的 bean 从两个并发的 httprequest 监听它(并发通过断点处理)。对于这两个事件,仅触发了范围为当前请求的侦听器。所以我认为 eventPublisher 可能已经被限定了。

标签: java spring vaadin


【解决方案1】:

TL;DR : 我认为它已经按照你的意愿工作了

长版:

文档对此有些不清楚,但尝试了一个简单的测试:

发布事件的控制器:

@Controller
public class FooController {
    @Autowired
    private ApplicationEventPublisher publisher;

    @GetMapping("/fireEvent")
    public void fireEvent() {
        publisher.publishEvent(new FooEvent(this));
    }
}

还有一个监听的作用域 bean:

@Scope(value = WebApplicationContext.SCOPE_REQUEST)
@Component
public class FooListener {
    @EventListener(FooEvent.class)
    public void listen() {
         System.out.println("I'm listening. PS : I am "+this.toString());
    }
}

并且当运行两个并发请求时,只有作用域为同一个 httprequest 的侦听器才能获取事件。


我的解释(没有深入研究,所以持保留态度):

在我看来,ApplicationEventMulticasterListenerRetriever 使用 BeanFactory 来获取先前注册为侦听器的 bean(按其名称)。显然,工厂将返回当前范围内的 bean。

【讨论】:

    【解决方案2】:

    请看看这是不是你要找的:

    主要应用:

       package com.fg7.evision.EventList;
    
        import org.springframework.context.annotation.AnnotationConfigApplicationContext;
        import org.springframework.context.annotation.ComponentScan;
    
        @ComponentScan(basePackages = "com.fg7.evision.EventList")
        public class EventApplication {
    
    
            public static void main(String[] args) {
               MyScopedEvent event = new MyScopedEvent("hello world");
               AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(EventApplication.class);
                ctx.publishEvent(event);
            }
    
        }
    

    事件:

     package com.fg7.evision.EventList;
    
        public class MyScopedEvent  {
    
            private String message;
    
            public MyScopedEvent( String message) {
                this.message = message;
            }
    
            public String getMessage() {
                return message;
            }
        }
    

    仅限于单例范围的事件侦听器。

    package com.fg7.evision.EventList;
    
    import org.springframework.beans.factory.BeanNameAware;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.ConfigurableApplicationContext;
    import org.springframework.context.event.EventListener;
    import org.springframework.stereotype.Component;
    
    @Component(value = "listener")
    public class ScopedEventListener implements BeanNameAware {
    
        @Autowired
        ConfigurableApplicationContext context;
    
        String beanName;
    
        @Override
        public void setBeanName(String name) {
            this.beanName = name;
        }
    
        @EventListener(condition = "@listener.getScope() == 'singleton'")
        public void handleEvent(MyScopedEvent myScopedEvent) {
            System.out.println(myScopedEvent.getMessage());
    
        }
    
        public String getScope(){
            return this.context.getBeanFactory().getBeanDefinition(this.beanName).getScope();
        }
    
    
    }
    

    【讨论】:

    • 感谢您的回答,但这不是我想要的。条件应始终匹配,因为 ScopedEventListener 是单例。如果您有进一步的兴趣,请查看Vaadin UI-scope。 UI 范围在(HTTP-)请求时被激活,因此每个用户(会话)都有他们“自己的”UI 范围。对于我的问题,事件应该只传播到用户自己的 UI 范围的 bean,而不是整个应用程序的 bean。
    • 只需用您自己的自定义范围替换单例(您可以在 Spring 中这样做)。我把单例作为参考。您可以创建自己的组合注释@Scope + @UIScope,即@UISpringAwareScope
    • 你能描述为什么它不会吗?
    • 它只检查 bean 定义的 scope 属性。请参阅解决方案的其他答案。
    • 我正在检查发出事件的 bean 的范围属性,该属性包含在它的 bean 定义中。 Bean 没有作用域,作用域是在它们各自的 Bean 定义中定义的。我检查了其他解决方案,它绑定到一般请求范围,但我认为你有特定的范围,没有在 Spring 中定义。它绑定到任何 Web 请求的东西,不管它是否是 Vaadin UI Scope。无论如何,我很高兴你找到了解决这个问题的方法。干杯!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-03
    • 2011-01-11
    • 2013-07-04
    • 2011-05-28
    相关资源
    最近更新 更多