In Spring, you can use the ApplicationListener interface. If there are multiple methods that need to listen for events, you can use the ApplicationListener interface. It’s not very convenient to implement the ApplicationListener interface for each method in a class, so Spring provides another way to implement event listeners: using the @EventListener annotation
First, annotation usage
The annotated source code is as follows, with the following functions:
- Can be applied to methods
- The arguments can be class arrays and multiple events can be written
- Methods that use this annotation are fired when an event is published in the container, similar to implementing the ApplicationListener interface
/ * * @ author Stephane Nicoll * @ @ since 4.2 the author Sam Brannen * * @ see EventListenerMethodProcessor * /
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface EventListener {
@AliasFor("classes")Class<? >[] value()default {};
@AliasFor("value")Class<? >[] classes()default {};
String condition(a) default "";
}
Copy the code
From comments can see is the use of EventListenerMethodProcessor in the way that the processor to parse the EventListener annotations, EventListenerMethodProcessor mainly by the implementation of the interface SmartInitializingSingleton for processing, later analysis of the source code.
Ii. Case analysis
// Start the test class
@Test
public void TestMain(a){
// Create an IOC container
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
// Publish an event yourself
applicationContext.publishEvent(new ApplicationEvent("Self-published events") {}); applicationContext.close(); }// The event is triggered
@Service
public class UserService {
// This method fires when an event is published in the container
@EventListener(classes = {ApplicationEvent.class})
public void listener1(ApplicationEvent event){
System.out.println("Monitored Event 1:" + event);
}
@EventListener(classes = {ApplicationEvent.class})
public void listener2(ApplicationEvent event){
System.out.println("Monitored Event 2:"+ event); }}/ / configuration class
@ComponentScan("listener")
@Configuration
public class AppConfig {}Copy the code
Run the launch class and you can see that both events are raised, using the @EventListener annotation to make it easier for multiple methods to fire
Third, source code analysis
Said above is using EventListenerMethodProcessor in the way that the processor to parse the EventListener annotations, point into EventListenerMethodProcessor view, Found that implements SmartInitializingSingleton interface, the main is through the interface implementation.
public class EventListenerMethodProcessor implements SmartInitializingSingleton.ApplicationContextAware.BeanFactoryPostProcessor {}
Copy the code
public interface SmartInitializingSingleton {
/**
* Invoked right at the end of the singleton pre-instantiation phase,
* with a guarantee that all regular singleton beans have been created
* already. {@linkListableBeanFactory#getBeansOfType} calls within * this method won't trigger accidental side effects during bootstrap. * <p><b>NOTE:</b> This callback won't be triggered for singleton beans
* lazily initialized on demand after {@link BeanFactory} bootstrap,
* and not for any other bean scope either. Carefully use it for beans
* with the intended bootstrap semantics only.
*/
void afterSingletonsInstantiated(a);
}
Copy the code
A afterSingletonsInstantiated SmartInitializingSingleton interface method, when single instance bean has been created, all will trigger this interface, perform afterSingletonsInstantiated method, Similar to ContextRefreshedEvent
On afterSingletonsInstantiated method for our breakpoints, look at the source code is when calling this method of execution.
Through the method call stack, the container creates objects, Call the refresh () method to refresh the container — > finishBeanFactoryInitialization (the beanFactory) – > preInstantiateSingletons bean () initializes the rest of the single instance
- Create all single-instance beans
- Get all the beans, create good single instance to determine whether each bean SmartInitializingSingleton type
- If it is call afterSingletonsInstantiated method
Here in our analysis of the above SmartInitializingSingleton# afterSingletonsInstantiated method, namely @ EventListener annotation annotations work place
@Override
public void preInstantiateSingletons(a) throws BeansException {
if (logger.isTraceEnabled()) {
logger.trace("Pre-instantiating singletons in " + this);
}
// Iterate over a copy to allow for init methods which in turn register new bean definitions.
// While this may not be part of the regular factory bootstrap, it does otherwise work fine.
List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
// Iterate over the beanName to create the bean, i.e. initialize the non-lazy-loaded singleton bean
for (String beanName : beanNames) {
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
if(! bd.isAbstract() && bd.isSingleton() && ! bd.isLazyInit()) {if (isFactoryBean(beanName)) {
Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
if (bean instanceofFactoryBean) { FactoryBean<? > factory = (FactoryBean<? >) bean;boolean isEagerInit;
if(System.getSecurityManager() ! =null && factory instanceofSmartFactoryBean) { isEagerInit = AccessController.doPrivileged( (PrivilegedAction<Boolean>) ((SmartFactoryBean<? >) factory)::isEagerInit, getAccessControlContext()); }else {
isEagerInit = (factory instanceofSmartFactoryBean && ((SmartFactoryBean<? >) factory).isEagerInit()); }if(isEagerInit) { getBean(beanName); }}}else {
// Create an objectgetBean(beanName); }}}// Trigger post-initialization callback for all applicable beans...
/ / after creating the bean to judge whether the bean implements SmartInitializingSingleton, if is execute smartSingleton afterSingletonsInstantiated () method
for (String beanName : beanNames) {
Object singletonInstance = getSingleton(beanName);
if (singletonInstance instanceof SmartInitializingSingleton) {
StartupStep smartInitialize = this.getApplicationStartup().start("spring.beans.smart-initialize")
.tag("beanName", beanName);
SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
if(System.getSecurityManager() ! =null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
/ / afterSingletonsInstantiated execution
smartSingleton.afterSingletonsInstantiated();
return null;
}, getAccessControlContext());
}
else{ smartSingleton.afterSingletonsInstantiated(); } smartInitialize.end(); }}}Copy the code
Four,
- The IOC container creates objects and refreshes them
- FinishBeanFactoryInitialization (the beanFactory) – > preInstantiateSingletons () to initialize the remaining single instance of the bean
- Create all single-instance beans
- Get all the beans, create good single instance to determine whether each bean SmartInitializingSingleton type
- If it is call afterSingletonsInstantiated method