Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions microsphere-java-core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,14 @@
<name>Microsphere :: Java :: Core</name>
<description>Microsphere Java Core</description>

<properties>
<!-- Allow reflective access to JDK internals needed by ConstantPoolUtils -->
<jvm.argLine>
--add-opens java.base/java.lang=ALL-UNNAMED
--add-opens java.base/jdk.internal.reflect=ALL-UNNAMED
</jvm.argLine>
</properties>

<dependencies>

<!-- Microsphere Java Annotations -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -69,7 +70,7 @@
* @since 1.0.0
*/
@FunctionalInterface
public interface EventListener<E extends Event> extends java.util.EventListener, Prioritized {
public interface EventListener<E extends Event> extends java.util.EventListener, Prioritized, Serializable {

Check failure on line 73 in microsphere-java-core/src/main/java/io/microsphere/event/EventListener.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Rename this interface.

See more on https://sonarcloud.io/project/issues?id=microsphere-projects_microsphere-java&issues=AZ_CkSM2AnduJNYFqsL2&open=AZ_CkSM2AnduJNYFqsL2&pullRequest=291

/**
* Find the {@link Class type} of the {@link Event} from the specified {@link EventListener} instance.
Expand Down Expand Up @@ -99,7 +100,16 @@
* @return the {@link Class} object representing the type of event this listener handles, or {@code null} if not found
*/
static Class<? extends Event> 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<Class<?>> parameterTypes = resolveLambdaMethodParameterTypes(listener, EventListener.class);
if (parameterTypes.size() == 1) {
return (Class<? extends Event>) parameterTypes.get(0);
}
return findEventType(listenerClass);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -238,9 +239,9 @@ public static String getUTF8At(Class<?> targetClass, int index) {

@Nullable
static <T> 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());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -85,7 +88,11 @@ public static List<Class<?>> resolveLambdaMethodParameterTypes(@Nullable Class<?
* @return the parameter types of the lambda method
*/
public static List<Class<?>> resolveLambdaMethodParameterTypes(@Nullable Object object, @Nullable Class<?> functionalInterface) {
return resolveLambdaMethodParameterTypes(getType(object), functionalInterface);
List<Class<?>> parameterTypes = resolveLambdaMethodParameterTypes(getType(object), functionalInterface);
if (parameterTypes.isEmpty() && object instanceof Serializable && isLambdaClass(getType(object))) {
parameterTypes = resolveSerializableLambdaParameterTypes((Serializable) object, functionalInterface);
}
return parameterTypes;
}

/**
Expand Down Expand Up @@ -167,6 +174,40 @@ static Class<?>[] resolveParameterTypes(Method actualMethod, Class<?>... declare
return matchedParameterTypes;
}

/**
* Resolve the parameter types of a serializable lambda using {@link SerializedLambda}.
* <p>
* 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.
* </p>
*
* @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<Class<?>> 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<Class<?>> parameterTypes = methodType.parameterList();
if (parameterTypes.size() == functionalInterfaceMethod.getParameterCount()) {
return parameterTypes;
}
} catch (Exception e) {
// silently ignore if SerializedLambda cannot be obtained
}
return emptyList();
}

private LambdaUtils() {
}
}