All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.springframework.core.BridgeMethodResolver Maven / Gradle / Ivy

Go to download

Spring core support for annotations, meta-annotations, and composed annotations with attribute overrides

The newest version!
/*
 * Copyright 2002-2018 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. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */

package org.springframework.core;

import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.springframework.lang.Nullable;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;

/**
 * Helper for resolving synthetic {@link Method#isBridge bridge Methods} to the {@link Method} being
 * bridged.
 *
 * 

* Given a synthetic {@link Method#isBridge bridge Method} returns the {@link Method} being bridged. * A bridge method may be created by the compiler when extending a parameterized type whose methods * have parameterized arguments. During runtime invocation the bridge {@link Method} may be invoked * and/or used via reflection. When attempting to locate annotations on {@link Method Methods}, it * is wise to check for bridge {@link Method Methods} as appropriate and find the bridged * {@link Method}. * *

* See * The Java Language Specification for more details on the use of bridge methods. * * @author Rob Harrop * @author Juergen Hoeller * @author Phillip Webb * @since 2.0 */ public abstract class BridgeMethodResolver { /** * Find the original method for the supplied {@link Method bridge Method}. *

* It is safe to call this method passing in a non-bridge {@link Method} instance. In such a case, * the supplied {@link Method} instance is returned directly to the caller. Callers are * not required to check for bridging before calling this method. * * @param bridgeMethod the method to introspect * @return the original method (either the bridged method or the passed-in method if no more * specific one could be found) */ public static Method findBridgedMethod(Method bridgeMethod) { if (!bridgeMethod.isBridge()) { return bridgeMethod; } // Gather all methods with matching name and parameter size. List candidateMethods = new ArrayList<>(); Method[] methods = ReflectionUtils.getAllDeclaredMethods(bridgeMethod.getDeclaringClass()); for (Method candidateMethod : methods) { if (isBridgedCandidateFor(candidateMethod, bridgeMethod)) { candidateMethods.add(candidateMethod); } } // Now perform simple quick check. if (candidateMethods.size() == 1) { return candidateMethods.get(0); } // Search for candidate match. Method bridgedMethod = searchCandidates(candidateMethods, bridgeMethod); if (bridgedMethod != null) { // Bridged method found... return bridgedMethod; } else { // A bridge method was passed in but we couldn't find the bridged method. // Let's proceed with the passed-in method and hope for the best... return bridgeMethod; } } /** * Returns {@code true} if the supplied '{@code candidateMethod}' can be consider a validate * candidate for the {@link Method} that is {@link Method#isBridge() bridged} by the supplied * {@link Method bridge Method}. This method performs inexpensive checks and can be used quickly * filter for a set of possible matches. */ private static boolean isBridgedCandidateFor(Method candidateMethod, Method bridgeMethod) { return (!candidateMethod.isBridge() && !candidateMethod.equals(bridgeMethod) && candidateMethod.getName().equals(bridgeMethod.getName()) && candidateMethod.getParameterCount() == bridgeMethod.getParameterCount()); } /** * Searches for the bridged method in the given candidates. * * @param candidateMethods the List of candidate Methods * @param bridgeMethod the bridge method * @return the bridged method, or {@code null} if none found */ @Nullable private static Method searchCandidates(List candidateMethods, Method bridgeMethod) { if (candidateMethods.isEmpty()) { return null; } Method previousMethod = null; boolean sameSig = true; for (Method candidateMethod : candidateMethods) { if (isBridgeMethodFor(bridgeMethod, candidateMethod, bridgeMethod.getDeclaringClass())) { return candidateMethod; } else if (previousMethod != null) { sameSig = sameSig && Arrays.equals(candidateMethod.getGenericParameterTypes(), previousMethod.getGenericParameterTypes()); } previousMethod = candidateMethod; } return (sameSig ? candidateMethods.get(0) : null); } /** * Determines whether or not the bridge {@link Method} is the bridge for the supplied candidate * {@link Method}. */ static boolean isBridgeMethodFor(Method bridgeMethod, Method candidateMethod, Class declaringClass) { if (isResolvedTypeMatch(candidateMethod, bridgeMethod, declaringClass)) { return true; } Method method = findGenericDeclaration(bridgeMethod); return (method != null && isResolvedTypeMatch(method, candidateMethod, declaringClass)); } /** * Returns {@code true} if the {@link Type} signature of both the supplied * {@link Method#getGenericParameterTypes() generic Method} and concrete {@link Method} are equal * after resolving all types against the declaringType, otherwise returns {@code false}. */ private static boolean isResolvedTypeMatch(Method genericMethod, Method candidateMethod, Class declaringClass) { Type[] genericParameters = genericMethod.getGenericParameterTypes(); Class[] candidateParameters = candidateMethod.getParameterTypes(); if (genericParameters.length != candidateParameters.length) { return false; } for (int i = 0; i < candidateParameters.length; i++) { ResolvableType genericParameter = ResolvableType.forMethodParameter(genericMethod, i, declaringClass); Class candidateParameter = candidateParameters[i]; if (candidateParameter.isArray()) { // An array type: compare the component type. if (!candidateParameter.getComponentType().equals(genericParameter.getComponentType().resolve(Object.class))) { return false; } } // A non-array type: compare the type itself. if (!candidateParameter.equals(genericParameter.resolve(Object.class))) { return false; } } return true; } /** * Searches for the generic {@link Method} declaration whose erased signature matches that of the * supplied bridge method. * * @throws IllegalStateException if the generic declaration cannot be found */ @Nullable private static Method findGenericDeclaration(Method bridgeMethod) { // Search parent types for method that has same signature as bridge. Class superclass = bridgeMethod.getDeclaringClass().getSuperclass(); while (superclass != null && Object.class != superclass) { Method method = searchForMatch(superclass, bridgeMethod); if (method != null && !method.isBridge()) { return method; } superclass = superclass.getSuperclass(); } Class[] interfaces = ClassUtils.getAllInterfacesForClass(bridgeMethod.getDeclaringClass()); return searchInterfaces(interfaces, bridgeMethod); } @Nullable private static Method searchInterfaces(Class[] interfaces, Method bridgeMethod) { for (Class ifc : interfaces) { Method method = searchForMatch(ifc, bridgeMethod); if (method != null && !method.isBridge()) { return method; } else { method = searchInterfaces(ifc.getInterfaces(), bridgeMethod); if (method != null) { return method; } } } return null; } /** * If the supplied {@link Class} has a declared {@link Method} whose signature matches that of the * supplied {@link Method}, then this matching {@link Method} is returned, otherwise {@code null} * is returned. */ @Nullable private static Method searchForMatch(Class type, Method bridgeMethod) { try { return type.getDeclaredMethod(bridgeMethod.getName(), bridgeMethod.getParameterTypes()); } catch (NoSuchMethodException ex) { return null; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy