1 /* 2 * Copyright 2002-2009 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.aop.framework; 18 19 import java.lang.reflect.AccessibleObject; 20 import java.lang.reflect.Method; 21 import java.util.HashMap; 22 import java.util.List; 23 import java.util.Map; 24 25 import org.aopalliance.intercept.MethodInterceptor; 26 import org.aopalliance.intercept.MethodInvocation; 27 28 import org.springframework.aop.ProxyMethodInvocation; 29 import org.springframework.aop.support.AopUtils; 30 import org.springframework.core.BridgeMethodResolver; 31 32 /** 33 * Spring's implementation of the AOP Alliance 34 * {@link org.aopalliance.intercept.MethodInvocation} interface, 35 * implementing the extended 36 * {@link org.springframework.aop.ProxyMethodInvocation} interface. 37 * 38 * <p>Invokes the target object using reflection. Subclasses can override the 39 * {@link #invokeJoinpoint()} method to change this behavior, so this is also 40 * a useful base class for more specialized MethodInvocation implementations. 41 * 42 * <p>It is possible to clone an invocation, to invoke {@link #proceed()} 43 * repeatedly (once per clone), using the {@link #invocableClone()} method. 44 * It is also possible to attach custom attributes to the invocation, 45 * using the {@link #setUserAttribute} / {@link #getUserAttribute} methods. 46 * 47 * <p><b>NOTE:</b> This class is considered internal and should not be 48 * directly accessed. The sole reason for it being public is compatibility 49 * with existing framework integrations (e.g. Pitchfork). For any other 50 * purposes, use the {@link ProxyMethodInvocation} interface instead. 51 * 52 * @author Rod Johnson 53 * @author Juergen Hoeller 54 * @author Adrian Colyer 55 * @see #invokeJoinpoint 56 * @see #proceed 57 * @see #invocableClone 58 * @see #setUserAttribute 59 * @see #getUserAttribute 60 */ 61 public class ReflectiveMethodInvocation implements ProxyMethodInvocation, Cloneable { 62 63 protected final Object proxy; 64 65 protected final Object target; 66 67 protected final Method method; 68 69 protected Object[] arguments; 70 71 private final Class targetClass; 72 73 /** 74 * Lazily initialized map of user-specific attributes for this invocation. 75 */ 76 private Map<String, Object> userAttributes; 77 78 /** 79 * List of MethodInterceptor and InterceptorAndDynamicMethodMatcher 80 * that need dynamic checks. 81 */ 82 protected final List interceptorsAndDynamicMethodMatchers; 83 84 /** 85 * Index from 0 of the current interceptor we're invoking. 86 * -1 until we invoke: then the current interceptor. 87 */ 88 private int currentInterceptorIndex = -1; 89 90 91 /** 92 * Construct a new ReflectiveMethodInvocation with the given arguments. 93 * @param proxy the proxy object that the invocation was made on 94 * @param target the target object to invoke 95 * @param method the method to invoke 96 * @param arguments the arguments to invoke the method with 97 * @param targetClass the target class, for MethodMatcher invocations 98 * @param interceptorsAndDynamicMethodMatchers interceptors that should be applied, 99 * along with any InterceptorAndDynamicMethodMatchers that need evaluation at runtime. 100 * MethodMatchers included in this struct must already have been found to have matched 101 * as far as was possibly statically. Passing an array might be about 10% faster, 102 * but would complicate the code. And it would work only for static pointcuts. 103 */ ReflectiveMethodInvocation( Object proxy, Object target, Method method, Object[] arguments, Class targetClass, List<Object> interceptorsAndDynamicMethodMatchers)104 protected ReflectiveMethodInvocation( 105 Object proxy, Object target, Method method, Object[] arguments, 106 Class targetClass, List<Object> interceptorsAndDynamicMethodMatchers) { 107 108 this.proxy = proxy; 109 this.target = target; 110 this.targetClass = targetClass; 111 this.method = BridgeMethodResolver.findBridgedMethod(method); 112 this.arguments = arguments; 113 this.interceptorsAndDynamicMethodMatchers = interceptorsAndDynamicMethodMatchers; 114 } 115 116 getProxy()117 public final Object getProxy() { 118 return this.proxy; 119 } 120 getThis()121 public final Object getThis() { 122 return this.target; 123 } 124 getStaticPart()125 public final AccessibleObject getStaticPart() { 126 return this.method; 127 } 128 129 /** 130 * Return the method invoked on the proxied interface. 131 * May or may not correspond with a method invoked on an underlying 132 * implementation of that interface. 133 */ getMethod()134 public final Method getMethod() { 135 return this.method; 136 } 137 getArguments()138 public final Object[] getArguments() { 139 return (this.arguments != null ? this.arguments : new Object[0]); 140 } 141 setArguments(Object[] arguments)142 public void setArguments(Object[] arguments) { 143 this.arguments = arguments; 144 } 145 146 proceed()147 public Object proceed() throws Throwable { 148 // We start with an index of -1 and increment early. 149 if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) { 150 return invokeJoinpoint(); 151 } 152 153 Object interceptorOrInterceptionAdvice = 154 this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex); 155 if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) { 156 // Evaluate dynamic method matcher here: static part will already have 157 // been evaluated and found to match. 158 InterceptorAndDynamicMethodMatcher dm = 159 (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice; 160 if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) { 161 return dm.interceptor.invoke(this); 162 } 163 else { 164 // Dynamic matching failed. 165 // Skip this interceptor and invoke the next in the chain. 166 return proceed(); 167 } 168 } 169 else { 170 // It's an interceptor, so we just invoke it: The pointcut will have 171 // been evaluated statically before this object was constructed. 172 return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this); 173 } 174 } 175 176 /** 177 * Invoke the joinpoint using reflection. 178 * Subclasses can override this to use custom invocation. 179 * @return the return value of the joinpoint 180 * @throws Throwable if invoking the joinpoint resulted in an exception 181 */ invokeJoinpoint()182 protected Object invokeJoinpoint() throws Throwable { 183 return AopUtils.invokeJoinpointUsingReflection(this.target, this.method, this.arguments); 184 } 185 186 187 /** 188 * This implementation returns a shallow copy of this invocation object, 189 * including an independent copy of the original arguments array. 190 * <p>We want a shallow copy in this case: We want to use the same interceptor 191 * chain and other object references, but we want an independent value for the 192 * current interceptor index. 193 * @see java.lang.Object#clone() 194 */ invocableClone()195 public MethodInvocation invocableClone() { 196 Object[] cloneArguments = null; 197 if (this.arguments != null) { 198 // Build an independent copy of the arguments array. 199 cloneArguments = new Object[this.arguments.length]; 200 System.arraycopy(this.arguments, 0, cloneArguments, 0, this.arguments.length); 201 } 202 return invocableClone(cloneArguments); 203 } 204 205 /** 206 * This implementation returns a shallow copy of this invocation object, 207 * using the given arguments array for the clone. 208 * <p>We want a shallow copy in this case: We want to use the same interceptor 209 * chain and other object references, but we want an independent value for the 210 * current interceptor index. 211 * @see java.lang.Object#clone() 212 */ invocableClone(Object[] arguments)213 public MethodInvocation invocableClone(Object[] arguments) { 214 // Force initialization of the user attributes Map, 215 // for having a shared Map reference in the clone. 216 if (this.userAttributes == null) { 217 this.userAttributes = new HashMap<String, Object>(); 218 } 219 220 // Create the MethodInvocation clone. 221 try { 222 ReflectiveMethodInvocation clone = (ReflectiveMethodInvocation) clone(); 223 clone.arguments = arguments; 224 return clone; 225 } 226 catch (CloneNotSupportedException ex) { 227 throw new IllegalStateException( 228 "Should be able to clone object of type [" + getClass() + "]: " + ex); 229 } 230 } 231 232 setUserAttribute(String key, Object value)233 public void setUserAttribute(String key, Object value) { 234 if (value != null) { 235 if (this.userAttributes == null) { 236 this.userAttributes = new HashMap<String, Object>(); 237 } 238 this.userAttributes.put(key, value); 239 } 240 else { 241 if (this.userAttributes != null) { 242 this.userAttributes.remove(key); 243 } 244 } 245 } 246 getUserAttribute(String key)247 public Object getUserAttribute(String key) { 248 return (this.userAttributes != null ? this.userAttributes.get(key) : null); 249 } 250 251 /** 252 * Return user attributes associated with this invocation. 253 * This method provides an invocation-bound alternative to a ThreadLocal. 254 * <p>This map is initialized lazily and is not used in the AOP framework itself. 255 * @return any user attributes associated with this invocation 256 * (never <code>null</code>) 257 */ getUserAttributes()258 public Map<String, Object> getUserAttributes() { 259 if (this.userAttributes == null) { 260 this.userAttributes = new HashMap<String, Object>(); 261 } 262 return this.userAttributes; 263 } 264 265 266 @Override toString()267 public String toString() { 268 // Don't do toString on target, it may be proxied. 269 StringBuilder sb = new StringBuilder("ReflectiveMethodInvocation: "); 270 sb.append(this.method).append("; "); 271 if (this.target == null) { 272 sb.append("target is null"); 273 } 274 else { 275 sb.append("target is of class [").append(this.target.getClass().getName()).append(']'); 276 } 277 return sb.toString(); 278 } 279 280 } 281