Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commite228f4b

Browse files
committed
Consistent pre-resolution of event type vs payload type
Restores proper event type propagation to parent context.Selectively applies payload type to given payload object.Also reuses cached type for regular ApplicationEvent now.Closesgh-30360
1 parentc733ae0 commite228f4b

File tree

4 files changed

+227
-31
lines changed

4 files changed

+227
-31
lines changed

‎spring-context/src/main/java/org/springframework/context/PayloadApplicationEvent.java‎

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2021 the original author or authors.
2+
* Copyright 2002-2023 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -41,27 +41,30 @@ public class PayloadApplicationEvent<T> extends ApplicationEvent implements Reso
4141

4242
privatefinalResolvableTypepayloadType;
4343

44+
4445
/**
45-
* Create a new PayloadApplicationEvent.
46+
* Create a new PayloadApplicationEvent, using the instance to infer its type.
4647
* @param source the object on which the event initially occurred (never {@code null})
4748
* @param payload the payload object (never {@code null})
48-
* @param payloadType the type object of payload object (can be {@code null})
49-
* @since 6.0
5049
*/
51-
publicPayloadApplicationEvent(Objectsource,Tpayload,@NullableResolvableTypepayloadType) {
52-
super(source);
53-
Assert.notNull(payload,"Payload must not be null");
54-
this.payload =payload;
55-
this.payloadType = (payloadType !=null) ?payloadType :ResolvableType.forInstance(payload);
50+
publicPayloadApplicationEvent(Objectsource,Tpayload) {
51+
this(source,payload,null);
5652
}
5753

5854
/**
59-
* Create a new PayloadApplicationEvent, usingtheinstance to infer its type.
55+
* Create a new PayloadApplicationEvent based ontheprovided payload type.
6056
* @param source the object on which the event initially occurred (never {@code null})
6157
* @param payload the payload object (never {@code null})
58+
* @param payloadType the type object of payload object (can be {@code null}).
59+
* Note that this is meant to indicate the payload type (e.g. {@code String}),
60+
* not the full event type (such as {@code PayloadApplicationEvent<&lt;String&gt;}).
61+
* @since 6.0
6262
*/
63-
publicPayloadApplicationEvent(Objectsource,Tpayload) {
64-
this(source,payload,null);
63+
publicPayloadApplicationEvent(Objectsource,Tpayload,@NullableResolvableTypepayloadType) {
64+
super(source);
65+
Assert.notNull(payload,"Payload must not be null");
66+
this.payload =payload;
67+
this.payloadType = (payloadType !=null ?payloadType :ResolvableType.forInstance(payload));
6568
}
6669

6770

‎spring-context/src/main/java/org/springframework/context/event/SimpleApplicationEventMulticaster.java‎

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -128,12 +128,12 @@ protected ErrorHandler getErrorHandler() {
128128

129129
@Override
130130
publicvoidmulticastEvent(ApplicationEventevent) {
131-
multicastEvent(event,resolveDefaultEventType(event));
131+
multicastEvent(event,null);
132132
}
133133

134134
@Override
135135
publicvoidmulticastEvent(ApplicationEventevent,@NullableResolvableTypeeventType) {
136-
ResolvableTypetype = (eventType !=null ?eventType :resolveDefaultEventType(event));
136+
ResolvableTypetype = (eventType !=null ?eventType :ResolvableType.forInstance(event));
137137
Executorexecutor =getTaskExecutor();
138138
for (ApplicationListener<?>listener :getApplicationListeners(event,type)) {
139139
if (executor !=null) {
@@ -145,10 +145,6 @@ public void multicastEvent(ApplicationEvent event, @Nullable ResolvableType even
145145
}
146146
}
147147

148-
privateResolvableTyperesolveDefaultEventType(ApplicationEventevent) {
149-
returnResolvableType.forInstance(event);
150-
}
151-
152148
/**
153149
* Invoke the given listener with the given event.
154150
* @param listener the ApplicationListener to invoke

‎spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java‎

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -385,23 +385,47 @@ public void publishEvent(Object event) {
385385

386386
/**
387387
* Publish the given event to all listeners.
388+
* <p>This is the internal delegate that all other {@code publishEvent}
389+
* methods refer to. It is not meant to be called directly but rather serves
390+
* as a propagation mechanism between application contexts in a hierarchy,
391+
* potentially overridden in subclasses for a custom propagation arrangement.
388392
* @param event the event to publish (may be an {@link ApplicationEvent}
389393
* or a payload object to be turned into a {@link PayloadApplicationEvent})
390-
* @param eventType the resolved event type, if known
394+
* @param typeHint the resolved event type, if known.
395+
* The implementation of this method also tolerates a payload type hint for
396+
* a payload object to be turned into a {@link PayloadApplicationEvent}.
397+
* However, the recommended way is to construct an actual event object via
398+
* {@link PayloadApplicationEvent#PayloadApplicationEvent(Object, Object, ResolvableType)}
399+
* instead for such scenarios.
391400
* @since 4.2
401+
* @see ApplicationEventMulticaster#multicastEvent(ApplicationEvent, ResolvableType)
392402
*/
393-
protectedvoidpublishEvent(Objectevent,@NullableResolvableTypeeventType) {
403+
protectedvoidpublishEvent(Objectevent,@NullableResolvableTypetypeHint) {
394404
Assert.notNull(event,"Event must not be null");
405+
ResolvableTypeeventType =null;
395406

396407
// Decorate event as an ApplicationEvent if necessary
397408
ApplicationEventapplicationEvent;
398409
if (eventinstanceofApplicationEventapplEvent) {
399410
applicationEvent =applEvent;
411+
eventType =typeHint;
400412
}
401413
else {
402-
applicationEvent =newPayloadApplicationEvent<>(this,event,eventType);
403-
if (eventType ==null) {
404-
eventType = ((PayloadApplicationEvent<?>)applicationEvent).getResolvableType();
414+
ResolvableTypepayloadType =null;
415+
if (typeHint !=null &&ApplicationEvent.class.isAssignableFrom(typeHint.toClass())) {
416+
eventType =typeHint;
417+
}
418+
else {
419+
payloadType =typeHint;
420+
}
421+
applicationEvent =newPayloadApplicationEvent<>(this,event,payloadType);
422+
}
423+
424+
// Determine event type only once (for multicast and parent publish)
425+
if (eventType ==null) {
426+
eventType =ResolvableType.forInstance(applicationEvent);
427+
if (typeHint ==null) {
428+
typeHint =eventType;
405429
}
406430
}
407431

@@ -416,7 +440,7 @@ protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
416440
// Publish event via parent context as well...
417441
if (this.parent !=null) {
418442
if (this.parentinstanceofAbstractApplicationContextabstractApplicationContext) {
419-
abstractApplicationContext.publishEvent(event,eventType);
443+
abstractApplicationContext.publishEvent(event,typeHint);
420444
}
421445
else {
422446
this.parent.publishEvent(event);

‎spring-context/src/test/java/org/springframework/context/event/PayloadApplicationEventTests.java‎

Lines changed: 180 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2022 the original author or authors.
2+
* Copyright 2002-2023 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -21,6 +21,7 @@
2121

2222
importorg.junit.jupiter.api.Test;
2323

24+
importorg.springframework.beans.BeansException;
2425
importorg.springframework.context.ApplicationListener;
2526
importorg.springframework.context.ConfigurableApplicationContext;
2627
importorg.springframework.context.PayloadApplicationEvent;
@@ -68,16 +69,97 @@ void payloadApplicationEventWithType() {
6869
});
6970
}
7071

72+
@Test
73+
@SuppressWarnings("resource")
74+
voidtestEventClassWithPayloadType() {
75+
ConfigurableApplicationContextac =newAnnotationConfigApplicationContext(NumberHolderListener.class);
76+
77+
PayloadApplicationEvent<NumberHolder<Integer>>event =newPayloadApplicationEvent<>(this,
78+
newNumberHolder<>(42),ResolvableType.forClassWithGenerics(NumberHolder.class,Integer.class));
79+
ac.publishEvent(event);
80+
assertThat(ac.getBean(NumberHolderListener.class).events.contains(event.getPayload())).isTrue();
81+
ac.close();
82+
}
83+
84+
@Test
85+
@SuppressWarnings("resource")
86+
voidtestEventClassWithPayloadTypeOnParentContext() {
87+
ConfigurableApplicationContextparent =newAnnotationConfigApplicationContext(NumberHolderListener.class);
88+
ConfigurableApplicationContextac =newGenericApplicationContext(parent);
89+
ac.refresh();
90+
91+
PayloadApplicationEvent<NumberHolder<Integer>>event =newPayloadApplicationEvent<>(this,
92+
newNumberHolder<>(42),ResolvableType.forClassWithGenerics(NumberHolder.class,Integer.class));
93+
ac.publishEvent(event);
94+
assertThat(parent.getBean(NumberHolderListener.class).events.contains(event.getPayload())).isTrue();
95+
ac.close();
96+
parent.close();
97+
}
98+
99+
@Test
100+
@SuppressWarnings("resource")
101+
voidtestPayloadObjectWithPayloadType() {
102+
finalObjectpayload =newNumberHolder<>(42);
103+
104+
AnnotationConfigApplicationContextac =newAnnotationConfigApplicationContext(NumberHolderListener.class) {
105+
@Override
106+
protectedvoidfinishRefresh()throwsBeansException {
107+
super.finishRefresh();
108+
// This is not recommended: use publishEvent(new PayloadApplicationEvent(...)) instead
109+
publishEvent(payload,ResolvableType.forClassWithGenerics(NumberHolder.class,Integer.class));
110+
}
111+
};
112+
113+
assertThat(ac.getBean(NumberHolderListener.class).events.contains(payload)).isTrue();
114+
ac.close();
115+
}
116+
117+
@Test
118+
@SuppressWarnings("resource")
119+
voidtestPayloadObjectWithPayloadTypeOnParentContext() {
120+
finalObjectpayload =newNumberHolder<>(42);
121+
122+
ConfigurableApplicationContextparent =newAnnotationConfigApplicationContext(NumberHolderListener.class);
123+
ConfigurableApplicationContextac =newGenericApplicationContext(parent) {
124+
@Override
125+
protectedvoidfinishRefresh()throwsBeansException {
126+
super.finishRefresh();
127+
// This is not recommended: use publishEvent(new PayloadApplicationEvent(...)) instead
128+
publishEvent(payload,ResolvableType.forClassWithGenerics(NumberHolder.class,Integer.class));
129+
}
130+
};
131+
ac.refresh();
132+
133+
assertThat(parent.getBean(NumberHolderListener.class).events.contains(payload)).isTrue();
134+
ac.close();
135+
parent.close();
136+
}
137+
71138
@Test
72139
@SuppressWarnings("resource")
73140
voidtestEventClassWithInterface() {
74141
ConfigurableApplicationContextac =newAnnotationConfigApplicationContext(AuditableListener.class);
142+
75143
AuditablePayloadEvent<String>event =newAuditablePayloadEvent<>(this,"xyz");
76144
ac.publishEvent(event);
77145
assertThat(ac.getBean(AuditableListener.class).events.contains(event)).isTrue();
78146
ac.close();
79147
}
80148

149+
@Test
150+
@SuppressWarnings("resource")
151+
voidtestEventClassWithInterfaceOnParentContext() {
152+
ConfigurableApplicationContextparent =newAnnotationConfigApplicationContext(AuditableListener.class);
153+
ConfigurableApplicationContextac =newGenericApplicationContext(parent);
154+
ac.refresh();
155+
156+
AuditablePayloadEvent<String>event =newAuditablePayloadEvent<>(this,"xyz");
157+
ac.publishEvent(event);
158+
assertThat(parent.getBean(AuditableListener.class).events.contains(event)).isTrue();
159+
ac.close();
160+
parent.close();
161+
}
162+
81163
@Test
82164
@SuppressWarnings("resource")
83165
voidtestProgrammaticEventListener() {
@@ -96,6 +178,27 @@ void testProgrammaticEventListener() {
96178
ac.close();
97179
}
98180

181+
@Test
182+
@SuppressWarnings("resource")
183+
voidtestProgrammaticEventListenerOnParentContext() {
184+
List<Auditable>events =newArrayList<>();
185+
ApplicationListener<AuditablePayloadEvent<String>>listener =events::add;
186+
ApplicationListener<AuditablePayloadEvent<Integer>>mismatch = (event ->event.getPayload());
187+
188+
ConfigurableApplicationContextparent =newGenericApplicationContext();
189+
parent.addApplicationListener(listener);
190+
parent.addApplicationListener(mismatch);
191+
parent.refresh();
192+
ConfigurableApplicationContextac =newGenericApplicationContext(parent);
193+
ac.refresh();
194+
195+
AuditablePayloadEvent<String>event =newAuditablePayloadEvent<>(this,"xyz");
196+
ac.publishEvent(event);
197+
assertThat(events.contains(event)).isTrue();
198+
ac.close();
199+
parent.close();
200+
}
201+
99202
@Test
100203
@SuppressWarnings("resource")
101204
voidtestProgrammaticPayloadListener() {
@@ -108,10 +211,75 @@ void testProgrammaticPayloadListener() {
108211
ac.addApplicationListener(mismatch);
109212
ac.refresh();
110213

111-
AuditablePayloadEvent<String>event =newAuditablePayloadEvent<>(this,"xyz");
112-
ac.publishEvent(event);
113-
assertThat(events.contains(event.getPayload())).isTrue();
214+
Stringpayload ="xyz";
215+
ac.publishEvent(payload);
216+
assertThat(events.contains(payload)).isTrue();
217+
ac.close();
218+
}
219+
220+
@Test
221+
@SuppressWarnings("resource")
222+
voidtestProgrammaticPayloadListenerOnParentContext() {
223+
List<String>events =newArrayList<>();
224+
ApplicationListener<PayloadApplicationEvent<String>>listener =ApplicationListener.forPayload(events::add);
225+
ApplicationListener<PayloadApplicationEvent<Integer>>mismatch =ApplicationListener.forPayload(Integer::intValue);
226+
227+
ConfigurableApplicationContextparent =newGenericApplicationContext();
228+
parent.addApplicationListener(listener);
229+
parent.addApplicationListener(mismatch);
230+
parent.refresh();
231+
ConfigurableApplicationContextac =newGenericApplicationContext(parent);
232+
ac.refresh();
233+
234+
Stringpayload ="xyz";
235+
ac.publishEvent(payload);
236+
assertThat(events.contains(payload)).isTrue();
237+
ac.close();
238+
parent.close();
239+
}
240+
241+
@Test
242+
@SuppressWarnings("resource")
243+
voidtestPlainPayloadListener() {
244+
ConfigurableApplicationContextac =newAnnotationConfigApplicationContext(PlainPayloadListener.class);
245+
246+
Stringpayload ="xyz";
247+
ac.publishEvent(payload);
248+
assertThat(ac.getBean(PlainPayloadListener.class).events.contains(payload)).isTrue();
249+
ac.close();
250+
}
251+
252+
@Test
253+
@SuppressWarnings("resource")
254+
voidtestPlainPayloadListenerOnParentContext() {
255+
ConfigurableApplicationContextparent =newAnnotationConfigApplicationContext(PlainPayloadListener.class);
256+
ConfigurableApplicationContextac =newGenericApplicationContext(parent);
257+
ac.refresh();
258+
259+
Stringpayload ="xyz";
260+
ac.publishEvent(payload);
261+
assertThat(parent.getBean(PlainPayloadListener.class).events.contains(payload)).isTrue();
114262
ac.close();
263+
parent.close();
264+
}
265+
266+
267+
staticclassNumberHolder<TextendsNumber> {
268+
269+
publicNumberHolder(Tnumber) {
270+
}
271+
}
272+
273+
274+
@Component
275+
publicstaticclassNumberHolderListener {
276+
277+
publicfinalList<NumberHolder<Integer>>events =newArrayList<>();
278+
279+
@EventListener
280+
publicvoidonEvent(NumberHolder<Integer>event) {
281+
events.add(event);
282+
}
115283
}
116284

117285

@@ -139,11 +307,16 @@ public void onEvent(Auditable event) {
139307
}
140308
}
141309

142-
staticclassNumberHolder<TextendsNumber> {
143310

144-
publicNumberHolder(Tnumber) {
145-
}
311+
@Component
312+
publicstaticclassPlainPayloadListener {
313+
314+
publicfinalList<String>events =newArrayList<>();
146315

316+
@EventListener
317+
publicvoidonEvent(Stringevent) {
318+
events.add(event);
319+
}
147320
}
148321

149322
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp