Scoped Spring events possible?

2020-07-10 03:36发布

问题:

The Spring event mechanism supports publishing application events and listening to these events within Spring components via the @EventListener annotation. However, I cannot find anything about sending events within a specific scope in the documentation. My specific need for Vaadin is:

  • in context of a user interaction, send an event (e.g. logged-in event)
  • this event should only be consumed by beans in same @UIScope, i.e. other user UIs shouldn't be effected

Is that possible? NOTE: This is not really specific to Vaadin. I could also ask how it would be done with Spring web mvc request scope.

回答1:

TL;DR : I think it already works as you wanted it

Long version :

The documentation is somewhat unclear about this, but having tried a simple test :

A controller that publishes events :

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

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

And a scoped bean that listens :

@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());
    }
}

And when running two concurrent requests, only the listener scoped to the the same httprequest gets the event.


My interpretation (without having looked at it really deeply, so take it with a grain of salt) :

It seems to me that the ApplicationEventMulticaster's ListenerRetriever uses the BeanFactory to get the beans that were previously registered as listeners (by their names). And obviously, the factory will return a bean in the current scope.



回答2:

Please see if this is what you are looking for:

Main application :

   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);
        }

    }

Event :

 package com.fg7.evision.EventList;

    public class MyScopedEvent  {

        private String message;

        public MyScopedEvent( String message) {
            this.message = message;
        }

        public String getMessage() {
            return message;
        }
    }

Event listener scoped to Singleton scope only.

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();
    }


}