Skip to content

Commit dbec212

Browse files
committed
EventListenerMethodProcessor does not validate target classes behind proxies anymore
Issue: SPR-13526 Issue: SPR-13538
1 parent 28c07a6 commit dbec212

File tree

2 files changed

+41
-74
lines changed

2 files changed

+41
-74
lines changed

spring-context/src/main/java/org/springframework/context/event/EventListenerMethodProcessor.java

+23-57
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,7 @@
2828
import org.apache.commons.logging.Log;
2929
import org.apache.commons.logging.LogFactory;
3030

31-
import org.springframework.aop.SpringProxy;
3231
import org.springframework.aop.scope.ScopedProxyUtils;
33-
import org.springframework.aop.support.AopUtils;
3432
import org.springframework.beans.BeansException;
3533
import org.springframework.beans.factory.BeanInitializationException;
3634
import org.springframework.beans.factory.SmartInitializingSingleton;
@@ -48,6 +46,7 @@
4846
* instances.
4947
*
5048
* @author Stephane Nicoll
49+
* @author Juergen Hoeller
5150
* @since 4.2
5251
*/
5352
public class EventListenerMethodProcessor implements SmartInitializingSingleton, ApplicationContextAware {
@@ -61,6 +60,7 @@ public class EventListenerMethodProcessor implements SmartInitializingSingleton,
6160
private final Set<Class<?>> nonAnnotatedClasses =
6261
Collections.newSetFromMap(new ConcurrentHashMap<Class<?>, Boolean>(64));
6362

63+
6464
@Override
6565
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
6666
Assert.isTrue(applicationContext instanceof ConfigurableApplicationContext,
@@ -69,18 +69,6 @@ public void setApplicationContext(ApplicationContext applicationContext) throws
6969

7070
}
7171

72-
/**
73-
* Return the {@link EventListenerFactory} instances to use to handle {@link EventListener}
74-
* annotated methods.
75-
*/
76-
protected List<EventListenerFactory> getEventListenerFactories() {
77-
Map<String, EventListenerFactory> beans =
78-
this.applicationContext.getBeansOfType(EventListenerFactory.class);
79-
List<EventListenerFactory> allFactories = new ArrayList<EventListenerFactory>(beans.values());
80-
AnnotationAwareOrderComparator.sort(allFactories);
81-
return allFactories;
82-
}
83-
8472
@Override
8573
public void afterSingletonsInstantiated() {
8674
List<EventListenerFactory> factories = getEventListenerFactories();
@@ -91,16 +79,27 @@ public void afterSingletonsInstantiated() {
9179
try {
9280
processBean(factories, beanName, type);
9381
}
94-
catch (RuntimeException e) {
82+
catch (Throwable ex) {
9583
throw new BeanInitializationException("Failed to process @EventListener " +
96-
"annotation on bean with name '" + beanName + "'", e);
84+
"annotation on bean with name '" + beanName + "'", ex);
9785
}
9886
}
9987
}
10088
}
10189

102-
protected void processBean(List<EventListenerFactory> factories, String beanName, final Class<?> type) {
103-
Class<?> targetType = getTargetClass(beanName, type);
90+
/**
91+
* Return the {@link EventListenerFactory} instances to use to handle {@link EventListener}
92+
* annotated methods.
93+
*/
94+
protected List<EventListenerFactory> getEventListenerFactories() {
95+
Map<String, EventListenerFactory> beans =
96+
this.applicationContext.getBeansOfType(EventListenerFactory.class);
97+
List<EventListenerFactory> allFactories = new ArrayList<EventListenerFactory>(beans.values());
98+
AnnotationAwareOrderComparator.sort(allFactories);
99+
return allFactories;
100+
}
101+
102+
protected void processBean(List<EventListenerFactory> factories, String beanName, final Class<?> targetType) {
104103
if (!this.nonAnnotatedClasses.contains(targetType)) {
105104
final Set<Method> annotatedMethods = new LinkedHashSet<Method>(1);
106105
Method[] methods = ReflectionUtils.getUniqueDeclaredMethods(targetType);
@@ -111,13 +110,10 @@ protected void processBean(List<EventListenerFactory> factories, String beanName
111110
}
112111
for (EventListenerFactory factory : factories) {
113112
if (factory.supportsMethod(method)) {
114-
if (!type.equals(targetType)) {
115-
method = getProxyMethod(type, method);
116-
}
117113
ApplicationListener<?> applicationListener =
118-
factory.createApplicationListener(beanName, type, method);
114+
factory.createApplicationListener(beanName, targetType, method);
119115
if (applicationListener instanceof ApplicationListenerMethodAdapter) {
120-
((ApplicationListenerMethodAdapter)applicationListener)
116+
((ApplicationListenerMethodAdapter) applicationListener)
121117
.init(this.applicationContext, this.evaluator);
122118
}
123119
this.applicationContext.addApplicationListener(applicationListener);
@@ -127,49 +123,19 @@ protected void processBean(List<EventListenerFactory> factories, String beanName
127123
}
128124
}
129125
if (annotatedMethods.isEmpty()) {
130-
this.nonAnnotatedClasses.add(type);
126+
this.nonAnnotatedClasses.add(targetType);
131127
if (logger.isTraceEnabled()) {
132-
logger.trace("No @EventListener annotations found on bean class: " + type);
128+
logger.trace("No @EventListener annotations found on bean class: " + targetType);
133129
}
134130
}
135131
else {
136132
// Non-empty set of methods
137133
if (logger.isDebugEnabled()) {
138-
logger.debug(annotatedMethods.size() + " @EventListener methods processed on bean '" + beanName +
139-
"': " + annotatedMethods);
134+
logger.debug(annotatedMethods.size() + " @EventListener methods processed on bean '" +
135+
beanName + "': " + annotatedMethods);
140136
}
141137
}
142138
}
143139
}
144140

145-
private Class<?> getTargetClass(String beanName, Class<?> type) {
146-
if (SpringProxy.class.isAssignableFrom(type)) {
147-
Object bean = this.applicationContext.getBean(beanName);
148-
return AopUtils.getTargetClass(bean);
149-
}
150-
else {
151-
return type;
152-
}
153-
}
154-
155-
private Method getProxyMethod(Class<?> proxyType, Method method) {
156-
try {
157-
// Found a @EventListener method on the target class for this JDK proxy ->
158-
// is it also present on the proxy itself?
159-
return proxyType.getMethod(method.getName(), method.getParameterTypes());
160-
}
161-
catch (SecurityException ex) {
162-
ReflectionUtils.handleReflectionException(ex);
163-
}
164-
catch (NoSuchMethodException ex) {
165-
throw new IllegalStateException(String.format(
166-
"@EventListener method '%s' found on bean target class '%s', " +
167-
"but not found in any interface(s) for bean JDK proxy. Either " +
168-
"pull the method up to an interface or switch to subclass (CGLIB) " +
169-
"proxies by setting proxy-target-class/proxyTargetClass " +
170-
"attribute to 'true'", method.getName(), method.getDeclaringClass().getSimpleName()));
171-
}
172-
return null;
173-
}
174-
175141
}

spring-context/src/test/java/org/springframework/context/event/AnnotationDrivenEventListenerTests.java

+18-17
Original file line numberDiff line numberDiff line change
@@ -73,13 +73,15 @@ public class AnnotationDrivenEventListenerTests {
7373

7474
private CountDownLatch countDownLatch; // 1 call by default
7575

76+
7677
@After
7778
public void closeContext() {
7879
if (this.context != null) {
7980
this.context.close();
8081
}
8182
}
8283

84+
8385
@Test
8486
public void simpleEventJavaConfig() {
8587
load(TestEventListener.class);
@@ -241,13 +243,6 @@ public void eventListenerWorksWithInterfaceProxy() throws Exception {
241243
this.eventCollector.assertTotalEventsCount(1);
242244
}
243245

244-
@Test
245-
public void methodNotAvailableOnProxyIsDetected() throws Exception {
246-
thrown.expect(BeanInitializationException.class);
247-
thrown.expectMessage("handleIt2");
248-
load(InvalidProxyTestBean.class);
249-
}
250-
251246
@Test
252247
public void eventListenerWorksWithCglibProxy() throws Exception {
253248
load(CglibProxyTestBean.class);
@@ -409,6 +404,7 @@ public void orderedListeners() {
409404
assertThat(listener.order, contains("first", "second", "third"));
410405
}
411406

407+
412408
private void load(Class<?>... classes) {
413409
List<Class<?>> allClasses = new ArrayList<>();
414410
allClasses.add(BasicConfiguration.class);
@@ -450,6 +446,7 @@ public CountDownLatch testCountDownLatch() {
450446

451447
}
452448

449+
453450
static abstract class AbstractTestEventListener extends AbstractIdentifiable {
454451

455452
@Autowired
@@ -458,9 +455,9 @@ static abstract class AbstractTestEventListener extends AbstractIdentifiable {
458455
protected void collectEvent(Object content) {
459456
this.eventCollector.addEvent(this, content);
460457
}
461-
462458
}
463459

460+
464461
@Component
465462
static class TestEventListener extends AbstractTestEventListener {
466463

@@ -473,15 +470,16 @@ public void handle(TestEvent event) {
473470
public void handleString(String content) {
474471
collectEvent(content);
475472
}
476-
477473
}
478474

475+
479476
@EventListener
480477
@Target(ElementType.METHOD)
481478
@Retention(RetentionPolicy.RUNTIME)
482479
@interface FooListener {
483480
}
484481

482+
485483
@Component
486484
static class MetaAnnotationListenerTestBean extends AbstractTestEventListener {
487485

@@ -491,6 +489,7 @@ public void handleIt(TestEvent event) {
491489
}
492490
}
493491

492+
494493
@Component
495494
static class ContextEventListener extends AbstractTestEventListener {
496495

@@ -501,6 +500,7 @@ public void handleContextEvent(ApplicationContextEvent event) {
501500

502501
}
503502

503+
504504
@Component
505505
static class InvalidMethodSignatureEventListener {
506506

@@ -509,6 +509,7 @@ public void cannotBeCalled(String s, Integer what) {
509509
}
510510
}
511511

512+
512513
@Component
513514
static class ReplyEventListener extends AbstractTestEventListener {
514515

@@ -532,6 +533,7 @@ else if (event.content instanceof String) {
532533

533534
}
534535

536+
535537
@Component
536538
static class ExceptionEventListener extends AbstractTestEventListener {
537539

@@ -557,12 +559,14 @@ public void handleAsync(AnotherTestEvent event) {
557559
}
558560
}
559561

562+
560563
@Configuration
561564
@Import(BasicConfiguration.class)
562565
@EnableAsync(proxyTargetClass = true)
563566
static class AsyncConfiguration {
564567
}
565568

569+
566570
@Component
567571
static class AsyncEventListener extends AbstractTestEventListener {
568572

@@ -578,13 +582,15 @@ public void handleAsync(AnotherTestEvent event) {
578582
}
579583
}
580584

585+
581586
interface SimpleService extends Identifiable {
582587

583588
@EventListener
584589
void handleIt(TestEvent event);
585590

586591
}
587592

593+
588594
@Component
589595
@Scope(proxyMode = ScopedProxyMode.INTERFACES)
590596
static class ProxyTestBean extends AbstractIdentifiable implements SimpleService {
@@ -598,14 +604,6 @@ public void handleIt(TestEvent event) {
598604
}
599605
}
600606

601-
@Component
602-
@Scope(proxyMode = ScopedProxyMode.INTERFACES)
603-
static class InvalidProxyTestBean extends ProxyTestBean {
604-
605-
@EventListener // does not exist on any interface so it should fail
606-
public void handleIt2(TestEvent event) {
607-
}
608-
}
609607

610608
@Component
611609
@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
@@ -617,6 +615,7 @@ public void handleIt(TestEvent event) {
617615
}
618616
}
619617

618+
620619
@Component
621620
static class GenericEventListener extends AbstractTestEventListener {
622621

@@ -626,6 +625,7 @@ public void handleString(PayloadApplicationEvent<String> event) {
626625
}
627626
}
628627

628+
629629
@Component
630630
static class ConditionalEventListener extends TestEventListener {
631631

@@ -648,6 +648,7 @@ public void handleTimestamp(Long timestamp) {
648648

649649
}
650650

651+
651652
@Component
652653
static class OrderedTestListener extends TestEventListener {
653654

0 commit comments

Comments
 (0)