1 /* 2 * Copyright 2002-2011 the original author or authors. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package org.springframework.cache.interceptor; 18 19 import java.lang.reflect.Method; 20 import java.util.Map; 21 22 import org.springframework.aop.support.AopUtils; 23 import org.springframework.core.ParameterNameDiscoverer; 24 import org.springframework.expression.spel.support.StandardEvaluationContext; 25 import org.springframework.util.ObjectUtils; 26 27 /** 28 * Evaluation context class that adds a method parameters as SpEL 29 * variables, in a lazy manner. The lazy nature eliminates unneeded 30 * parsing of classes byte code for parameter discovery. 31 * 32 * <p>To limit the creation of objects, an ugly constructor is used 33 * (rather then a dedicated 'closure'-like class for deferred execution). 34 * 35 * @author Costin Leau 36 * @since 3.1 37 */ 38 class LazyParamAwareEvaluationContext extends StandardEvaluationContext { 39 40 private final ParameterNameDiscoverer paramDiscoverer; 41 42 private final Method method; 43 44 private final Object[] args; 45 46 private final Class<?> targetClass; 47 48 private final Map<String, Method> methodCache; 49 50 private boolean paramLoaded = false; 51 52 LazyParamAwareEvaluationContext(Object rootObject, ParameterNameDiscoverer paramDiscoverer, Method method, Object[] args, Class<?> targetClass, Map<String, Method> methodCache)53 LazyParamAwareEvaluationContext(Object rootObject, ParameterNameDiscoverer paramDiscoverer, Method method, 54 Object[] args, Class<?> targetClass, Map<String, Method> methodCache) { 55 super(rootObject); 56 57 this.paramDiscoverer = paramDiscoverer; 58 this.method = method; 59 this.args = args; 60 this.targetClass = targetClass; 61 this.methodCache = methodCache; 62 } 63 64 65 /** 66 * Load the param information only when needed. 67 */ 68 @Override lookupVariable(String name)69 public Object lookupVariable(String name) { 70 Object variable = super.lookupVariable(name); 71 if (variable != null) { 72 return variable; 73 } 74 if (!this.paramLoaded) { 75 loadArgsAsVariables(); 76 this.paramLoaded = true; 77 variable = super.lookupVariable(name); 78 } 79 return variable; 80 } 81 loadArgsAsVariables()82 private void loadArgsAsVariables() { 83 // shortcut if no args need to be loaded 84 if (ObjectUtils.isEmpty(this.args)) { 85 return; 86 } 87 88 String mKey = toString(this.method); 89 Method targetMethod = this.methodCache.get(mKey); 90 if (targetMethod == null) { 91 targetMethod = AopUtils.getMostSpecificMethod(this.method, this.targetClass); 92 if (targetMethod == null) { 93 targetMethod = this.method; 94 } 95 this.methodCache.put(mKey, targetMethod); 96 } 97 98 // save arguments as indexed variables 99 for (int i = 0; i < this.args.length; i++) { 100 setVariable("a" + i, this.args[i]); 101 setVariable("p" + i, this.args[i]); 102 } 103 104 String[] parameterNames = this.paramDiscoverer.getParameterNames(targetMethod); 105 // save parameter names (if discovered) 106 if (parameterNames != null) { 107 for (int i = 0; i < parameterNames.length; i++) { 108 setVariable(parameterNames[i], this.args[i]); 109 } 110 } 111 } 112 toString(Method m)113 private String toString(Method m) { 114 StringBuilder sb = new StringBuilder(); 115 sb.append(m.getDeclaringClass().getName()); 116 sb.append("#"); 117 sb.append(m.toString()); 118 return sb.toString(); 119 } 120 }