Skip to content

Commit 56b082d

Browse files
committed
Merge branch '6.2.x'
2 parents b59fec2 + a6f6ecf commit 56b082d

File tree

5 files changed

+58
-21
lines changed

5 files changed

+58
-21
lines changed

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

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -101,10 +101,9 @@
101101

102102
/**
103103
* The event classes that this listener handles.
104-
* <p>If this attribute is specified with a single value, the
105-
* annotated method may optionally accept a single parameter.
106-
* However, if this attribute is specified with multiple values,
107-
* the annotated method must <em>not</em> declare any parameters.
104+
* <p>The annotated method may optionally accept a single parameter
105+
* of the given event class, or of a common base class or interface
106+
* for all given event classes.
108107
*/
109108
@AliasFor("value")
110109
Class<?>[] classes() default {};

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

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package org.springframework.context.event;
1818

19+
import java.io.Serializable;
1920
import java.lang.annotation.ElementType;
2021
import java.lang.annotation.Retention;
2122
import java.lang.annotation.RetentionPolicy;
@@ -105,8 +106,9 @@ void simpleEventJavaConfig() {
105106
this.eventCollector.assertTotalEventsCount(1);
106107

107108
this.eventCollector.clear();
108-
this.context.publishEvent(event);
109-
this.eventCollector.assertEvent(listener, event);
109+
TestEvent otherEvent = new TestEvent(this, Integer.valueOf(1));
110+
this.context.publishEvent(otherEvent);
111+
this.eventCollector.assertEvent(listener, otherEvent);
110112
this.eventCollector.assertTotalEventsCount(1);
111113

112114
context.getBean(ApplicationEventMulticaster.class).removeApplicationListeners(l ->
@@ -723,6 +725,11 @@ public void handle(TestEvent event) {
723725
public void handleString(String content) {
724726
collectEvent(content);
725727
}
728+
729+
@EventListener({Boolean.class, Integer.class})
730+
public void handleBooleanOrInteger(Serializable content) {
731+
collectEvent(content);
732+
}
726733
}
727734

728735

@@ -990,6 +997,8 @@ interface ConditionalEventInterface extends Identifiable {
990997

991998
void handleString(String payload);
992999

1000+
void handleBooleanOrInteger(Serializable content);
1001+
9931002
void handleTimestamp(Long timestamp);
9941003

9951004
void handleRatio(Double ratio);
@@ -1012,6 +1021,12 @@ public void handleString(String payload) {
10121021
super.handleString(payload);
10131022
}
10141023

1024+
@EventListener({Boolean.class, Integer.class})
1025+
@Override
1026+
public void handleBooleanOrInteger(Serializable content) {
1027+
super.handleBooleanOrInteger(content);
1028+
}
1029+
10151030
@ConditionalEvent("#root.event.timestamp > #p0")
10161031
@Override
10171032
public void handleTimestamp(Long timestamp) {

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,12 @@
1818

1919
/**
2020
* @author Stephane Nicoll
21+
* @author Juergen Hoeller
2122
*/
2223
@SuppressWarnings("serial")
2324
public class TestEvent extends IdentifiableApplicationEvent {
2425

25-
public final String msg;
26+
public final Object msg;
2627

2728
public TestEvent(Object source, String id, String msg) {
2829
super(source, id);
@@ -34,6 +35,11 @@ public TestEvent(Object source, String msg) {
3435
this.msg = msg;
3536
}
3637

38+
public TestEvent(Object source, Integer msg) {
39+
super(source);
40+
this.msg = msg;
41+
}
42+
3743
public TestEvent(Object source) {
3844
this(source, "test");
3945
}

spring-core/src/main/java/org/springframework/util/ClassUtils.java

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1466,8 +1466,8 @@ private static Method getInterfaceMethodIfPossible(Method method, @Nullable Clas
14661466
}
14671467

14681468
/**
1469-
* Get the highest publicly accessible method in the supplied method's type hierarchy that
1470-
* has a method signature equivalent to the supplied method, if possible.
1469+
* Get the closest publicly accessible (and exported) method in the supplied method's type
1470+
* hierarchy that has a method signature equivalent to the supplied method, if possible.
14711471
* <p>Otherwise, this method recursively searches the class hierarchy and implemented
14721472
* interfaces for an equivalent method that is {@code public} and declared in a
14731473
* {@code public} type.
@@ -1490,18 +1490,21 @@ private static Method getInterfaceMethodIfPossible(Method method, @Nullable Clas
14901490
* @see #getMostSpecificMethod(Method, Class)
14911491
*/
14921492
public static Method getPubliclyAccessibleMethodIfPossible(Method method, @Nullable Class<?> targetClass) {
1493-
// If the method is not public, we can abort the search immediately.
1494-
if (!Modifier.isPublic(method.getModifiers())) {
1493+
Class<?> declaringClass = method.getDeclaringClass();
1494+
// If the method is not public or its declaring class is public and exported already,
1495+
// we can abort the search immediately (avoiding reflection as well as cache access).
1496+
if (!Modifier.isPublic(method.getModifiers()) || (Modifier.isPublic(declaringClass.getModifiers()) &&
1497+
declaringClass.getModule().isExported(declaringClass.getPackageName(), ClassUtils.class.getModule()))) {
14951498
return method;
14961499
}
14971500

14981501
Method interfaceMethod = getInterfaceMethodIfPossible(method, targetClass, true);
14991502
// If we found a method in a public interface, return the interface method.
1500-
if (interfaceMethod != method) {
1503+
if (interfaceMethod != method && interfaceMethod.getDeclaringClass().getModule().isExported(
1504+
interfaceMethod.getDeclaringClass().getPackageName(), ClassUtils.class.getModule())) {
15011505
return interfaceMethod;
15021506
}
15031507

1504-
Class<?> declaringClass = method.getDeclaringClass();
15051508
// Bypass cache for java.lang.Object unless it is actually an overridable method declared there.
15061509
if (declaringClass.getSuperclass() == Object.class && !ReflectionUtils.isObjectMethod(method)) {
15071510
return method;
@@ -1522,7 +1525,9 @@ public static Method getPubliclyAccessibleMethodIfPossible(Method method, @Nulla
15221525
if (method == null) {
15231526
break;
15241527
}
1525-
if (Modifier.isPublic(method.getDeclaringClass().getModifiers())) {
1528+
if (Modifier.isPublic(method.getDeclaringClass().getModifiers()) &&
1529+
method.getDeclaringClass().getModule().isExported(
1530+
method.getDeclaringClass().getPackageName(), ClassUtils.class.getModule())) {
15261531
result = method;
15271532
}
15281533
current = method.getDeclaringClass().getSuperclass();

spring-core/src/test/java/org/springframework/util/ClassUtilsTests.java

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import java.lang.reflect.Method;
2828
import java.lang.reflect.Modifier;
2929
import java.lang.reflect.Proxy;
30+
import java.net.URLConnection;
3031
import java.time.ZoneId;
3132
import java.util.ArrayList;
3233
import java.util.Arrays;
@@ -687,13 +688,13 @@ void publicMethodInNonPublicInterface() throws Exception {
687688
}
688689

689690
@Test
690-
void publicMethodInObjectClass() throws Exception {
691+
void publicMethodInPublicClass() throws Exception {
691692
Class<?> originalType = String.class;
692-
Method originalMethod = originalType.getDeclaredMethod("hashCode");
693+
Method originalMethod = originalType.getDeclaredMethod("toString");
693694

694695
Method publiclyAccessibleMethod = ClassUtils.getPubliclyAccessibleMethodIfPossible(originalMethod, null);
695-
assertThat(publiclyAccessibleMethod.getDeclaringClass()).isEqualTo(Object.class);
696-
assertThat(publiclyAccessibleMethod.getName()).isEqualTo("hashCode");
696+
assertThat(publiclyAccessibleMethod.getDeclaringClass()).isEqualTo(originalType);
697+
assertThat(publiclyAccessibleMethod).isSameAs(originalMethod);
697698
assertPubliclyAccessible(publiclyAccessibleMethod);
698699
}
699700

@@ -703,9 +704,20 @@ void publicInterfaceMethodInPublicClass() throws Exception {
703704
Method originalMethod = originalType.getDeclaredMethod("size");
704705

705706
Method publiclyAccessibleMethod = ClassUtils.getPubliclyAccessibleMethodIfPossible(originalMethod, null);
706-
// Should find the interface method in List.
707-
assertThat(publiclyAccessibleMethod.getDeclaringClass()).isEqualTo(List.class);
708-
assertThat(publiclyAccessibleMethod.getName()).isEqualTo("size");
707+
// Should not find the interface method in List.
708+
assertThat(publiclyAccessibleMethod.getDeclaringClass()).isEqualTo(originalType);
709+
assertThat(publiclyAccessibleMethod).isSameAs(originalMethod);
710+
assertPubliclyAccessible(publiclyAccessibleMethod);
711+
}
712+
713+
@Test
714+
void publicMethodInNonExportedClass() throws Exception {
715+
Class<?> originalType = getClass().getClassLoader().loadClass("sun.net.www.protocol.http.HttpURLConnection");
716+
Method originalMethod = originalType.getDeclaredMethod("getOutputStream");
717+
718+
Method publiclyAccessibleMethod = ClassUtils.getPubliclyAccessibleMethodIfPossible(originalMethod, null);
719+
assertThat(publiclyAccessibleMethod.getDeclaringClass()).isEqualTo(URLConnection.class);
720+
assertThat(publiclyAccessibleMethod.getName()).isSameAs(originalMethod.getName());
709721
assertPubliclyAccessible(publiclyAccessibleMethod);
710722
}
711723

0 commit comments

Comments
 (0)