Stack Exchange
Spring Cacheable produces ArrayIndexOutOfBoundsException with varArgs
We just upgraded from spring 3.2.6 to 4.2.6.
And after migration we have an exception.
java.lang.ArrayIndexOutOfBoundsException: 1
at org.springframework.context.expression.MethodBasedEvaluationContext.lazyLoadArguments(MethodBasedEvaluationContext.java:93) ~[spring-context-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.context.expression.MethodBasedEvaluationContext.lookupVariable(MethodBasedEvaluationContext.java:67) ~[spring-context-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.cache.interceptor.CacheEvaluationContext.lookupVariable(CacheEvaluationContext.java:74) ~[spring-context-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.expression.spel.ExpressionState.lookupVariable(ExpressionState.java:144) ~[spring-expression-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.expression.spel.ast.VariableReference.getValueInternal(VariableReference.java:75) ~[spring-expression-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.expression.spel.ast.CompoundExpression.getValueRef(CompoundExpression.java:51) ~[spring-expression-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.expression.spel.ast.CompoundExpression.getValueInternal(CompoundExpression.java:87) ~[spring-expression-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.expression.spel.ast.SpelNodeImpl.getValue(SpelNodeImpl.java:120) ~[spring-expression-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.expression.spel.standard.SpelExpression.getValue(SpelExpression.java:242) ~[spring-expression-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.cache.interceptor.ExpressionEvaluator.key(ExpressionEvaluator.java:115) ~[spring-context-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.cache.interceptor.CacheAspectSupport$CacheOperationContext.generateKey(CacheAspectSupport.java:632) ~[spring-context-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.cache.interceptor.CacheAspectSupport.generateKey(CacheAspectSupport.java:487) ~[spring-context-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.cache.interceptor.CacheAspectSupport.findCachedItem(CacheAspectSupport.java:431) ~[spring-context-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:336) ~[spring-context-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:302) ~[spring-context-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.cache.interceptor.CacheInterceptor.invoke(CacheInterceptor.java:61) ~[spring-context-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99) ~[spring-tx-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:281) ~[spring-tx-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96) ~[spring-tx-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:208) ~[spring-aop-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at com.sun.proxy.$Proxy122.getUserBylId(Unknown Source) ~[na:na]

It happens when we call a service method with empty variable arguments. Which is defined like this.
@Transactional(readOnly = true)
@Override
@Cacheable(value = "users", key = "#id")
public final User getUserBylId(String id, EntityFilter<User>... filters) {}

The call of this method looks like this
userService.getUserById(ID)

So the thing is that I don't pass any arguments as filters.
When I pass anything it works fine.
For me it seems like a bug, but I can't find any thread or issue related in spring resources. 
I've tracked the issue to two places.
Here, spring extracts call arguments to an array. It has a special case for varArgs which looks correct. 
Running this code for my case produces an array with one ID element [id] which seems good.
protected class CacheOperationContext ...{
    .....
    private Object[] extractArgs(Method method, Object[] args) {
        if (!method.isVarArgs()) {
            return args;
        }
        Object[] varArgs = ObjectUtils.toObjectArray(args[args.length - 1]);
        Object[] combinedArgs = new Object[args.length - 1 + varArgs.length];
        System.arraycopy(args, 0, combinedArgs, 0, args.length - 1);
        System.arraycopy(varArgs, 0, combinedArgs, args.length - 1, varArgs.length);
        return combinedArgs;
    }
    ....
}

The second place is
public class MethodBasedEvaluationContext ...{
    ....
    protected void lazyLoadArguments() {
        // shortcut if no args need to be loaded
        if (ObjectUtils.isEmpty(this.args)) {
            return;
        }

       // save arguments as indexed variables
       for (int i = 0; i < this.args.length; i++) {
           setVariable("a" + i, this.args[i]);
           setVariable("p" + i, this.args[i]);
       }

       String[] parameterNames = this.paramDiscoverer.getParameterNames(this.method);
       // save parameter names (if discovered)
       if (parameterNames != null) {
           for (int i = 0; i < parameterNames.length; i++) {
                setVariable(parameterNames[i], this.args[i]);
           }
       }
    }
}

Here spring is iterating through parameter names and trying to find corresponding attribute in arguments array. And this array has only 1 element instead of 2.
It seems like a bug. I tried to fallback to spring 4.1.7 and it has the same issue. 
My main concern is that this code is so deep inside of spring that there is no 'clean' way to override this behavior.
The only way I can think of is to fork spring-context which I don't really like to do.
What do you think? Please advice =)

I was able to isolate the issue and submit PR. https://github.com/spring-projects/spring-framework/pull/1070

Alternatively, if you identify the root cause of the problem and include a reproducible test case, then you can fix it and submit a PR to the Spring Framework project.
At first glance, this problem seems easily reproducible.  However, I just wrote a highly simple, quick/contrived test class with a couple of test cases to test this particular issue, and it works as expected using the core Spring Framework 4.2.6.RELEASE.
Note, I minimized the number of variables that could possible affect the successful caching interaction, so my SUT is not @Transactional.
It is possible the @Transactional annotation may have had an effect, however, it is not likely and I have not heard of any cases where Transaction demarcation mixed with Caching causes any issues.
I might separate the 2 in a test case, which would be as simple as not "enabling" transaction management and see if that changes anything.  However, as far as I can tell, the @Cacheable var args (service) method works as expected.
Please report back with any additional findings or information on your end.
Thanks!

Thank you for advice. I will try to create a reproducible test case.
