DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Related

  • Leveraging Salesforce Using a Client Written In Vue.js
  • Introduction to Apache Kafka With Spring
  • MongoDB Change Streams and Go
  • A Practical Guide to Creating a Spring Modulith Project

Trending

  • Engineering LLMOps: Building Robust CI/CD Pipelines for LLM Applications on Google Cloud
  • Retesting Best Practices for Agile Teams: A Quick Guide to Bug Fix Verification
  • The Network Attach Problem Nobody Warns You About
  • Jakarta EE 12: Entering the Data Age of Enterprise Java
  1. DZone
  2. Coding
  3. Frameworks
  4. Spring Application Listeners

Spring Application Listeners

This review is focused on the basic concepts of Spring application listeners and has some tips on how to disable them when you need them.

By 
K.D. Prog user avatar
K.D. Prog
·
Jan. 29, 24 · Review
Likes (1)
Comment
Save
Tweet
Share
9.3K Views

Join the DZone community and get the full member experience.

Join For Free

I've recently faced a problem when disabling application listeners created using org.springframework.context.event.EventListener annotation for org.springframework.context.event.ContextRefreshedEvent event in unit tests. I found several decisions and decided to share them.

Quick Guide To Spring Application Listeners

First of all, here are some words about key objects that Spring uses when handling application events.

Application Event

Application events are implementations of org.springframework.context.ApplicationEvent. Actually, Spring allows the use of any objects for publishing and handling. This is achieved by using org.springframework.context.PayloadApplicationEvent. It extends org.springframework.context.ApplicationEvent and is used as an adapter. Finally, we can say that application events are DTOs that keep specific information for each event.

Publisher

Publishers are implementations of org.springframework.context.ApplicationEventPublisher interface. They publish events to listeners. In most cases, org.springframework.context.ApplicationContext implementation is used as publisher. It delegates publishing to multicaster. Here is a code example,

Java
 
		// Multicast right now if possible - or lazily once the multicaster is initialized
		if (this.earlyApplicationEvents != null) {
			this.earlyApplicationEvents.add(applicationEvent);
		}
		else {
			getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
		}

		// Publish event via parent context as well...
		if (this.parent != null) {
			if (this.parent instanceof AbstractApplicationContext abstractApplicationContext) {
				abstractApplicationContext.publishEvent(event, eventType);
			}
			else {
				this.parent.publishEvent(event);
			}
		}

Multicaster

Multicasters are implementations of org.springframework.context.event.ApplicationEventMulticaster interface. Its main purpose is to publish events to listeners as well as for publisher. 

But, besides publisher, multicaster stores all application listeners, manages them, and determines which of them should fire for specific events. 

By default, Spring uses org.springframework.context.event.SimpleApplicationEventMulticaster. 

Actually, Spring is looking for a bean named AbstractApplicationContext.APPLICATION_EVENT_MULTICASTER_BEAN_NAME, if it exists in the application context, Spring uses this bean; otherwise, it creates an instance of SimpleApplicationEventMulticaster and adds it as a singleton bean to beanFactory.

Here is a code fragment. 

Java
 
 protected void initApplicationEventMulticaster() {

        ConfigurableListableBeanFactory beanFactory = getBeanFactory();

        if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {

            this.applicationEventMulticaster =

                    beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);

            if (logger.isTraceEnabled()) {

                logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");

            }

        }

        else {

            this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);

            beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);

            if (logger.isTraceEnabled()) {

                logger.trace("No '" + APPLICATION_EVENT_MULTICASTER_BEAN_NAME + "' bean, using " +

                        "[" + this.applicationEventMulticaster.getClass().getSimpleName() + "]");

            }

        }

    }


All this stuff is done when executing ConfigurableApplicationContext#refresh() method right before registering application listeners and after applying BeanFactoryPostProcessors and registering messageSources.

So, if you want to replace the default implementation of this bean, you have to create it before and use the same bean name. I will come back to it later.

ApplicationListener

I divided all listeners into two different groups because they created and registered in different ways. When I say 'register listener' I mean to add it to multicaster.

ApplicationListener Listener

'ApplicationListener listerners' are implementations of org.springframework.context.ApplicationListener or org.springframework.context.event.SmartApplicationListener interface. These listeners are beans and they are created as all other beans. 

They can be ordered using annotation org.springframework.core.annotation.Order or interfaces org.springframework.core.Ordered and org.springframework.core.PriorityOrdered and process events according to this order.

Such listeners are registered during ConfigurableApplicationContext#refresh() execution right after initializing multicaster and before initializing org.springframework.beans.factory.SmartInitializingSingletons at the stage of finishing singleton initialization.

Be also aware that within this step, all early events are published. Early events are all events that are published before the multicaster is initialized in an application context.

Early events are handled only by 'ApplicationListener listerner'. Another type of application listener won't process such an event. 

Keep it in mind if you want to disable such listeners. You will probably have to replace the default ApplicationEventMulticaster bean for this purpose. There will be more details later.

@EventListener Listener

These are listeners created using org.springframework.context.event.EventListener annotation. They are processed by org.springframework.context.event.EventListenerMethodProcessor.

If the bean method contains this annotation, EventListenerMethodProcessor creates instance of ApplicationListener<?> or SmartApplicationListener using org.springframework.context.event.EventListenerFactory.EventListenerFactory based on annotation parameters and adds it to AbstractApplicationContext.applicationEventMulticaster.

You can create as many EventListenerFactory as you need and EventListenerMethodProcessor will create the same amount of listeners for each method. Here is a code fragment from EventListenerMethodProcessor.          

Java
 
for (Method method : annotatedMethods.keySet()) {
	for (EventListenerFactory factory : factories) {
		if (factory.supportsMethod(method)) {
			Method methodToUse = AopUtils.selectInvocableMethod(method, context.getType(beanName));
			ApplicationListener<?> applicationListener = factory.createApplicationListener(beanName, targetType, methodToUse);
			if (applicationListener instanceof ApplicationListenerMethodAdapter) {
                ((ApplicationListenerMethodAdapter) applicationListener).init(context, this.evaluator);
			}
			context.addApplicationListener(applicationListener);
			break;
		}
	}
}


Spring itself has a default implementation org.springframework.context.event.DefaultEventListenerFactory , which is enough in many cases. DefaultEventListenerFactory#createApplicationListener(String,Class<?>,Method) creates org.springframework.context.event.ApplicationListenerMethodAdapter instance, that implements SmartApplicationListener.

The bean name for DefaultEventListenerFactory is AnnotationConfigUtils.EVENT_LISTENER_FACTORY_BEAN_NAME, so if you are going to replace this bean, take it into account.

As mentioned above all '@EventListener listener' are created and registered in EventListenerMethodProcessor, to be more precise in EventListenerMethodProcessor#afterSingletonsInstantiated(), implementation of org.springframework.beans.factory.SmartInitializingSingleton. All SmartInitializingSingletons are initialized in ConfigurableListableBeanFactory#preInstantiateSingletons() executed in ConfigurableApplicationContext#refresh().

ConfigurableListableBeanFactory#preInstantiateSingletons()  is executed almost at the end of application context refresh, right after registering listener beans, i.e. 'ApplicationListener listeners', and before initializing lifecycle beans.

So, listeners are registered before initializing lifecycle beans. Let's keep it in mind.

Now, let's take a look at different approaches to disabling application listeners.

Using org.springframework.context.event.EventListener@#condition

org.springframework.context.event.EventListener has an attribute condition which is the SPeL expression used for making the event handling conditional. The event will be handled if the expression evaluates to a boolean true or one of the following strings: "true," "on," "yes," or "1".

It's possible to switch off listeners by application properties parameter, for example:

Java
 
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

@Component
class TestListener {

    @EventListener(condition = "@environment.getProperty('listeners.enabled')")
    void handle(final ContextRefreshedEvent e) {
        System.out.println("@EventListener works!");
    }
}


I used a bean reference to org.springframework.core.env.Environment, but not predefined environment['listeners.enabled'], because EventListenerMethodProcessor configures its own SPeL context, which is an instance of org.springframework.context.expression.MethodBasedEvaluationContext, and it does not contain required org.springframework.expression.PropertyAccessor for org.springframework.core.env.Environment like here:

Java
 
  public static void main(final String[] args) {
        final var app = SpringApplication.run(Main.class, args);
        final var expression = new SpelExpressionParser().parseExpression("environment['listeners.enabled']");
        final var beanExpressionContext = new BeanExpressionContext(app.getBeanFactory(), null);
        final var sec = new StandardEvaluationContext(beanExpressionContext);
        sec.addPropertyAccessor(new BeanExpressionContextAccessor());
        sec.addPropertyAccessor(new EnvironmentAccessor());
        final var value = expression.getValue(sec, Boolean.class);
        org.springframework.util.Assert.isTrue(value, "Incorrect 'listeners.enabled' value");
    }


Using org.springframework.boot.autoconfigure.condition.ConditionalOnProperty

This approach is also applicable to 'ApplicationListener listerners' as well. As I mentioned above, these listeners are beans and managed by Spring application context, at least created if you use prototype beans, so simply create beans marked with org.springframework.boot.autoconfigure.condition.ConditionalOnProperty

Java
 
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;

@Component
@ConditionalOnProperty(name = "listeners.enabled", havingValue = "true")
class TestApplicationListener implements ApplicationListener<ContextRefreshedEvent> {
    @Override
    public void onApplicationEvent(final ContextRefreshedEvent event) {
        System.out.println("ApplicationListener works");
    }
}


You can do the same for '@EventListener listener's; however, if you have several methods marked with @EventListener annotation, you will disable all of them.

Using org.springframework.context.SmartLifecycle Bean

As it was mentioned above multicaster and all listeners are registered before initializing lifecycle beans. As soon as lifecycle beans are initialized and started, the application context publishes org.springframework.context.event.ContextRefreshedEvent event.

It means that all beans are created and initialized. All custom events are mostly published after context initialization, especially events for '@EventListener listeners,' so this approach is also applicable in many cases.

The idea is to register a bean that implements org.springframework.context.SmartLifecycle interface and disables listeners when starting. 

Here it is:

Java
 
import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.context.SmartLifecycle;
import org.springframework.context.event.ApplicationEventMulticaster;
import org.springframework.context.event.SmartApplicationListener;

import java.util.Arrays;
import java.util.Collections;
import java.util.Set;
import java.util.stream.Collectors;

import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;

public class DisableEventListenersBean implements SmartLifecycle {
    private final Set<String> ids;
    private final Set<String> names;
    private final Set<Class<?>> types;
    private final ApplicationEventMulticaster multicaster;

    public DisableEventListenersBean(
            final ApplicationEventMulticaster multicaster,
            final String... ids
    ) {
        this.multicaster = multicaster;
        this.types = Collections.emptySet();
        this.names = Collections.emptySet();
        this.ids = Arrays.stream(ids).collect(Collectors.toSet());
    }

    public DisableEventListenersBean(
            final ApplicationContext context,
            final ApplicationEventMulticaster multicaster,
            final Class<?>... types
    ) {
        this(
                Collections.emptySet(), Arrays.stream(types).collect(Collectors.toSet()), context, multicaster
        );
    }

    public DisableEventListenersBean(
            final Set<String> ids,
            final Set<Class<?>> types,
            final ApplicationContext context,
            final ApplicationEventMulticaster multicaster
    ) {
        this.multicaster = multicaster;
        this.ids = defaultIfNull(ids, Collections.emptySet());
        this.types = defaultIfNull(types, Collections.emptySet());
        this.names = types.stream().map(context::getBeanNamesForType).flatMap(Arrays::stream).collect(Collectors.toSet());
    }

    @Override
    public void start() {
        multicaster.removeApplicationListeners(
                listener -> {
                    final boolean toRemove;
                    if (listener instanceof SmartApplicationListener smart) {
                        toRemove = ids.contains(smart.getListenerId()) || shouldDisableForType(listener);
                    } else {
                        toRemove = shouldDisableForType(listener);
                    }
                    return toRemove;
                }
        );
        multicaster.removeApplicationListenerBeans(
                names::contains
        );
    }

    private boolean shouldDisableForType(final ApplicationListener<?> type) {
        final var actual = AopProxyUtils.ultimateTargetClass(type);
        return types.contains(actual);
    }

    @Override
    public void stop() {

    }

    @Override
    public boolean isRunning() {
        return false;
    }
}


When discussing 'ApplicationListener listerner' I mentioned org.springframework.context.event.SmartApplicationListener interface. 

It has additional methods, one of which is org.springframework.context.event.SmartApplicationListener#getListenerId().

Moreover, org.springframework.context.event.EventListener annotation has the corresponding attribute org.springframework.context.event.EventListener#id and it has the default value constructed like "mypackage.MyClass.myMethod()".

As discussed above '@EventListener listeners' are registered using EventListenerFactories. DefaultEventListenerFactory creates ApplicationListenerMethodAdapter instances and add them to multicaster, so they all have the same type and org.springframework.context.event.SmartApplicationListener#getListenerId() is the only parameter that helps to identify them. 

That's why I used it in my code.

However, org.springframework.context.event.SmartApplicationListener#getListenerId() is optional and might be an empty string, so it will not be superfluous to check types as well.

Besides, when removing 'ApplicationListener listerner' we have to remove them from both applicationListeners and applicationListenerBeans at least for my Spring version 6.04 (Spring Boot version 3.02).

I also added SmartApplicationListener implementation in addition to what was already provided above TestApplicationListener and TestListener.

Java
 
import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.SmartApplicationListener;
import org.springframework.stereotype.Component;

@Component
class TestSmartApplicationListener implements SmartApplicationListener {

    @Override
    public boolean supportsEventType(final Class<? extends ApplicationEvent> eventType) {
        return ContextRefreshedEvent.class.isAssignableFrom(eventType);
    }

    @Override
    public void onApplicationEvent(final ApplicationEvent event) {
        System.out.println("SmartApplicationListener works");
    }
}


Ensure listeners.enabled = true in your application.properties in order to be sure that TestApplicationListener and TestListener are not disabled by application properties parameter.

Here are several examples of the configuration of DisableEventListenersBean bean.

Java
 

import org.springframework.context.ApplicationContext;
import org.springframework.context.SmartLifecycle;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.ApplicationEventMulticaster;

import java.util.Set;

@Configuration
class CustomApplicationEventConfig {

//    @Bean
//    SmartLifecycle disableEventListenersBean(final ApplicationEventMulticaster multicaster) {
//        return new DisableEventListenersBean(
//                multicaster,
//                "com.example.demo.events.app.TestListener.handle(org.springframework.context.event.ContextRefreshedEvent)"
//        );
//    }

//    @Bean
//    SmartLifecycle disableEventListenersBean(
//            final ApplicationContext context, final ApplicationEventMulticaster multicaster
//    ) {
//        return new DisableEventListenersBean(
//                context,
//                multicaster,
//                TestApplicationListener.class
//        );
//    }
    @Bean
    SmartLifecycle disableEventListenersBean(
            final ApplicationContext context, final ApplicationEventMulticaster multicaster
    ) {
        return new DisableEventListenersBean(
                Set.of(
                        "com.example.demo.events.app.TestListener.handle(org.springframework.context.event.ContextRefreshedEvent)"
                ),
                Set.of(
                       TestApplicationListener.class, TestSmartApplicationListener.class
                ),
                context,
                multicaster
        );
    }
}


The last example disables all my application listeners.

Replacing Default org.springframework.context.event.EventListenerFactory bean

If the previous implementation is not enough to disable '@EventListener listeners,' let's replace the default org.springframework.context.event.EventListenerFactory bean in the application context to not create any instances of SmartApplicationListener for the specific annotated methods.

I simplified factory implementation, for example, purpose, to process only types. I think if you need to disable only some annotated methods within the same class, you can easily add method information, i.e., method name and parameter type. 

So, here is the factory:

Java
 

import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ApplicationListenerMethodAdapter;
import org.springframework.context.event.EventListenerFactory;
import org.springframework.util.ClassUtils;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Set;
import java.util.stream.Collectors;

public class DisableEventListenerFactory implements EventListenerFactory {
    private final Set<Class<?>> types;

    public DisableEventListenerFactory(final Class<?>... types) {
        this.types = Arrays.stream(types).collect(Collectors.toSet());
    }

    @Override
    public boolean supportsMethod(final Method method) {
        return true;
    }

    @Override
    public ApplicationListener<?> createApplicationListener(
            final String name, final Class<?> type, final Method method
    ) {
        final ApplicationListener<?> listener;
        if (types.contains(ClassUtils.getUserClass(type))) {
            listener = event -> {};
        } else {
            listener = new ApplicationListenerMethodAdapter(name, type, method);
        }
        return listener;
    }
}


Now, let's register the bean. Here is the implementation of org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor

Java
 
import lombok.SneakyThrows;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.ConstructorArgumentValues;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.AnnotationConfigUtils;
import org.springframework.stereotype.Component;

@Component
class RegisterEventListenerFactoryProcessor implements BeanDefinitionRegistryPostProcessor {
    @Override
    public void postProcessBeanDefinitionRegistry(final BeanDefinitionRegistry registry) throws BeansException {
        if (registry.containsBeanDefinition(AnnotationConfigUtils.EVENT_LISTENER_FACTORY_BEAN_NAME)) {
            registry.removeBeanDefinition(AnnotationConfigUtils.EVENT_LISTENER_FACTORY_BEAN_NAME);
        }
        registry.registerBeanDefinition(
                AnnotationConfigUtils.EVENT_LISTENER_FACTORY_BEAN_NAME,
                createDisableEventListenerFactoryBeanDefinition()
        );
    }

    @Override
    public void postProcessBeanFactory(final ConfigurableListableBeanFactory beanFactory) throws BeansException {

    }

    @SneakyThrows
    private RootBeanDefinition createDisableEventListenerFactoryBeanDefinition() {
        final var bd = new RootBeanDefinition();
        bd.setScope(BeanDefinition.SCOPE_SINGLETON);
        bd.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
        bd.setBeanClass(DisableEventListenerFactory.class);
        final var constructorValues = new ConstructorArgumentValues();
        constructorValues.addIndexedArgumentValue(
                0,
                new Class<?>[] {
                        Class.forName("com.example.demo.events.app.TestListener")
                }
        );
        bd.setConstructorArgumentValues(constructorValues);
        return bd;
    }
}


Default bean definition created in org.springframework.context.annotation.AnnotationConfigUtils .

Java
 
        if (!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) {
            RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class);
            def.setSource(source);
            beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME));
        }


Spring checks if the bean definition already exists and creates its own otherwise. As soon as Spring adds bean definition, we can't create beans like we used to because there will be two bean definitions and setting spring.main.allow-bean-definition-overriding=true will not give any guarantees that our implementation will be used.

I mean that such an approach:

Java
 
import lombok.SneakyThrows;
import org.springframework.context.annotation.AnnotationConfigUtils;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.EventListenerFactory;

@Configuration
class CustomApplicationEventConfig {

    @SneakyThrows
    @Bean(AnnotationConfigUtils.EVENT_LISTENER_FACTORY_BEAN_NAME)
    EventListenerFactory disableEventListenerFactory() {
        return new DisableEventListenerFactory(
                Class.forName("com.example.demo.events.app.TestListener")
        );
    }
}


will lead to error The bean 'org.springframework.context.event.internalEventListenerFactory', defined in class path resource [com/example/demo/events/app/CustomApplicationEventConfig.class], could not be registered. A bean with that name has already been defined and overriding is disabled.

You can switch on overriding by setting spring.main.allow-bean-definition-overriding=true  in application.properties, but be careful.

org.springframework.context.ApplicationContextInitializer can also be used.

Java
 
import lombok.SneakyThrows;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigUtils;

public class DisableEventListenerApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {

    @Override
    @SneakyThrows
    public void initialize(final ConfigurableApplicationContext context) {
        context.getBeanFactory()
                .registerSingleton(
                        AnnotationConfigUtils.EVENT_LISTENER_FACTORY_BEAN_NAME,
                        new DisableEventListenerFactory(
                                Class.forName("com.example.demo.events.app.TestListener")
                        )
                );
    }
}


Add META-INF/spring.factories to create initializer

Properties files
 
org.springframework.context.ApplicationContextInitializer = com.example.demo.events.app.DisableEventListenerApplicationContextInitializer


For test purposes, you can use org.springframework.test.context.ContextConfiguration as well:

Java
 
@ContextConfiguration(initializers = DisableEventListenerApplicationContextInitializer.class)

I used java.lang.Class.forName(String) everywhere because com.example.demo.events.app.TestListener has package-private access.

Replacing Default org.springframework.context.event.ApplicationEventMulticaster bean

To disable 'ApplicationListener listerners', but not '@EventListener listeners', in cases when approach described in 'Using org.springframework.context.SmartLifecycle bean' is not enough; default org.springframework.context.event.ApplicationEventMulticaster can be replaced.

As it was mentioned above, this bean is created during ConfigurableApplicationContext#refresh(). Spring first checks it is already in context and creates its own instance otherwise.

Here is multicaster itself.

Java
 
import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.SimpleApplicationEventMulticaster;

import java.util.Arrays;
import java.util.Set;
import java.util.stream.Collectors;

public class DisableApplicationEventMulticaster extends SimpleApplicationEventMulticaster {
    private final Set<Class<ApplicationListener<?>>> types;

    public DisableApplicationEventMulticaster(final Class<ApplicationListener<?>>... types) {
        this.types = Arrays.stream(types).collect(Collectors.toSet());
    }

    public DisableApplicationEventMulticaster(final BeanFactory factory, final Class<ApplicationListener<?>>... types) {
        super(factory);
        this.types = Arrays.stream(types).collect(Collectors.toSet());
    }

    @Override
    protected void invokeListener(final ApplicationListener<?> listener, final ApplicationEvent event) {
        if (!types.contains(AopProxyUtils.ultimateTargetClass(listener))) {
            super.invokeListener(listener, event);
        }
    }
}


It simply does not execute the listener`s methods when it is required.

Finally, create a bean.

Java
 
@Configuration
class CustomApplicationEventConfig {

    @SneakyThrows
    @Bean(AbstractApplicationContext.APPLICATION_EVENT_MULTICASTER_BEAN_NAME)
    ApplicationEventMulticaster disableApplicationEventMulticaster() {
        return new DisableApplicationEventMulticaster(
                (Class<ApplicationListener<?>>) Class.forName("com.example.demo.events.app.TestApplicationListener"),
                (Class<ApplicationListener<?>>) Class.forName("com.example.demo.events.app.TestSmartApplicationListener")
        );
    }
}


DisableApplicationEventMulticaster will disable all 'ApplicationListener listerners' which types are passed in its constructor, but not '@EventListener listeners'. For '@EventListener listeners' use another approach.

Conclusion

I used Spring Boot version 3.0.2 (Spring version 6.0.4) and Java 17 for the examples described above.

So, enjoy your application listeners and disable them when you need them!

Spring Framework application Event

Opinions expressed by DZone contributors are their own.

Related

  • Leveraging Salesforce Using a Client Written In Vue.js
  • Introduction to Apache Kafka With Spring
  • MongoDB Change Streams and Go
  • A Practical Guide to Creating a Spring Modulith Project

Partner Resources

×

Comments

The likes didn't load as expected. Please refresh the page and try again.

  • RSS
  • X
  • Facebook

ABOUT US

  • About DZone
  • Support and feedback
  • Community research

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 215
  • Nashville, TN 37211
  • [email protected]

Let's be friends:

  • RSS
  • X
  • Facebook