org.springframework.classify.util.MethodInvokerUtils Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of spring-retry Show documentation
Show all versions of spring-retry Show documentation
Spring Retry provides an abstraction around retrying failed operations, with an
emphasis on declarative control of the process and policy-based behaviour that is
easy to extend and customize. For instance, you can configure a plain POJO
operation to retry if it fails, based on the type of exception, and with a fixed
or exponential backoff.
/*
* Copyright 2006-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.
* 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.classify.util;
import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.concurrent.atomic.AtomicReference;
import org.springframework.aop.framework.Advised;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.ReflectionUtils;
/**
* Utility methods to create {@link MethodInvoker} instances.
*
* @author Lucas Ward
* @author Artem Bilan
* @since 1.1
*/
public class MethodInvokerUtils {
/**
* Create a {@link MethodInvoker} using the provided method name to search.
* @param object to be invoked
* @param methodName of the method to be invoked
* @param paramsRequired boolean indicating whether the parameters are required, if
* false, a no args version of the method will be searched for.
* @param paramTypes - parameter types of the method to search for.
* @return MethodInvoker if the method is found, null if it is not.
*/
public static MethodInvoker getMethodInvokerByName(Object object, String methodName, boolean paramsRequired,
Class>... paramTypes) {
Assert.notNull(object, "Object to invoke must not be null");
Method method = ClassUtils.getMethodIfAvailable(object.getClass(), methodName, paramTypes);
if (method == null) {
String errorMsg = "no method found with name [" + methodName + "] on class ["
+ object.getClass().getSimpleName() + "] compatable with the signature ["
+ getParamTypesString(paramTypes) + "].";
Assert.isTrue(!paramsRequired, errorMsg);
// if no method was found for the given parameters, and the
// parameters aren't required, then try with no params
method = ClassUtils.getMethodIfAvailable(object.getClass(), methodName, new Class[] {});
Assert.notNull(method, errorMsg);
}
return new SimpleMethodInvoker(object, method);
}
/**
* Create a String representation of the array of parameter types.
* @param paramTypes the types of parameters
* @return the paramTypes as String representation
*/
public static String getParamTypesString(Class>... paramTypes) {
StringBuilder paramTypesList = new StringBuilder("(");
for (int i = 0; i < paramTypes.length; i++) {
paramTypesList.append(paramTypes[i].getSimpleName());
if (i + 1 < paramTypes.length) {
paramTypesList.append(", ");
}
}
return paramTypesList.append(")").toString();
}
/**
* Create a {@link MethodInvoker} using the provided interface, and method name from
* that interface.
* @param cls the interface to search for the method named
* @param methodName of the method to be invoked
* @param object to be invoked
* @param paramTypes - parameter types of the method to search for.
* @return MethodInvoker if the method is found, null if it is not.
*/
public static MethodInvoker getMethodInvokerForInterface(Class> cls, String methodName, Object object,
Class>... paramTypes) {
if (cls.isAssignableFrom(object.getClass())) {
return MethodInvokerUtils.getMethodInvokerByName(object, methodName, true, paramTypes);
}
else {
return null;
}
}
/**
* Create a MethodInvoker from the delegate based on the annotationType. Ensure that
* the annotated method has a valid set of parameters.
* @param annotationType the annotation to scan for
* @param target the target object
* @param expectedParamTypes the expected parameter types for the method
* @return a MethodInvoker
*/
public static MethodInvoker getMethodInvokerByAnnotation(final Class extends Annotation> annotationType,
final Object target, final Class>... expectedParamTypes) {
MethodInvoker mi = MethodInvokerUtils.getMethodInvokerByAnnotation(annotationType, target);
final Class> targetClass = (target instanceof Advised) ? ((Advised) target).getTargetSource().getTargetClass()
: target.getClass();
if (mi != null) {
ReflectionUtils.doWithMethods(targetClass, method -> {
Annotation annotation = AnnotationUtils.findAnnotation(method, annotationType);
if (annotation != null) {
Class>[] paramTypes = method.getParameterTypes();
if (paramTypes.length > 0) {
String errorMsg = "The method [" + method.getName() + "] on target class ["
+ targetClass.getSimpleName() + "] is incompatable with the signature ["
+ getParamTypesString(expectedParamTypes) + "] expected for the annotation ["
+ annotationType.getSimpleName() + "].";
Assert.isTrue(paramTypes.length == expectedParamTypes.length, errorMsg);
for (int i = 0; i < paramTypes.length; i++) {
Assert.isTrue(expectedParamTypes[i].isAssignableFrom(paramTypes[i]), errorMsg);
}
}
}
});
}
return mi;
}
/**
* Create {@link MethodInvoker} for the method with the provided annotation on the
* provided object. Annotations that cannot be applied to methods (i.e. that aren't
* annotated with an element type of METHOD) will cause an exception to be thrown.
* @param annotationType to be searched for
* @param target to be invoked
* @return MethodInvoker for the provided annotation, null if none is found.
*/
public static MethodInvoker getMethodInvokerByAnnotation(final Class extends Annotation> annotationType,
final Object target) {
Assert.notNull(target, "Target must not be null");
Assert.notNull(annotationType, "AnnotationType must not be null");
Assert.isTrue(
ObjectUtils.containsElement(annotationType.getAnnotation(Target.class).value(), ElementType.METHOD),
"Annotation [" + annotationType + "] is not a Method-level annotation.");
final Class> targetClass = (target instanceof Advised) ? ((Advised) target).getTargetSource().getTargetClass()
: target.getClass();
if (targetClass == null) {
// Proxy with no target cannot have annotations
return null;
}
final AtomicReference annotatedMethod = new AtomicReference<>();
ReflectionUtils.doWithMethods(targetClass, method -> {
Annotation annotation = AnnotationUtils.findAnnotation(method, annotationType);
if (annotation != null) {
Assert.isNull(annotatedMethod.get(),
"found more than one method on target class [" + targetClass.getSimpleName()
+ "] with the annotation type [" + annotationType.getSimpleName() + "].");
annotatedMethod.set(method);
}
});
Method method = annotatedMethod.get();
if (method == null) {
return null;
}
else {
return new SimpleMethodInvoker(target, annotatedMethod.get());
}
}
/**
* Create a {@link MethodInvoker} for the delegate from a single public method.
* @param target an object to search for an appropriate method
* @return a MethodInvoker that calls a method on the delegate
* @param the t
* @param the C
*/
public static MethodInvoker getMethodInvokerForSingleArgument(Object target) {
final AtomicReference methodHolder = new AtomicReference<>();
ReflectionUtils.doWithMethods(target.getClass(), method -> {
if ((method.getModifiers() & Modifier.PUBLIC) == 0 || method.isBridge()) {
return;
}
if (method.getParameterTypes() == null || method.getParameterTypes().length != 1) {
return;
}
if (method.getReturnType().equals(Void.TYPE) || ReflectionUtils.isEqualsMethod(method)) {
return;
}
Assert.state(methodHolder.get() == null,
"More than one non-void public method detected with single argument.");
methodHolder.set(method);
});
Method method = methodHolder.get();
return new SimpleMethodInvoker(target, method);
}
}