Helper class that allows for specifying a method to invoke in a declarative
fashion, be it static or non-static.
Usage: Specify "targetClass"/"targetMethod" or "targetObject"/"targetMethod",
optionally specify arguments, prepare the invoker. Afterwards, you may
invoke the method any number of times, obtaining the invocation result.
| Method from org.springframework.util.MethodInvoker Detail: |
protected Method findMatchingMethod() {
String targetMethod = getTargetMethod();
Object[] arguments = getArguments();
int argCount = arguments.length;
Method[] candidates = ReflectionUtils.getAllDeclaredMethods(getTargetClass());
int minTypeDiffWeight = Integer.MAX_VALUE;
Method matchingMethod = null;
for (int i = 0; i < candidates.length; i++) {
Method candidate = candidates[i];
if (candidate.getName().equals(targetMethod)) {
Class[] paramTypes = candidate.getParameterTypes();
if (paramTypes.length == argCount) {
int typeDiffWeight = getTypeDifferenceWeight(paramTypes, arguments);
if (typeDiffWeight < minTypeDiffWeight) {
minTypeDiffWeight = typeDiffWeight;
matchingMethod = candidate;
}
}
}
}
return matchingMethod;
}
Find a matching method with the specified name for the specified arguments. |
public Object[] getArguments() {
return this.arguments;
}
Return the arguments for the method invocation. |
public Method getPreparedMethod() throws IllegalStateException {
if (this.methodObject == null) {
throw new IllegalStateException("prepare() must be called prior to invoke() on MethodInvoker");
}
return this.methodObject;
}
|
public Class getTargetClass() {
return this.targetClass;
}
Return the target class on which to call the target method. |
public String getTargetMethod() {
return this.targetMethod;
}
Return the name of the method to be invoked. |
public Object getTargetObject() {
return this.targetObject;
}
Return the target object on which to call the target method. |
public static int getTypeDifferenceWeight(Class[] paramTypes,
Object[] args) {
int result = 0;
for (int i = 0; i < paramTypes.length; i++) {
if (!ClassUtils.isAssignableValue(paramTypes[i], args[i])) {
return Integer.MAX_VALUE;
}
if (args[i] != null) {
Class paramType = paramTypes[i];
Class superClass = args[i].getClass().getSuperclass();
while (superClass != null) {
if (paramType.equals(superClass)) {
result = result + 2;
superClass = null;
}
else if (ClassUtils.isAssignable(paramType, superClass)) {
result = result + 2;
superClass = superClass.getSuperclass();
}
else {
superClass = null;
}
}
if (paramType.isInterface()) {
result = result + 1;
}
}
}
return result;
}
Algorithm that judges the match between the declared parameter types of a candidate method
and a specific list of arguments that this method is supposed to be invoked with.
Determines a weight that represents the class hierarchy difference between types and
arguments. A direct match, i.e. type Integer -> arg of class Integer, does not increase
the result - all direct matches means weight 0. A match between type Object and arg of
class Integer would increase the weight by 2, due to the superclass 2 steps up in the
hierarchy (i.e. Object) being the last one that still matches the required type Object.
Type Number and class Integer would increase the weight by 1 accordingly, due to the
superclass 1 step up the hierarchy (i.e. Number) still matching the required type Number.
Therefore, with an arg of type Integer, a constructor (Integer) would be preferred to a
constructor (Number) which would in turn be preferred to a constructor (Object).
All argument weights get accumulated. |
public Object invoke() throws InvocationTargetException, IllegalAccessException {
// In the static case, target will simply be < code >null< /code >.
Object targetObject = getTargetObject();
Method preparedMethod = getPreparedMethod();
if (targetObject == null && !Modifier.isStatic(preparedMethod.getModifiers())) {
throw new IllegalArgumentException("Target method must not be non-static without a target");
}
ReflectionUtils.makeAccessible(preparedMethod);
return preparedMethod.invoke(targetObject, getArguments());
}
|
public boolean isPrepared() {
return (this.methodObject != null);
}
Return whether this invoker has been prepared already,
i.e. whether it allows access to #getPreparedMethod() already. |
public void prepare() throws ClassNotFoundException, NoSuchMethodException {
if (this.staticMethod != null) {
int lastDotIndex = this.staticMethod.lastIndexOf('.");
if (lastDotIndex == -1 || lastDotIndex == this.staticMethod.length()) {
throw new IllegalArgumentException(
"staticMethod must be a fully qualified class plus method name: " +
"e.g. 'example.MyExampleClass.myExampleMethod'");
}
String className = this.staticMethod.substring(0, lastDotIndex);
String methodName = this.staticMethod.substring(lastDotIndex + 1);
this.targetClass = resolveClassName(className);
this.targetMethod = methodName;
}
Class targetClass = getTargetClass();
String targetMethod = getTargetMethod();
if (targetClass == null) {
throw new IllegalArgumentException("Either 'targetClass' or 'targetObject' is required");
}
if (targetMethod == null) {
throw new IllegalArgumentException("Property 'targetMethod' is required");
}
Object[] arguments = getArguments();
Class[] argTypes = new Class[arguments.length];
for (int i = 0; i < arguments.length; ++i) {
argTypes[i] = (arguments[i] != null ? arguments[i].getClass() : Object.class);
}
// Try to get the exact method first.
try {
this.methodObject = targetClass.getMethod(targetMethod, argTypes);
}
catch (NoSuchMethodException ex) {
// Just rethrow exception if we can't get any match.
this.methodObject = findMatchingMethod();
if (this.methodObject == null) {
throw ex;
}
}
}
Prepare the specified method.
The method can be invoked any number of times afterwards. |
protected Class resolveClassName(String className) throws ClassNotFoundException {
return ClassUtils.forName(className);
}
Resolve the given class name into a Class.
The default implementations uses ClassUtils.forName,
using the thread context class loader. |
public void setArguments(Object[] arguments) {
this.arguments = (arguments != null ? arguments : new Object[0]);
}
Set arguments for the method invocation. If this property is not set,
or the Object array is of length 0, a method with no arguments is assumed. |
public void setStaticMethod(String staticMethod) {
this.staticMethod = staticMethod;
}
Set a fully qualified static method name to invoke,
e.g. "example.MyExampleClass.myExampleMethod".
Convenient alternative to specifying targetClass and targetMethod. |
public void setTargetClass(Class targetClass) {
this.targetClass = targetClass;
}
Set the target class on which to call the target method.
Only necessary when the target method is static; else,
a target object needs to be specified anyway. |
public void setTargetMethod(String targetMethod) {
this.targetMethod = targetMethod;
}
Set the name of the method to be invoked.
Refers to either a static method or a non-static method,
depending on a target object being set. |
public void setTargetObject(Object targetObject) {
this.targetObject = targetObject;
if (targetObject != null) {
this.targetClass = targetObject.getClass();
}
}
Set the target object on which to call the target method.
Only necessary when the target method is not static;
else, a target class is sufficient. |