001/* 002 * Copyright 2002-2019 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.aop.framework; 018 019import java.lang.reflect.AccessibleObject; 020import java.lang.reflect.Method; 021import java.util.HashMap; 022import java.util.List; 023import java.util.Map; 024 025import org.aopalliance.intercept.MethodInterceptor; 026import org.aopalliance.intercept.MethodInvocation; 027 028import org.springframework.aop.ProxyMethodInvocation; 029import org.springframework.aop.support.AopUtils; 030import org.springframework.core.BridgeMethodResolver; 031 032/** 033 * Spring's implementation of the AOP Alliance 034 * {@link org.aopalliance.intercept.MethodInvocation} interface, 035 * implementing the extended 036 * {@link org.springframework.aop.ProxyMethodInvocation} interface. 037 * 038 * <p>Invokes the target object using reflection. Subclasses can override the 039 * {@link #invokeJoinpoint()} method to change this behavior, so this is also 040 * a useful base class for more specialized MethodInvocation implementations. 041 * 042 * <p>It is possible to clone an invocation, to invoke {@link #proceed()} 043 * repeatedly (once per clone), using the {@link #invocableClone()} method. 044 * It is also possible to attach custom attributes to the invocation, 045 * using the {@link #setUserAttribute} / {@link #getUserAttribute} methods. 046 * 047 * <p><b>NOTE:</b> This class is considered internal and should not be 048 * directly accessed. The sole reason for it being public is compatibility 049 * with existing framework integrations (e.g. Pitchfork). For any other 050 * purposes, use the {@link ProxyMethodInvocation} interface instead. 051 * 052 * @author Rod Johnson 053 * @author Juergen Hoeller 054 * @author Adrian Colyer 055 * @see #invokeJoinpoint 056 * @see #proceed 057 * @see #invocableClone 058 * @see #setUserAttribute 059 * @see #getUserAttribute 060 */ 061public class ReflectiveMethodInvocation implements ProxyMethodInvocation, Cloneable { 062 063 protected final Object proxy; 064 065 protected final Object target; 066 067 protected final Method method; 068 069 protected Object[] arguments; 070 071 private final Class<?> targetClass; 072 073 /** 074 * Lazily initialized map of user-specific attributes for this invocation. 075 */ 076 private Map<String, Object> userAttributes; 077 078 /** 079 * List of MethodInterceptor and InterceptorAndDynamicMethodMatcher 080 * that need dynamic checks. 081 */ 082 protected final List<?> interceptorsAndDynamicMethodMatchers; 083 084 /** 085 * Index from 0 of the current interceptor we're invoking. 086 * -1 until we invoke: then the current interceptor. 087 */ 088 private int currentInterceptorIndex = -1; 089 090 091 /** 092 * Construct a new ReflectiveMethodInvocation with the given arguments. 093 * @param proxy the proxy object that the invocation was made on 094 * @param target the target object to invoke 095 * @param method the method to invoke 096 * @param arguments the arguments to invoke the method with 097 * @param targetClass the target class, for MethodMatcher invocations 098 * @param interceptorsAndDynamicMethodMatchers interceptors that should be applied, 099 * 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 */ 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 = AopProxyUtils.adaptArgumentsIfNecessary(method, arguments); 113 this.interceptorsAndDynamicMethodMatchers = interceptorsAndDynamicMethodMatchers; 114 } 115 116 117 @Override 118 public final Object getProxy() { 119 return this.proxy; 120 } 121 122 @Override 123 public final Object getThis() { 124 return this.target; 125 } 126 127 @Override 128 public final AccessibleObject getStaticPart() { 129 return this.method; 130 } 131 132 /** 133 * Return the method invoked on the proxied interface. 134 * May or may not correspond with a method invoked on an underlying 135 * implementation of that interface. 136 */ 137 @Override 138 public final Method getMethod() { 139 return this.method; 140 } 141 142 @Override 143 public final Object[] getArguments() { 144 return (this.arguments != null ? this.arguments : new Object[0]); 145 } 146 147 @Override 148 public void setArguments(Object... arguments) { 149 this.arguments = arguments; 150 } 151 152 153 @Override 154 public Object proceed() throws Throwable { 155 // We start with an index of -1 and increment early. 156 if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) { 157 return invokeJoinpoint(); 158 } 159 160 Object interceptorOrInterceptionAdvice = 161 this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex); 162 if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) { 163 // Evaluate dynamic method matcher here: static part will already have 164 // been evaluated and found to match. 165 InterceptorAndDynamicMethodMatcher dm = 166 (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice; 167 if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) { 168 return dm.interceptor.invoke(this); 169 } 170 else { 171 // Dynamic matching failed. 172 // Skip this interceptor and invoke the next in the chain. 173 return proceed(); 174 } 175 } 176 else { 177 // It's an interceptor, so we just invoke it: The pointcut will have 178 // been evaluated statically before this object was constructed. 179 return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this); 180 } 181 } 182 183 /** 184 * Invoke the joinpoint using reflection. 185 * Subclasses can override this to use custom invocation. 186 * @return the return value of the joinpoint 187 * @throws Throwable if invoking the joinpoint resulted in an exception 188 */ 189 protected Object invokeJoinpoint() throws Throwable { 190 return AopUtils.invokeJoinpointUsingReflection(this.target, this.method, this.arguments); 191 } 192 193 194 /** 195 * This implementation returns a shallow copy of this invocation object, 196 * including an independent copy of the original arguments array. 197 * <p>We want a shallow copy in this case: We want to use the same interceptor 198 * chain and other object references, but we want an independent value for the 199 * current interceptor index. 200 * @see java.lang.Object#clone() 201 */ 202 @Override 203 public MethodInvocation invocableClone() { 204 Object[] cloneArguments = null; 205 if (this.arguments != null) { 206 // Build an independent copy of the arguments array. 207 cloneArguments = new Object[this.arguments.length]; 208 System.arraycopy(this.arguments, 0, cloneArguments, 0, this.arguments.length); 209 } 210 return invocableClone(cloneArguments); 211 } 212 213 /** 214 * This implementation returns a shallow copy of this invocation object, 215 * using the given arguments array for the clone. 216 * <p>We want a shallow copy in this case: We want to use the same interceptor 217 * chain and other object references, but we want an independent value for the 218 * current interceptor index. 219 * @see java.lang.Object#clone() 220 */ 221 @Override 222 public MethodInvocation invocableClone(Object... arguments) { 223 // Force initialization of the user attributes Map, 224 // for having a shared Map reference in the clone. 225 if (this.userAttributes == null) { 226 this.userAttributes = new HashMap<String, Object>(); 227 } 228 229 // Create the MethodInvocation clone. 230 try { 231 ReflectiveMethodInvocation clone = (ReflectiveMethodInvocation) clone(); 232 clone.arguments = arguments; 233 return clone; 234 } 235 catch (CloneNotSupportedException ex) { 236 throw new IllegalStateException( 237 "Should be able to clone object of type [" + getClass() + "]: " + ex); 238 } 239 } 240 241 242 @Override 243 public void setUserAttribute(String key, Object value) { 244 if (value != null) { 245 if (this.userAttributes == null) { 246 this.userAttributes = new HashMap<String, Object>(); 247 } 248 this.userAttributes.put(key, value); 249 } 250 else { 251 if (this.userAttributes != null) { 252 this.userAttributes.remove(key); 253 } 254 } 255 } 256 257 @Override 258 public Object getUserAttribute(String key) { 259 return (this.userAttributes != null ? this.userAttributes.get(key) : null); 260 } 261 262 /** 263 * Return user attributes associated with this invocation. 264 * This method provides an invocation-bound alternative to a ThreadLocal. 265 * <p>This map is initialized lazily and is not used in the AOP framework itself. 266 * @return any user attributes associated with this invocation 267 * (never {@code null}) 268 */ 269 public Map<String, Object> getUserAttributes() { 270 if (this.userAttributes == null) { 271 this.userAttributes = new HashMap<String, Object>(); 272 } 273 return this.userAttributes; 274 } 275 276 277 @Override 278 public String toString() { 279 // Don't do toString on target, it may be proxied. 280 StringBuilder sb = new StringBuilder("ReflectiveMethodInvocation: "); 281 sb.append(this.method).append("; "); 282 if (this.target == null) { 283 sb.append("target is null"); 284 } 285 else { 286 sb.append("target is of class [").append(this.target.getClass().getName()).append(']'); 287 } 288 return sb.toString(); 289 } 290 291}