I use events a lot. They let me think In terms of clean separate of modules, in terms of events, not data/state. I use ApplicationEvent and ApplicationListener a lot. There's at least one in every Spring Boot app I've ever written. In anything more serious than a demo, I use them a ton, as they help me keep my API surface area clean - I only export and work with types. This in turn makes it easier to move to things like CQRS or microservices. I love events and they're implemented well in Spring.

@EventListener 
public void applicationReady (ApplicationReadyEvent are)  throws Exception {
   System.out.println( "the application is ready!" ) ;
} 

The trouble is that I often need dependencies, so I end up creating a @Bean:

@Bean 
ApplicationListener<ApplicationReadyEvent> applicationReady (A a ) { 
 return event ->    System.out.println( "the application is ready? " + (a.isReady()) ) ;
}

This is a problem because a) i have to use ApplicationEvent subclasses again (I like not having to use ApplicationEvent with the plain @EventListener b) i am forced to handle exceptions because ApplicationListener doesn't have throws Throwable on its signature.

So, my dream use case is:

@EventListener 
public void applicationReady ( ApplicationReadyEvent are ,  A a)  throws Throwable { 
   System.out.println( "the application is ready? " + (a.isReady()) ) ;
}

I'd be willing to settle for having to declare @Autowired on the beans that are injected (A a) or , alternatively a new annotation to disambiguate (say, @ApplicationEvent on the instance of the event)

If I could have anything I wanted for my birthday, at the end of January, I'd also love to be able to have package private @EventListener-annotated methods that are not public :)

Thanks in advance for an amazing piece of software.

Comment From: odrotbohm

Where exactly does the first method you show live? To become a listener, it has to live in a bean, which is subject for DI. In other words: wouldn't you just inject the needed dependency into the bean that declares the event listener method?

Also, @EventListener works like a charm on package protected methods.

Comment From: joshlong

good to know on the package protected methods! thanks

I often put these listeners in @Configuration classes. I often put them in the Spring Boot main class, but sometimes in package-specific configuration classes, in the same class as the Configuration class that exports the dependency, which creates a circular dependency, so I have to extract them out to another component, which means I have another full class, which again is what I'm trying to avoid. not everything in the world needs a class. Sometimes an event listener is just a function/method invocation.

Comment From: sbrannen

in the same class as the Configuration class that exports the dependency, which creates a circular dependency, so I have to extract them out to another component, which means I have another full class,

What happens if you make the injection point @Lazy or use an ObjectProvider -- to avoid the circular dependency?

Also, regarding having dependencies injected as method arguments in an @EventListener method, doing so would require that the @Autowired dependencies be resolved for each method invocation of the @EventListener method (unless the resolved dependencies were somehow cached for each @EventListener method, but I'm not sure we would want to introduce a cache for that purpose). So it might not be feasible to provide such support. Instead, we recommend that dependencies of an @EventListener method be injected into the enclosing instance.

Comment From: sbrannen

Related Issues

  • 18628

  • 18629

Comment From: joshlong

I think having a cache seems reasonable enough. Spring maintains a cache for beans. SIngletons are going to be the most common. That’ll be a quick constant time operation, right? If the beans are scoped, then of course that’ll require re-evaluation but that’s true for the much more verbose alternative of having fields in a containing class, too.

Comment From: snicoll

Thanks for the suggestion but I think it would be at odd with everything else we're doing. If you look at our method invocation mechanism, we do support injections of things that are related to the task at hand. The best example of this is @RequestMapping that provides a wide range of options based on the HTTP request/response. We don't offer a way to inject a bean in there and I think we should continue to do that for consistency at the very least.

Comment From: chkpnt

@joshlong If you do not want to inject the bean into your @Configuration class, you can request a bean from the ApplicationContext. Something like A a = ApplicationContextProvider.getApplicationContext().getBean(A.class).

Comment From: joshlong

Thank you for the suggestion