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 }