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