From 5c0e9a2f40d6bede5129269cf08c0f585d39b4d5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 2 Aug 2026 13:00:51 +0000 Subject: [PATCH] Fix lambda EventListener type detection via SerializedLambda fallback --- microsphere-java-core/pom.xml | 8 ++++ .../io/microsphere/event/EventListener.java | 14 +++++- .../internal/reflect/ConstantPoolUtils.java | 5 ++- .../microsphere/lang/invoke/LambdaUtils.java | 43 ++++++++++++++++++- 4 files changed, 65 insertions(+), 5 deletions(-) diff --git a/microsphere-java-core/pom.xml b/microsphere-java-core/pom.xml index a46ea1d93..160a6f627 100644 --- a/microsphere-java-core/pom.xml +++ b/microsphere-java-core/pom.xml @@ -18,6 +18,14 @@ Microsphere :: Java :: Core Microsphere Java Core + + + + --add-opens java.base/java.lang=ALL-UNNAMED + --add-opens java.base/jdk.internal.reflect=ALL-UNNAMED + + + diff --git a/microsphere-java-core/src/main/java/io/microsphere/event/EventListener.java b/microsphere-java-core/src/main/java/io/microsphere/event/EventListener.java index 0c7136f3b..60b9f8a1c 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/event/EventListener.java +++ b/microsphere-java-core/src/main/java/io/microsphere/event/EventListener.java @@ -18,6 +18,7 @@ import io.microsphere.lang.Prioritized; +import java.io.Serializable; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.List; @@ -69,7 +70,7 @@ * @since 1.0.0 */ @FunctionalInterface -public interface EventListener extends java.util.EventListener, Prioritized { +public interface EventListener extends java.util.EventListener, Prioritized, Serializable { /** * Find the {@link Class type} of the {@link Event} from the specified {@link EventListener} instance. @@ -99,7 +100,16 @@ public interface EventListener extends java.util.EventListener, * @return the {@link Class} object representing the type of event this listener handles, or {@code null} if not found */ static Class findEventType(EventListener listener) { - return findEventType(listener.getClass()); + Class listenerClass = listener.getClass(); + if (!isAssignableFrom(EventListener.class, listenerClass)) { + return null; + } + // Pass the instance so that SerializedLambda can be used for lambda listeners + List> parameterTypes = resolveLambdaMethodParameterTypes(listener, EventListener.class); + if (parameterTypes.size() == 1) { + return (Class) parameterTypes.get(0); + } + return findEventType(listenerClass); } /** diff --git a/microsphere-java-core/src/main/java/io/microsphere/internal/reflect/ConstantPoolUtils.java b/microsphere-java-core/src/main/java/io/microsphere/internal/reflect/ConstantPoolUtils.java index 0e6d04ee2..94a875256 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/internal/reflect/ConstantPoolUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/internal/reflect/ConstantPoolUtils.java @@ -147,7 +147,8 @@ public abstract class ConstantPoolUtils implements Utils { static final Method getUTF8AtMethod = findMethod(CONSTANT_POOL_CLASS, "getUTF8At", int.class); public static int getSize(Class targetClass) { - return invoke(targetClass, getSizeMethod); + Integer size = invoke(targetClass, getSizeMethod); + return size != null ? size : 0; } @Nullable @@ -238,9 +239,9 @@ public static String getUTF8At(Class targetClass, int index) { @Nullable static T invoke(Class targetClass, Method method, Object... args) { - Object constantPool = getConstantPool(targetClass); T returnValue = null; try { + Object constantPool = getConstantPool(targetClass); returnValue = invokeMethod(true, constantPool, method, args); } catch (Throwable e) { logger.trace(e.getMessage()); diff --git a/microsphere-java-core/src/main/java/io/microsphere/lang/invoke/LambdaUtils.java b/microsphere-java-core/src/main/java/io/microsphere/lang/invoke/LambdaUtils.java index beec30557..86da350c8 100644 --- a/microsphere-java-core/src/main/java/io/microsphere/lang/invoke/LambdaUtils.java +++ b/microsphere-java-core/src/main/java/io/microsphere/lang/invoke/LambdaUtils.java @@ -21,9 +21,12 @@ import io.microsphere.util.ClassUtils; import io.microsphere.util.Utils; +import java.io.Serializable; import java.lang.invoke.CallSite; import java.lang.invoke.LambdaMetafactory; import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.lang.invoke.SerializedLambda; import java.lang.reflect.Member; import java.lang.reflect.Method; import java.util.List; @@ -85,7 +88,11 @@ public static List> resolveLambdaMethodParameterTypes(@Nullable Class> resolveLambdaMethodParameterTypes(@Nullable Object object, @Nullable Class functionalInterface) { - return resolveLambdaMethodParameterTypes(getType(object), functionalInterface); + List> parameterTypes = resolveLambdaMethodParameterTypes(getType(object), functionalInterface); + if (parameterTypes.isEmpty() && object instanceof Serializable && isLambdaClass(getType(object))) { + parameterTypes = resolveSerializableLambdaParameterTypes((Serializable) object, functionalInterface); + } + return parameterTypes; } /** @@ -167,6 +174,40 @@ static Class[] resolveParameterTypes(Method actualMethod, Class... declare return matchedParameterTypes; } + /** + * Resolve the parameter types of a serializable lambda using {@link SerializedLambda}. + *

+ * This method provides a reliable alternative to constant-pool inspection for lambda types + * that implement {@link Serializable}. It invokes the lambda's {@code writeReplace()} method + * to obtain a {@link SerializedLambda} descriptor containing the instantiated method type, + * from which the exact parameter types (including erased generic types) can be recovered. + *

+ * + * @param lambda the serializable lambda object + * @param functionalInterface the functional interface type + * @return the parameter types of the lambda method, or an empty list if they cannot be resolved + */ + private static List> resolveSerializableLambdaParameterTypes(Serializable lambda, Class functionalInterface) { + Method functionalInterfaceMethod = findFunctionalInterfaceMethod(functionalInterface); + if (functionalInterfaceMethod == null) { + return emptyList(); + } + try { + Method writeReplace = lambda.getClass().getDeclaredMethod("writeReplace"); + SerializedLambda sl = (SerializedLambda) writeReplace.invoke(lambda); + String instantiatedMethodType = sl.getInstantiatedMethodType(); + ClassLoader classLoader = lambda.getClass().getClassLoader(); + MethodType methodType = MethodType.fromMethodDescriptorString(instantiatedMethodType, classLoader); + List> parameterTypes = methodType.parameterList(); + if (parameterTypes.size() == functionalInterfaceMethod.getParameterCount()) { + return parameterTypes; + } + } catch (Exception e) { + // silently ignore if SerializedLambda cannot be obtained + } + return emptyList(); + } + private LambdaUtils() { } } \ No newline at end of file