• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

TAKETODAY / today-infrastructure / 18154768944

01 Oct 2025 07:26AM UTC coverage: 81.882% (-0.005%) from 81.887%
18154768944

push

github

web-flow
Merge pull request #290 from TAKETODAY/dev/jspecify

jspecify

59788 of 78013 branches covered (76.64%)

Branch coverage included in aggregate %.

141239 of 167496 relevant lines covered (84.32%)

3.6 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

88.0
today-context/src/main/java/infra/context/event/EventListenerMethodProcessor.java
1
/*
2
 * Copyright 2017 - 2025 the original author or authors.
3
 *
4
 * This program is free software: you can redistribute it and/or modify
5
 * it under the terms of the GNU General Public License as published by
6
 * the Free Software Foundation, either version 3 of the License, or
7
 * (at your option) any later version.
8
 *
9
 * This program is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 * GNU General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU General Public License
15
 * along with this program. If not, see [https://www.gnu.org/licenses/]
16
 */
17

18
package infra.context.event;
19

20
import org.jspecify.annotations.Nullable;
21

22
import java.lang.reflect.Method;
23
import java.util.HashSet;
24
import java.util.List;
25
import java.util.Set;
26

27
import infra.aop.framework.autoproxy.AutoProxyUtils;
28
import infra.aop.scope.ScopedObject;
29
import infra.aop.scope.ScopedProxyUtils;
30
import infra.aop.support.AopUtils;
31
import infra.beans.factory.BeanInitializationException;
32
import infra.beans.factory.SmartInitializingSingleton;
33
import infra.beans.factory.config.BeanFactoryPostProcessor;
34
import infra.beans.factory.config.ConfigurableBeanFactory;
35
import infra.context.ApplicationContext;
36
import infra.context.ApplicationContextAware;
37
import infra.context.ApplicationListener;
38
import infra.context.ConfigurableApplicationContext;
39
import infra.context.expression.BeanFactoryResolver;
40
import infra.core.MethodIntrospector;
41
import infra.core.annotation.AnnotatedElementUtils;
42
import infra.core.annotation.AnnotationAwareOrderComparator;
43
import infra.core.annotation.AnnotationUtils;
44
import infra.expression.spel.support.StandardEvaluationContext;
45
import infra.lang.Assert;
46
import infra.logging.Logger;
47
import infra.logging.LoggerFactory;
48
import infra.stereotype.Component;
49
import infra.util.ClassUtils;
50
import infra.util.CollectionUtils;
51

52
/**
53
 * Process @EventListener annotated on a method
54
 *
55
 * <p>
56
 * Registers {@link EventListener} methods as individual {@link ApplicationListener} instances.
57
 * Implements {@link BeanFactoryPostProcessor} primarily for early retrieval,
58
 * avoiding AOP checks for this processor bean and its {@link EventListenerFactory} delegates.
59
 *
60
 * @author <a href="https://github.com/TAKETODAY">Harry Yang</a>
61
 * @see EventListenerFactory
62
 * @see DefaultEventListenerFactory
63
 * @since 2021/3/17 12:35
64
 */
65
public class EventListenerMethodProcessor implements SmartInitializingSingleton, ApplicationContextAware {
2✔
66

67
  protected final Logger logger = LoggerFactory.getLogger(getClass());
6✔
68

69
  @Nullable
70
  private ConfigurableApplicationContext applicationContext;
71

72
  @Override
73
  public void setApplicationContext(ApplicationContext applicationContext) {
74
    Assert.isTrue(applicationContext instanceof ConfigurableApplicationContext,
4✔
75
            "ApplicationContext does not implement ConfigurableApplicationContext");
76
    this.applicationContext = (ConfigurableApplicationContext) applicationContext;
4✔
77
  }
1✔
78

79
  @Override
80
  public void afterSingletonsInstantiated(ConfigurableBeanFactory beanFactory) {
81
    Set<Class<?>> nonAnnotatedClasses = new HashSet<>();
4✔
82

83
    var factories = beanFactory.getBeans(EventListenerFactory.class);
4✔
84
    AnnotationAwareOrderComparator.sort(factories);
2✔
85
    StandardEvaluationContext shared = new StandardEvaluationContext();
4✔
86
    shared.setBeanResolver(new BeanFactoryResolver(beanFactory));
6✔
87
    EventExpressionEvaluator evaluator = new EventExpressionEvaluator(shared);
5✔
88

89
    var beanNames = beanFactory.getBeanNamesForType(Object.class);
4✔
90
    for (String beanName : beanNames) {
16✔
91
      if (ScopedProxyUtils.isScopedTarget(beanName)) {
3✔
92
        continue;
1✔
93
      }
94
      Class<?> type = null;
2✔
95
      try {
96
        type = AutoProxyUtils.determineTargetClass(beanFactory, beanName);
4✔
97
      }
98
      catch (Throwable ex) {
×
99
        // An unresolvable bean type, probably from a lazy bean - let's ignore it.
100
        logger.debug("Could not resolve target class for bean with name '{}'", beanName, ex);
×
101
      }
1✔
102
      if (type != null) {
2!
103
        if (ScopedObject.class.isAssignableFrom(type)) {
4✔
104
          try {
105
            Class<?> targetClass = AutoProxyUtils.determineTargetClass(
4✔
106
                    beanFactory, ScopedProxyUtils.getTargetBeanName(beanName));
1✔
107
            if (targetClass != null) {
2!
108
              type = targetClass;
2✔
109
            }
110
          }
111
          catch (Throwable ex) {
1✔
112
            // An invalid scoped proxy arrangement - let's ignore it.
113
            logger.debug("Could not resolve target bean for scoped proxy '{}'", beanName, ex);
6✔
114
          }
1✔
115
        }
116
        try {
117
          process(beanName, type, factories, evaluator, nonAnnotatedClasses);
7✔
118
        }
119
        catch (Throwable ex) {
1✔
120
          throw new BeanInitializationException("Failed to process @EventListener annotation on bean with name '%s': %s"
12✔
121
                  .formatted(beanName, ex.getMessage()), ex);
6✔
122
        }
1✔
123
      }
124
    }
125
  }
1✔
126

127
  private void process(String beanName, Class<?> targetType,
128
          List<EventListenerFactory> factories, EventExpressionEvaluator evaluator, Set<Class<?>> nonAnnotatedClasses) {
129

130
    if (!nonAnnotatedClasses.contains(targetType)
6✔
131
            && AnnotationUtils.isCandidateClass(targetType, EventListener.class)
3✔
132
            && !isInfraContainerClass(targetType)) {
2✔
133

134
      Set<Method> annotatedMethods = null;
2✔
135
      try {
136
        annotatedMethods = MethodIntrospector.filterMethods(
4✔
137
                targetType, method -> AnnotatedElementUtils.hasAnnotation(method, EventListener.class));
4✔
138
      }
139
      catch (Throwable ex) {
×
140
        // An unresolvable type in a method signature, probably from a lazy bean - let's ignore it.
141
        logger.debug("Could not resolve methods for bean with name '{}'", beanName, ex);
×
142
      }
1✔
143

144
      if (CollectionUtils.isEmpty(annotatedMethods)) {
3✔
145
        nonAnnotatedClasses.add(targetType);
4✔
146
        logger.trace("No @EventListener annotations found on bean class: {}", targetType.getName());
7✔
147
      }
148
      else {
149
        // Non-empty set of methods
150
        ConfigurableApplicationContext context = this.applicationContext;
3✔
151
        Assert.state(context != null, "No ApplicationContext set");
6!
152
        for (Method method : annotatedMethods) {
10✔
153
          for (EventListenerFactory factory : factories) {
10!
154
            if (factory.supportsMethod(method)) {
4✔
155
              Method methodToUse = AopUtils.selectInvocableMethod(method, context.getType(beanName));
6✔
156
              var listener = factory.createApplicationListener(beanName, targetType, methodToUse);
6✔
157
              if (listener instanceof ApplicationListenerMethodAdapter adapter) {
6!
158
                adapter.init(context, evaluator);
4✔
159
              }
160
              context.addApplicationListener(listener);
3✔
161
              break;
1✔
162
            }
163
          }
1✔
164
        }
1✔
165
        if (logger.isDebugEnabled()) {
4!
166
          logger.debug("{} @EventListener methods processed on bean '{}': {}",
×
167
                  annotatedMethods.size(), beanName, annotatedMethods);
×
168
        }
169
      }
170
    }
171
  }
1✔
172

173
  /**
174
   * Determine whether the given class is an {@code infra}
175
   * bean class that is not annotated as a user or test {@link Component}...
176
   * which indicates that there is no {@link EventListener} to be found there.
177
   */
178
  private static boolean isInfraContainerClass(Class<?> clazz) {
179
    return clazz.getName().startsWith("infra.")
7✔
180
            && !AnnotatedElementUtils.isAnnotated(ClassUtils.getUserClass(clazz), Component.class);
7✔
181
  }
182

183
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc