Find interface method even for late-bound interface declaration in subclass
Closes gh-27995
This commit is contained in:
parent
a71a45e719
commit
bc9cd9a687
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
|
@ -1908,7 +1908,7 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
|
|||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Invoking init method '" + initMethodName + "' on bean with name '" + beanName + "'");
|
||||
}
|
||||
Method methodToInvoke = ClassUtils.getInterfaceMethodIfPossible(initMethod);
|
||||
Method methodToInvoke = ClassUtils.getInterfaceMethodIfPossible(initMethod, bean.getClass());
|
||||
|
||||
if (System.getSecurityManager() != null) {
|
||||
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
|
@ -138,7 +138,7 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
|
|||
beanName + "' has a non-boolean parameter - not supported as destroy method");
|
||||
}
|
||||
}
|
||||
destroyMethod = ClassUtils.getInterfaceMethodIfPossible(destroyMethod);
|
||||
destroyMethod = ClassUtils.getInterfaceMethodIfPossible(destroyMethod, bean.getClass());
|
||||
}
|
||||
this.destroyMethod = destroyMethod;
|
||||
}
|
||||
|
|
@ -252,9 +252,9 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
|
|||
invokeCustomDestroyMethod(this.destroyMethod);
|
||||
}
|
||||
else if (this.destroyMethodName != null) {
|
||||
Method methodToInvoke = determineDestroyMethod(this.destroyMethodName);
|
||||
if (methodToInvoke != null) {
|
||||
invokeCustomDestroyMethod(ClassUtils.getInterfaceMethodIfPossible(methodToInvoke));
|
||||
Method destroyMethod = determineDestroyMethod(this.destroyMethodName);
|
||||
if (destroyMethod != null) {
|
||||
invokeCustomDestroyMethod(ClassUtils.getInterfaceMethodIfPossible(destroyMethod, this.bean.getClass()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
|
@ -1256,7 +1256,7 @@ public abstract class ClassUtils {
|
|||
* (may be {@code null} or may not even implement the method)
|
||||
* @return the specific target method, or the original method if the
|
||||
* {@code targetClass} does not implement it
|
||||
* @see #getInterfaceMethodIfPossible
|
||||
* @see #getInterfaceMethodIfPossible(Method, Class)
|
||||
*/
|
||||
public static Method getMostSpecificMethod(Method method, @Nullable Class<?> targetClass) {
|
||||
if (targetClass != null && targetClass != method.getDeclaringClass() && isOverridable(method, targetClass)) {
|
||||
|
|
@ -1289,28 +1289,54 @@ public abstract class ClassUtils {
|
|||
* @param method the method to be invoked, potentially from an implementation class
|
||||
* @return the corresponding interface method, or the original method if none found
|
||||
* @since 5.1
|
||||
* @deprecated in favor of {@link #getInterfaceMethodIfPossible(Method, Class)}
|
||||
*/
|
||||
@Deprecated
|
||||
public static Method getInterfaceMethodIfPossible(Method method) {
|
||||
return getInterfaceMethodIfPossible(method, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine a corresponding interface method for the given method handle, if possible.
|
||||
* <p>This is particularly useful for arriving at a public exported type on Jigsaw
|
||||
* which can be reflectively invoked without an illegal access warning.
|
||||
* @param method the method to be invoked, potentially from an implementation class
|
||||
* @param targetClass the target class to check for declared interfaces
|
||||
* @return the corresponding interface method, or the original method if none found
|
||||
* @since 5.3.16
|
||||
* @see #getMostSpecificMethod
|
||||
*/
|
||||
public static Method getInterfaceMethodIfPossible(Method method) {
|
||||
public static Method getInterfaceMethodIfPossible(Method method, @Nullable Class<?> targetClass) {
|
||||
if (!Modifier.isPublic(method.getModifiers()) || method.getDeclaringClass().isInterface()) {
|
||||
return method;
|
||||
}
|
||||
return interfaceMethodCache.computeIfAbsent(method, key -> {
|
||||
Class<?> current = key.getDeclaringClass();
|
||||
while (current != null && current != Object.class) {
|
||||
Class<?>[] ifcs = current.getInterfaces();
|
||||
for (Class<?> ifc : ifcs) {
|
||||
try {
|
||||
return ifc.getMethod(key.getName(), key.getParameterTypes());
|
||||
}
|
||||
catch (NoSuchMethodException ex) {
|
||||
// ignore
|
||||
}
|
||||
// Try cached version of method in its declaring class
|
||||
Method result = interfaceMethodCache.computeIfAbsent(method,
|
||||
key -> findInterfaceMethodIfPossible(key, key.getDeclaringClass(), Object.class));
|
||||
if (result == method && targetClass != null) {
|
||||
// No interface method found yet -> try given target class (possibly a subclass of the
|
||||
// declaring class, late-binding a base class method to a subclass-declared interface:
|
||||
// see e.g. HashMap.HashIterator.hasNext)
|
||||
result = findInterfaceMethodIfPossible(method, targetClass, method.getDeclaringClass());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static Method findInterfaceMethodIfPossible(Method method, Class<?> startClass, Class<?> endClass) {
|
||||
Class<?> current = startClass;
|
||||
while (current != null && current != endClass) {
|
||||
Class<?>[] ifcs = current.getInterfaces();
|
||||
for (Class<?> ifc : ifcs) {
|
||||
try {
|
||||
return ifc.getMethod(method.getName(), method.getParameterTypes());
|
||||
}
|
||||
catch (NoSuchMethodException ex) {
|
||||
// ignore
|
||||
}
|
||||
current = current.getSuperclass();
|
||||
}
|
||||
return key;
|
||||
});
|
||||
current = current.getSuperclass();
|
||||
}
|
||||
return method;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
|
@ -58,8 +58,18 @@ public class ReflectiveMethodExecutor implements MethodExecutor {
|
|||
* @param method the method to invoke
|
||||
*/
|
||||
public ReflectiveMethodExecutor(Method method) {
|
||||
this(method, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new executor for the given method.
|
||||
* @param method the method to invoke
|
||||
* @param targetClass the target class to invoke the method on
|
||||
* @since 5.3.16
|
||||
*/
|
||||
public ReflectiveMethodExecutor(Method method, @Nullable Class<?> targetClass) {
|
||||
this.originalMethod = method;
|
||||
this.methodToInvoke = ClassUtils.getInterfaceMethodIfPossible(method);
|
||||
this.methodToInvoke = ClassUtils.getInterfaceMethodIfPossible(method, targetClass);
|
||||
if (method.isVarArgs()) {
|
||||
this.varargsPosition = method.getParameterCount() - 1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
|
@ -176,7 +176,7 @@ public class ReflectiveMethodResolver implements MethodResolver {
|
|||
}
|
||||
if (matchInfo != null) {
|
||||
if (matchInfo.isExactMatch()) {
|
||||
return new ReflectiveMethodExecutor(method);
|
||||
return new ReflectiveMethodExecutor(method, type);
|
||||
}
|
||||
else if (matchInfo.isCloseMatch()) {
|
||||
if (this.useDistance) {
|
||||
|
|
@ -204,13 +204,13 @@ public class ReflectiveMethodResolver implements MethodResolver {
|
|||
}
|
||||
}
|
||||
if (closeMatch != null) {
|
||||
return new ReflectiveMethodExecutor(closeMatch);
|
||||
return new ReflectiveMethodExecutor(closeMatch, type);
|
||||
}
|
||||
else if (matchRequiringConversion != null) {
|
||||
if (multipleOptions) {
|
||||
throw new SpelEvaluationException(SpelMessage.MULTIPLE_POSSIBLE_METHODS, name);
|
||||
}
|
||||
return new ReflectiveMethodExecutor(matchRequiringConversion);
|
||||
return new ReflectiveMethodExecutor(matchRequiringConversion, type);
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
|
@ -139,7 +139,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
|
|||
// The readerCache will only contain gettable properties (let's not worry about setters for now).
|
||||
Property property = new Property(type, method, null);
|
||||
TypeDescriptor typeDescriptor = new TypeDescriptor(property);
|
||||
method = ClassUtils.getInterfaceMethodIfPossible(method);
|
||||
method = ClassUtils.getInterfaceMethodIfPossible(method, type);
|
||||
this.readerCache.put(cacheKey, new InvokerPair(method, typeDescriptor));
|
||||
this.typeDescriptorCache.put(cacheKey, typeDescriptor);
|
||||
return true;
|
||||
|
|
@ -182,7 +182,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
|
|||
// The readerCache will only contain gettable properties (let's not worry about setters for now).
|
||||
Property property = new Property(type, method, null);
|
||||
TypeDescriptor typeDescriptor = new TypeDescriptor(property);
|
||||
method = ClassUtils.getInterfaceMethodIfPossible(method);
|
||||
method = ClassUtils.getInterfaceMethodIfPossible(method, type);
|
||||
invoker = new InvokerPair(method, typeDescriptor);
|
||||
this.lastReadInvokerPair = invoker;
|
||||
this.readerCache.put(cacheKey, invoker);
|
||||
|
|
@ -242,7 +242,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
|
|||
// Treat it like a property
|
||||
Property property = new Property(type, null, method);
|
||||
TypeDescriptor typeDescriptor = new TypeDescriptor(property);
|
||||
method = ClassUtils.getInterfaceMethodIfPossible(method);
|
||||
method = ClassUtils.getInterfaceMethodIfPossible(method, type);
|
||||
this.writerCache.put(cacheKey, method);
|
||||
this.typeDescriptorCache.put(cacheKey, typeDescriptor);
|
||||
return true;
|
||||
|
|
@ -291,7 +291,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
|
|||
if (method == null) {
|
||||
method = findSetterForProperty(name, type, target);
|
||||
if (method != null) {
|
||||
method = ClassUtils.getInterfaceMethodIfPossible(method);
|
||||
method = ClassUtils.getInterfaceMethodIfPossible(method, type);
|
||||
cachedMember = method;
|
||||
this.writerCache.put(cacheKey, cachedMember);
|
||||
}
|
||||
|
|
@ -533,21 +533,21 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
|
|||
if (target == null) {
|
||||
return this;
|
||||
}
|
||||
Class<?> clazz = (target instanceof Class ? (Class<?>) target : target.getClass());
|
||||
if (clazz.isArray()) {
|
||||
Class<?> type = (target instanceof Class ? (Class<?>) target : target.getClass());
|
||||
if (type.isArray()) {
|
||||
return this;
|
||||
}
|
||||
|
||||
PropertyCacheKey cacheKey = new PropertyCacheKey(clazz, name, target instanceof Class);
|
||||
PropertyCacheKey cacheKey = new PropertyCacheKey(type, name, target instanceof Class);
|
||||
InvokerPair invocationTarget = this.readerCache.get(cacheKey);
|
||||
|
||||
if (invocationTarget == null || invocationTarget.member instanceof Method) {
|
||||
Method method = (Method) (invocationTarget != null ? invocationTarget.member : null);
|
||||
if (method == null) {
|
||||
method = findGetterForProperty(name, clazz, target);
|
||||
method = findGetterForProperty(name, type, target);
|
||||
if (method != null) {
|
||||
TypeDescriptor typeDescriptor = new TypeDescriptor(new MethodParameter(method, -1));
|
||||
method = ClassUtils.getInterfaceMethodIfPossible(method);
|
||||
method = ClassUtils.getInterfaceMethodIfPossible(method, type);
|
||||
invocationTarget = new InvokerPair(method, typeDescriptor);
|
||||
ReflectionUtils.makeAccessible(method);
|
||||
this.readerCache.put(cacheKey, invocationTarget);
|
||||
|
|
@ -561,7 +561,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
|
|||
if (invocationTarget == null || invocationTarget.member instanceof Field) {
|
||||
Field field = (invocationTarget != null ? (Field) invocationTarget.member : null);
|
||||
if (field == null) {
|
||||
field = findField(name, clazz, target instanceof Class);
|
||||
field = findField(name, type, target instanceof Class);
|
||||
if (field != null) {
|
||||
invocationTarget = new InvokerPair(field, new TypeDescriptor(field));
|
||||
ReflectionUtils.makeAccessible(field);
|
||||
|
|
@ -600,7 +600,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
|
|||
|
||||
private final String property;
|
||||
|
||||
private boolean targetIsClass;
|
||||
private final boolean targetIsClass;
|
||||
|
||||
public PropertyCacheKey(Class<?> clazz, String name, boolean targetIsClass) {
|
||||
this.clazz = clazz;
|
||||
|
|
|
|||
Loading…
Reference in New Issue