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

org.springframework.core.annotation.AnnotatedMethod Maven / Gradle / Ivy

There is a newer version: 6.1.11
Show newest version
/*
 * Copyright 2002-2024 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
 *
 *      https://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.annotation;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.springframework.core.BridgeMethodResolver;
import org.springframework.core.MethodParameter;
import org.springframework.core.ResolvableType;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;

/**
 * A convenient wrapper for a {@link Method} handle, providing deep annotation
 * introspection on methods and method parameters, including the exposure of
 * interface-declared parameter annotations from the concrete target method.
 *
 * @author Juergen Hoeller
 * @since 6.1
 * @see #getMethodAnnotation(Class)
 * @see #getMethodParameters()
 * @see AnnotatedElementUtils
 * @see SynthesizingMethodParameter
 */
public class AnnotatedMethod {

	private final Method method;

	private final Method bridgedMethod;

	private final MethodParameter[] parameters;

	@Nullable
	private volatile List inheritedParameterAnnotations;


	/**
	 * Create an instance that wraps the given {@link Method}.
	 * @param method the {@code Method} handle to wrap
	 */
	public AnnotatedMethod(Method method) {
		Assert.notNull(method, "Method is required");
		this.method = method;
		this.bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
		ReflectionUtils.makeAccessible(this.bridgedMethod);
		this.parameters = initMethodParameters();
	}

	/**
	 * Copy constructor for use in subclasses.
	 */
	protected AnnotatedMethod(AnnotatedMethod annotatedMethod) {
		Assert.notNull(annotatedMethod, "AnnotatedMethod is required");
		this.method = annotatedMethod.method;
		this.bridgedMethod = annotatedMethod.bridgedMethod;
		this.parameters = annotatedMethod.parameters;
		this.inheritedParameterAnnotations = annotatedMethod.inheritedParameterAnnotations;
	}


	/**
	 * Return the annotated method.
	 */
	public final Method getMethod() {
		return this.method;
	}

	/**
	 * If the annotated method is a bridge method, this method returns the bridged
	 * (user-defined) method. Otherwise, it returns the same method as {@link #getMethod()}.
	 */
	protected final Method getBridgedMethod() {
		return this.bridgedMethod;
	}

	/**
	 * Expose the containing class for method parameters.
	 * @see MethodParameter#getContainingClass()
	 */
	protected Class getContainingClass() {
		return this.method.getDeclaringClass();
	}

	/**
	 * Return the method parameters for this {@code AnnotatedMethod}.
	 */
	public final MethodParameter[] getMethodParameters() {
		return this.parameters;
	}

	private MethodParameter[] initMethodParameters() {
		int count = this.bridgedMethod.getParameterCount();
		MethodParameter[] result = new MethodParameter[count];
		for (int i = 0; i < count; i++) {
			result[i] = new AnnotatedMethodParameter(i);
		}
		return result;
	}

	/**
	 * Return a {@link MethodParameter} for the declared return type.
	 */
	public MethodParameter getReturnType() {
		return new AnnotatedMethodParameter(-1);
	}

	/**
	 * Return a {@link MethodParameter} for the actual return value type.
	 */
	public MethodParameter getReturnValueType(@Nullable Object returnValue) {
		return new ReturnValueMethodParameter(returnValue);
	}

	/**
	 * Return {@code true} if the method's return type is void, {@code false} otherwise.
	 */
	public boolean isVoid() {
		return (getReturnType().getParameterType() == void.class);
	}

	/**
	 * Return a single annotation on the underlying method, traversing its super methods
	 * if no annotation can be found on the given method itself.
	 * 

Supports merged composed annotations with attribute overrides. * @param annotationType the annotation type to look for * @return the annotation, or {@code null} if none found * @see AnnotatedElementUtils#findMergedAnnotation */ @Nullable public A getMethodAnnotation(Class annotationType) { return AnnotatedElementUtils.findMergedAnnotation(this.method, annotationType); } /** * Determine if an annotation of the given type is present or * meta-present on the method. * @param annotationType the annotation type to look for * @see AnnotatedElementUtils#hasAnnotation */ public boolean hasMethodAnnotation(Class annotationType) { return AnnotatedElementUtils.hasAnnotation(this.method, annotationType); } private List getInheritedParameterAnnotations() { List parameterAnnotations = this.inheritedParameterAnnotations; if (parameterAnnotations == null) { parameterAnnotations = new ArrayList<>(); Class clazz = this.method.getDeclaringClass(); while (clazz != null) { for (Class ifc : clazz.getInterfaces()) { for (Method candidate : ifc.getMethods()) { if (isOverrideFor(candidate)) { parameterAnnotations.add(candidate.getParameterAnnotations()); } } } clazz = clazz.getSuperclass(); if (clazz == Object.class) { clazz = null; } if (clazz != null) { for (Method candidate : clazz.getMethods()) { if (isOverrideFor(candidate)) { parameterAnnotations.add(candidate.getParameterAnnotations()); } } } } this.inheritedParameterAnnotations = parameterAnnotations; } return parameterAnnotations; } private boolean isOverrideFor(Method candidate) { if (!candidate.getName().equals(this.method.getName()) || candidate.getParameterCount() != this.method.getParameterCount()) { return false; } Class[] paramTypes = this.method.getParameterTypes(); if (Arrays.equals(candidate.getParameterTypes(), paramTypes)) { return true; } for (int i = 0; i < paramTypes.length; i++) { if (paramTypes[i] != ResolvableType.forMethodParameter(candidate, i, this.method.getDeclaringClass()).resolve()) { return false; } } return true; } @Override public boolean equals(@Nullable Object other) { return (this == other || (other != null && getClass() == other.getClass() && this.method.equals(((AnnotatedMethod) other).method))); } @Override public int hashCode() { return this.method.hashCode(); } @Override public String toString() { return this.method.toGenericString(); } // Support methods for use in subclass variants @Nullable protected static Object findProvidedArgument(MethodParameter parameter, @Nullable Object... providedArgs) { if (!ObjectUtils.isEmpty(providedArgs)) { for (Object providedArg : providedArgs) { if (parameter.getParameterType().isInstance(providedArg)) { return providedArg; } } } return null; } protected static String formatArgumentError(MethodParameter param, String message) { return "Could not resolve parameter [" + param.getParameterIndex() + "] in " + param.getExecutable().toGenericString() + (StringUtils.hasText(message) ? ": " + message : ""); } /** * A MethodParameter with AnnotatedMethod-specific behavior. */ protected class AnnotatedMethodParameter extends SynthesizingMethodParameter { @Nullable private volatile Annotation[] combinedAnnotations; public AnnotatedMethodParameter(int index) { super(AnnotatedMethod.this.getBridgedMethod(), index); } protected AnnotatedMethodParameter(AnnotatedMethodParameter original) { super(original); this.combinedAnnotations = original.combinedAnnotations; } @Override @NonNull public Method getMethod() { return AnnotatedMethod.this.getBridgedMethod(); } @Override public Class getContainingClass() { return AnnotatedMethod.this.getContainingClass(); } @Override @Nullable public T getMethodAnnotation(Class annotationType) { return AnnotatedMethod.this.getMethodAnnotation(annotationType); } @Override public boolean hasMethodAnnotation(Class annotationType) { return AnnotatedMethod.this.hasMethodAnnotation(annotationType); } @Override public Annotation[] getParameterAnnotations() { Annotation[] anns = this.combinedAnnotations; if (anns == null) { anns = super.getParameterAnnotations(); int index = getParameterIndex(); if (index >= 0) { for (Annotation[][] ifcAnns : getInheritedParameterAnnotations()) { if (index < ifcAnns.length) { Annotation[] paramAnns = ifcAnns[index]; if (paramAnns.length > 0) { List merged = new ArrayList<>(anns.length + paramAnns.length); merged.addAll(Arrays.asList(anns)); for (Annotation paramAnn : paramAnns) { boolean existingType = false; for (Annotation ann : anns) { if (ann.annotationType() == paramAnn.annotationType()) { existingType = true; break; } } if (!existingType) { merged.add(adaptAnnotation(paramAnn)); } } anns = merged.toArray(new Annotation[0]); } } } } this.combinedAnnotations = anns; } return anns; } @Override public AnnotatedMethodParameter clone() { return new AnnotatedMethodParameter(this); } } /** * A MethodParameter for an AnnotatedMethod return type based on an actual return value. */ private class ReturnValueMethodParameter extends AnnotatedMethodParameter { @Nullable private final Class returnValueType; public ReturnValueMethodParameter(@Nullable Object returnValue) { super(-1); this.returnValueType = (returnValue != null ? returnValue.getClass() : null); } protected ReturnValueMethodParameter(ReturnValueMethodParameter original) { super(original); this.returnValueType = original.returnValueType; } @Override public Class getParameterType() { return (this.returnValueType != null ? this.returnValueType : super.getParameterType()); } @Override public ReturnValueMethodParameter clone() { return new ReturnValueMethodParameter(this); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy