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 ApplicationListenerthrows 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