Auto-adapt reflective arguments in case of vararg array type mismatch
Issue: SPR-13328
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2015 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.
|
||||
@@ -57,14 +57,14 @@ public interface ProxyMethodInvocation extends MethodInvocation {
|
||||
* @return an invocable clone of this invocation.
|
||||
* {@code proceed()} can be called once per clone.
|
||||
*/
|
||||
MethodInvocation invocableClone(Object[] arguments);
|
||||
MethodInvocation invocableClone(Object... arguments);
|
||||
|
||||
/**
|
||||
* Set the arguments to be used on subsequent invocations in the any advice
|
||||
* in this chain.
|
||||
* @param arguments the argument array
|
||||
*/
|
||||
void setArguments(Object[] arguments);
|
||||
void setArguments(Object... arguments);
|
||||
|
||||
/**
|
||||
* Add the specified user attribute with the given value to this invocation.
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.springframework.aop.framework;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.Arrays;
|
||||
|
||||
@@ -25,6 +27,7 @@ import org.springframework.aop.TargetSource;
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.aop.target.SingletonTargetSource;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* Utility methods for AOP proxy factories.
|
||||
@@ -161,4 +164,38 @@ public abstract class AopProxyUtils {
|
||||
return Arrays.equals(a.getAdvisors(), b.getAdvisors());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adapt the given arguments to the target signature in the given method,
|
||||
* if necessary: in particular, if a given vararg argument array does not
|
||||
* match the array type of the declared vararg parameter in the method.
|
||||
* @param method the target method
|
||||
* @param arguments the given arguments
|
||||
* @return a cloned argument array, or the original if no adaptation is needed
|
||||
* @since 4.2.3
|
||||
*/
|
||||
static Object[] adaptArgumentsIfNecessary(Method method, Object... arguments) {
|
||||
if (method.isVarArgs() && !ObjectUtils.isEmpty(arguments)) {
|
||||
Class<?>[] paramTypes = method.getParameterTypes();
|
||||
if (paramTypes.length == arguments.length) {
|
||||
int varargIndex = paramTypes.length - 1;
|
||||
Class<?> varargType = paramTypes[varargIndex];
|
||||
if (varargType.isArray()) {
|
||||
Object varargArray = arguments[varargIndex];
|
||||
if (varargArray instanceof Object[] && !varargType.isInstance(varargArray)) {
|
||||
Object[] newArguments = new Object[arguments.length];
|
||||
System.arraycopy(arguments, 0, newArguments, 0, varargIndex);
|
||||
Class<?> targetElementType = varargType.getComponentType();
|
||||
int varargLength = Array.getLength(varargArray);
|
||||
Object newVarargArray = Array.newInstance(targetElementType, varargLength);
|
||||
System.arraycopy(varargArray, 0, newVarargArray, 0, varargLength);
|
||||
newArguments[varargIndex] = newVarargArray;
|
||||
return newArguments;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return arguments;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -304,13 +304,13 @@ class CglibAopProxy implements AopProxy, Serializable {
|
||||
Callback targetDispatcher = isStatic ?
|
||||
new StaticDispatcher(this.advised.getTargetSource().getTarget()) : new SerializableNoOp();
|
||||
|
||||
Callback[] mainCallbacks = new Callback[]{
|
||||
aopInterceptor, // for normal advice
|
||||
targetInterceptor, // invoke target without considering advice, if optimized
|
||||
new SerializableNoOp(), // no override for methods mapped to this
|
||||
targetDispatcher, this.advisedDispatcher,
|
||||
new EqualsInterceptor(this.advised),
|
||||
new HashCodeInterceptor(this.advised)
|
||||
Callback[] mainCallbacks = new Callback[] {
|
||||
aopInterceptor, // for normal advice
|
||||
targetInterceptor, // invoke target without considering advice, if optimized
|
||||
new SerializableNoOp(), // no override for methods mapped to this
|
||||
targetDispatcher, this.advisedDispatcher,
|
||||
new EqualsInterceptor(this.advised),
|
||||
new HashCodeInterceptor(this.advised)
|
||||
};
|
||||
|
||||
Callback[] callbacks;
|
||||
@@ -646,7 +646,8 @@ class CglibAopProxy implements AopProxy, Serializable {
|
||||
// Note that the final invoker must be an InvokerInterceptor, so we know
|
||||
// it does nothing but a reflective operation on the target, and no hot
|
||||
// swapping or fancy proxying.
|
||||
retVal = methodProxy.invoke(target, args);
|
||||
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
|
||||
retVal = methodProxy.invoke(target, argsToUse);
|
||||
}
|
||||
else {
|
||||
// We need to create a method invocation...
|
||||
|
||||
@@ -198,7 +198,8 @@ final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializa
|
||||
// We can skip creating a MethodInvocation: just invoke the target directly
|
||||
// Note that the final invoker must be an InvokerInterceptor so we know it does
|
||||
// nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
|
||||
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args);
|
||||
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
|
||||
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
|
||||
}
|
||||
else {
|
||||
// We need to create a method invocation...
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2015 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.
|
||||
@@ -109,7 +109,7 @@ public class ReflectiveMethodInvocation implements ProxyMethodInvocation, Clonea
|
||||
this.target = target;
|
||||
this.targetClass = targetClass;
|
||||
this.method = BridgeMethodResolver.findBridgedMethod(method);
|
||||
this.arguments = arguments;
|
||||
this.arguments = AopProxyUtils.adaptArgumentsIfNecessary(method, arguments);
|
||||
this.interceptorsAndDynamicMethodMatchers = interceptorsAndDynamicMethodMatchers;
|
||||
}
|
||||
|
||||
@@ -145,7 +145,7 @@ public class ReflectiveMethodInvocation implements ProxyMethodInvocation, Clonea
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setArguments(Object[] arguments) {
|
||||
public void setArguments(Object... arguments) {
|
||||
this.arguments = arguments;
|
||||
}
|
||||
|
||||
@@ -219,7 +219,7 @@ public class ReflectiveMethodInvocation implements ProxyMethodInvocation, Clonea
|
||||
* @see java.lang.Object#clone()
|
||||
*/
|
||||
@Override
|
||||
public MethodInvocation invocableClone(Object[] arguments) {
|
||||
public MethodInvocation invocableClone(Object... arguments) {
|
||||
// Force initialization of the user attributes Map,
|
||||
// for having a shared Map reference in the clone.
|
||||
if (this.userAttributes == null) {
|
||||
|
||||
Reference in New Issue
Block a user